var DomainInfo = null; var viewHelpers = { getFormGroup: function(keypath, setting, values, isAdvanced) { form_group = "
"; setting_value = _(values).valueForKeyPath(keypath); if (_.isUndefined(setting_value) || _.isNull(setting_value)) { if (_.has(setting, 'default')) { setting_value = setting.default; } else { setting_value = ""; } } label_class = 'control-label'; function common_attrs(extra_classes) { extra_classes = (!_.isUndefined(extra_classes) ? extra_classes : ""); return " class='" + (setting.type !== 'checkbox' ? 'form-control' : '') + " " + Settings.TRIGGER_CHANGE_CLASS + " " + extra_classes + "' data-short-name='" + setting.name + "' name='" + keypath + "' " + "id='" + (!_.isUndefined(setting.html_id) ? setting.html_id : keypath) + "'"; } if (setting.type === 'checkbox') { if (setting.label) { form_group += "" } form_group += "
" form_group += "" if (setting.help) { form_group += "" + setting.help + ""; } form_group += "
" } else { input_type = _.has(setting, 'type') ? setting.type : "text" if (setting.label) { form_group += ""; } if (input_type === 'table') { form_group += makeTable(setting, keypath, setting_value) } else { if (input_type === 'select') { form_group += "" form_group += "" } else if (input_type === 'button') { // Is this a button that should link to something directly? // If so, we use an anchor tag instead of a button tag if (setting.href) { form_group += "" + setting.button_label + ""; } else { form_group += ""; } } else { if (input_type == 'integer') { input_type = "text" } form_group += "" } form_group += "" + setting.help + "" } } form_group += "
" return form_group } } function showSpinnerAlert(title) { swal({ title: title, text: '
', html: true, showConfirmButton: false, allowEscapeKey: false }); } function reloadSettings(callback) { $.getJSON(Settings.endpoint, function(data){ _.extend(data, viewHelpers); for (var spliceIndex in Settings.extraGroupsAtIndex) { data.descriptions.splice(spliceIndex, 0, Settings.extraGroupsAtIndex[spliceIndex]); } for (var endGroupIndex in Settings.extraGroupsAtEnd) { data.descriptions.push(Settings.extraGroupsAtEnd[endGroupIndex]); } $('#panels').html(Settings.panelsTemplate(data)); Settings.data = data; Settings.initialValues = form2js('settings-form', ".", false, cleanupFormValues, true); Settings.afterReloadActions(); // setup any bootstrap switches $('.toggle-checkbox').bootstrapSwitch(); $('[data-toggle="tooltip"]').tooltip(); Settings.pendingChanges = 0; // call the callback now that settings are loaded callback(true); }).fail(function() { // call the failure object since settings load faild callback(false) }); } function postSettings(jsonSettings) { // POST the form JSON to the domain-server settings.json endpoint so the settings are saved $.ajax(Settings.endpoint, { data: JSON.stringify(jsonSettings), contentType: 'application/json', type: 'POST' }).done(function(data){ if (data.status == "success") { if ($(".save-button-text").html() === SAVE_BUTTON_LABEL_RESTART) { showRestartModal(); } else { location.reload(true); } } else { showErrorMessage("Error", SETTINGS_ERROR_MESSAGE) reloadSettings(); } }).fail(function(){ showErrorMessage("Error", SETTINGS_ERROR_MESSAGE) reloadSettings(); }); } $(document).ready(function(){ $(document).on('click', '.save-button', function(e){ saveSettings(); e.preventDefault(); }); $.ajaxSetup({ timeout: 20000, }); reloadSettings(function(success){ if (success) { // if there was a hash in the URL, jump to it now if (location.hash) { location.href = location.hash; } } else { swal({ title: '', type: 'error', text: Strings.LOADING_SETTINGS_ERROR }); } }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.ADD_ROW_BUTTON_CLASS, function(){ addTableRow($(this).closest('tr')); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.DEL_ROW_BUTTON_CLASS, function(){ deleteTableRow($(this).closest('tr')); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.ADD_CATEGORY_BUTTON_CLASS, function(){ addTableCategory($(this).closest('tr')); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.DEL_CATEGORY_BUTTON_CLASS, function(){ deleteTableCategory($(this).closest('tr')); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.TOGGLE_CATEGORY_COLUMN_CLASS, function(){ toggleTableCategory($(this).closest('tr')); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){ moveTableRow($(this).closest('tr'), true); }); $('#' + Settings.FORM_ID).on('click', '.' + Settings.MOVE_DOWN_BUTTON_CLASS, function(){ moveTableRow($(this).closest('tr'), false); }); $('#' + Settings.FORM_ID).on('keyup', function(e){ var $target = $(e.target); if (e.keyCode == 13) { if ($target.is('table input')) { // capture enter in table input // if we have a sibling next to us that has an input, jump to it, otherwise check if we have a glyphicon for add to click sibling = $target.parent('td').next(); if (sibling.hasClass(Settings.DATA_COL_CLASS)) { // set focus to next input sibling.find('input').focus(); } else { // jump over the re-order row, if that's what we're on if (sibling.hasClass(Settings.REORDER_BUTTONS_CLASS)) { sibling = sibling.next(); } // for tables with categories we add the entry and setup the new row on enter if (sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).length) { sibling.find("." + Settings.ADD_CATEGORY_BUTTON_CLASS).click(); // set focus to the first input in the new row $target.closest('table').find('tr.inputs input:first').focus(); } var tableRows = sibling.parent(); var tableBody = tableRows.parent(); // if theres no more siblings, we should jump to a new row if (sibling.next().length == 0 && tableRows.nextAll().length == 1) { tableBody.find("." + Settings.ADD_ROW_BUTTON_CLASS).click(); } } } else if ($target.is('input')) { $target.change().blur(); } e.preventDefault(); } }); $('#' + Settings.FORM_ID).on('keypress', function(e){ if (e.keyCode == 13) { e.preventDefault(); } }); $('#' + Settings.FORM_ID).on('change input propertychange', '.' + Settings.TRIGGER_CHANGE_CLASS , function(e){ // this input was changed, add the changed data attribute to it $(this).attr('data-changed', true); badgeForDifferences($(this)); }); $('#' + Settings.FORM_ID).on('switchChange.bootstrapSwitch', 'input.toggle-checkbox', function(){ // this checkbox was changed, add the changed data attribute to it $(this).attr('data-changed', true); badgeForDifferences($(this)); }); // Bootstrap switch in table $('#' + Settings.FORM_ID).on('change', 'input.table-checkbox', function () { // Bootstrap switches in table: set the changed data attribute for all rows in table. var row = $(this).closest('tr'); if (row.hasClass("value-row")) { // Don't set attribute on input row switches prior to it being added to table. row.find('td.' + Settings.DATA_COL_CLASS + ' input').attr('data-changed', true); updateDataChangedForSiblingRows(row, true); badgeForDifferences($(this)); } }); $('#' + Settings.FORM_ID).on('change', 'input.table-time', function() { // Bootstrap switches in table: set the changed data attribute for all rows in table. var row = $(this).closest('tr'); if (row.hasClass("value-row")) { // Don't set attribute on input row switches prior to it being added to table. row.find('td.' + Settings.DATA_COL_CLASS + ' input').attr('data-changed', true); updateDataChangedForSiblingRows(row, true); badgeForDifferences($(this)); } }); $('#' + Settings.FORM_ID).on('change', 'select', function(){ $("input[name='" + $(this).attr('data-hidden-input') + "']").val($(this).val()).change(); }); var panelsSource = $('#panels-template').html(); Settings.panelsTemplate = _.template(panelsSource); }); function dynamicButton(button_id, text) { return $(""); } function showSpinnerAlert(title) { swal({ title: title, text: '
', html: true, showConfirmButton: false, allowEscapeKey: false }); } function parseJSONResponse(xhr) { try { return JSON.parse(xhr.responseText); } catch (e) { } return null; } function validateInputs() { // check if any new values are bad var tables = $('table'); var inputsValid = true; var tables = $('table'); // clear any current invalid rows $('tr.' + Settings.INVALID_ROW_CLASS).removeClass(Settings.INVALID_ROW_CLASS); function markParentRowInvalid(rowChild) { $(rowChild).closest('tr').addClass(Settings.INVALID_ROW_CLASS); } _.each(tables, function(table) { // validate keys specificially for spaces and equality to an existing key var newKeys = $(table).find('tr.' + Settings.NEW_ROW_CLASS + ' td.key'); var keyWithSpaces = false; var empty = false; var duplicateKey = false; _.each(newKeys, function(keyCell) { var keyVal = $(keyCell).children('input').val(); if (keyVal.indexOf(' ') !== -1) { keyWithSpaces = true; markParentRowInvalid(keyCell); return; } // make sure the key isn't empty if (keyVal.length === 0) { empty = true markParentRowInvalid(input); return; } // make sure we don't have duplicate keys in the table var otherKeys = $(table).find('td.key').not(keyCell); _.each(otherKeys, function(otherKeyCell) { var keyInput = $(otherKeyCell).children('input'); if (keyInput.length) { if ($(keyInput).val() == keyVal) { duplicateKey = true; } } else if ($(otherKeyCell).html() == keyVal) { duplicateKey = true; } if (duplicateKey) { markParentRowInvalid(keyCell); return; } }); }); if (keyWithSpaces) { showErrorMessage("Error", "Key contains spaces"); inputsValid = false; return } if (empty) { showErrorMessage("Error", "Empty field(s)"); inputsValid = false; return } if (duplicateKey) { showErrorMessage("Error", "Two keys cannot be identical"); inputsValid = false; return; } }); return inputsValid; } var SETTINGS_ERROR_MESSAGE = "There was a problem saving domain settings. Please try again!"; function saveSettings() { if (validateInputs()) { // POST the form JSON to the domain-server settings.json endpoint so the settings are saved var canPost = true; // disable any inputs not changed $("input:not([data-changed])").each(function () { $(this).prop('disabled', true); }); // grab a JSON representation of the form via form2js var formJSON = form2js('settings-form', ".", false, cleanupFormValues, true); // re-enable all inputs $("input").each(function () { $(this).prop('disabled', false); }); // remove focus from the button $(this).blur(); if (Settings.handlePostSettings === undefined) { console.log("----- saveSettings() called ------"); console.log(formJSON); // POST the form JSON to the domain-server settings.json endpoint so the settings are saved postSettings(formJSON); } else { Settings.handlePostSettings(formJSON) } } } function makeTable(setting, keypath, setting_value) { var isArray = !_.has(setting, 'key'); var categoryKey = setting.categorize_by_key; var isCategorized = !!categoryKey && isArray; if (!isArray && setting.can_order) { setting.can_order = false; } var html = ""; if (setting.help) { html += "" + setting.help + "" } var nonDeletableRowKey = setting["non-deletable-row-key"]; var nonDeletableRowValues = setting["non-deletable-row-values"]; html += ""; if (setting.caption) { html += "" } // Column groups if (setting.groups) { html += "" _.each(setting.groups, function (group) { html += "" }) if (!setting.read_only) { if (setting.can_order) { html += ""; } html += "" } html += "" } // Column names html += "" if (setting.numbered === true) { html += "" // Row number } if (setting.key) { html += "" // Key } var numVisibleColumns = 0; _.each(setting.columns, function(col) { if (!col.hidden) numVisibleColumns++; html += "" // Data }) if (!setting.read_only) { if (setting.can_order) { numVisibleColumns++; html += ""; } numVisibleColumns++; html += ""; } // populate rows in the table from existing values var row_num = 1; if (keypath.length > 0 && _.size(setting_value) > 0) { var rowIsObject = setting.columns.length > 1; _.each(setting_value, function(row, rowIndexOrName) { var categoryPair = {}; var categoryValue = ""; if (isCategorized) { categoryValue = rowIsObject ? row[categoryKey] : row; categoryPair[categoryKey] = categoryValue; if (_.findIndex(setting_value, categoryPair) === rowIndexOrName) { html += makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, setting.can_add_new_categories, ""); } } html += ""; if (setting.numbered === true) { html += "" } if (setting.key) { html += "" } var isNonDeletableRow = !setting.can_add_new_rows; _.each(setting.columns, function(col) { var colValue, colName; if (isArray) { colValue = rowIsObject ? row[col.name] : row; colName = keypath + "[" + rowIndexOrName + "]" + (rowIsObject ? "." + col.name : ""); } else { colValue = row[col.name]; colName = keypath + "." + rowIndexOrName + "." + col.name; } isNonDeletableRow = isNonDeletableRow || (nonDeletableRowKey === col.name && nonDeletableRowValues.indexOf(colValue) !== -1); if (isArray && col.type === "checkbox" && col.editable) { html += ""; } else if (isArray && col.type === "time" && col.editable) { html += ""; } else { // Use a hidden input so that the values are posted. html += ""; } }); if (!setting.read_only) { if (setting.can_order) { html += "" } if (isNonDeletableRow) { html += ""; } else { html += ""; } } html += "" if (isCategorized && setting.can_add_new_rows && _.findLastIndex(setting_value, categoryPair) === rowIndexOrName) { html += makeTableInputs(setting, categoryPair, categoryValue); } row_num++ }); } // populate inputs in the table for new values if (!setting.read_only) { if (setting.can_add_new_categories) { html += makeTableCategoryInput(setting, numVisibleColumns); } if (setting.can_add_new_rows || setting.can_add_new_categories) { html += makeTableHiddenInputs(setting, {}, ""); } } html += "
" + setting.caption + "
" + group.label + "
#" + setting.key.label + "" + col.label + "
" + row_num + "" + rowIndexOrName + "" + "" + "" + "" + "" + colValue + "" + "" + "
" return html; } function makeTableCategoryHeader(categoryKey, categoryValue, numVisibleColumns, canRemove, message) { var html = "" + "" + "" + "" + categoryValue + "" + "" + ((canRemove) ? ( "" + "" + "" ) : ( "" )) + ""; return html; } function makeTableHiddenInputs(setting, initialValues, categoryValue) { var html = ""; if (setting.numbered === true) { html += ""; } if (setting.key) { html += "\ \ " } _.each(setting.columns, function(col) { var defaultValue = _.has(initialValues, col.name) ? initialValues[col.name] : col.default; if (col.type === "checkbox") { html += "" + "" + ""; } else if (col.type === "select") { html += "" html += ""; html += ""; } else { html += "" + "" + ""; } }) if (setting.can_order) { html += "" } html += "" html += "" return html } function makeTableCategoryInput(setting, numVisibleColumns) { var canAddRows = setting.can_add_new_rows; var categoryKey = setting.categorize_by_key; var placeholder = setting.new_category_placeholder || ""; var message = setting.new_category_message || ""; var html = "" + "" + "" + "" + "" + "" + "" + ""; return html; } function getDescriptionForKey(key) { for (var i in Settings.data.descriptions) { if (Settings.data.descriptions[i].name === key) { return Settings.data.descriptions[i]; } } } var SAVE_BUTTON_LABEL_SAVE = "Save"; var SAVE_BUTTON_LABEL_RESTART = "Save and restart"; var reasonsForRestart = []; var numChangesBySection = {}; function badgeForDifferences(changedElement) { // figure out which group this input is in var panelParentID = changedElement.closest('.panel').attr('id'); // if the panel contains non-grouped settings, the initial value is Settings.initialValues var isGrouped = $('#' + panelParentID).hasClass('grouped'); if (isGrouped) { var initialPanelJSON = Settings.initialValues[panelParentID] ? Settings.initialValues[panelParentID] : {}; // get a JSON representation of that section var panelJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID]; } else { var initialPanelJSON = Settings.initialValues; // get a JSON representation of that section var panelJSON = form2js(panelParentID, ".", false, cleanupFormValues, true); } var badgeValue = 0 var description = getDescriptionForKey(panelParentID); // badge for any settings we have that are not the same or are not present in initialValues for (var setting in panelJSON) { if ((!_.has(initialPanelJSON, setting) && panelJSON[setting] !== "") || (!_.isEqual(panelJSON[setting], initialPanelJSON[setting]) && (panelJSON[setting] !== "" || _.has(initialPanelJSON, setting)))) { badgeValue += 1; // add a reason to restart if (description && description.restart != false) { reasonsForRestart.push(setting); } } else { // remove a reason to restart if (description && description.restart != false) { reasonsForRestart = $.grep(reasonsForRestart, function(v) { return v != setting; }); } } } // update the list-group-item badge to have the new value if (badgeValue == 0) { badgeValue = "" } numChangesBySection[panelParentID] = badgeValue; var hasChanges = badgeValue > 0; if (!hasChanges) { for (var key in numChangesBySection) { if (numChangesBySection[key] > 0) { hasChanges = true; break; } } } $('.save-button').prop("disabled", !hasChanges); $('.save-button-text').html(reasonsForRestart.length > 0 ? SAVE_BUTTON_LABEL_RESTART : SAVE_BUTTON_LABEL_SAVE); // add the badge to the navbar item and the panel header $("a[href='" + settingsGroupAnchor(Settings.path, panelParentID) + "'] .badge").html(badgeValue); $("#" + panelParentID + " .panel-heading .badge").html(badgeValue); // make the navbar dropdown show a badge that is the total of the badges of all groups var totalChanges = 0; $('.panel-heading .badge').each(function(index){ if (this.innerHTML.length > 0) { totalChanges += parseInt(this.innerHTML); } }); Settings.pendingChanges = totalChanges; if (totalChanges == 0) { totalChanges = "" } var totalBadgeClass = Settings.content_settings ? '.content-settings-badge' : '.domain-settings-badge'; $(totalBadgeClass).html(totalChanges); $('#hamburger-badge').html(totalChanges); } function addTableRow(row) { var table = row.parents('table'); var isArray = table.data('setting-type') === 'array'; var keepField = row.data("keep-field"); var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS); var input_clone = row.clone(); // Change input row to data row var table = row.parents("table"); var setting_name = table.attr("name"); row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS); if (!isArray) { // show the key input var keyInput = row.children(".key").children("input"); // whenever the keyInput changes, re-badge for differences keyInput.on('change input propertychange', function(e){ // update siblings in the row to have the correct name var currentKey = $(this).val(); $(this).closest('tr').find('.value-col input').each(function(index){ var input = $(this); if (currentKey.length > 0) { input.attr("name", setting_name + "." + currentKey + "." + input.parent().attr('name')); } else { input.removeAttr("name"); } }); badgeForDifferences($(this)); }); } // if this is an array, add the row index (which is the index of the last row + 1) // as a data attribute to the row var row_index = 0; if (isArray) { var previous_row = row.siblings('.' + Settings.DATA_ROW_CLASS + ':last'); if (previous_row.length > 0) { row_index = parseInt(previous_row.attr(Settings.DATA_ROW_INDEX), 10) + 1; } else { row_index = 0; } row.attr(Settings.DATA_ROW_INDEX, row_index); } var focusChanged = false; _.each(row.children(), function(element) { if ($(element).hasClass("numbered")) { // Index row var numbers = columns.children(".numbered") if (numbers.length > 0) { $(element).html(parseInt(numbers.last().text()) + 1) } else { $(element).html(1) } } else if ($(element).hasClass(Settings.REORDER_BUTTONS_CLASS)) { $(element).html("") } else if ($(element).hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) { // Change buttons var anchor = $(element).children("a") anchor.removeClass(Settings.ADD_ROW_SPAN_CLASSES) anchor.addClass(Settings.DEL_ROW_SPAN_CLASSES) } else if ($(element).hasClass("key")) { var input = $(element).children("input") input.show(); } else if ($(element).hasClass(Settings.DATA_COL_CLASS)) { // show inputs var input = $(element).find("input"); input.show(); var isCheckbox = input.hasClass("table-checkbox"); var isDropdown = input.hasClass("table-dropdown"); if (isArray) { var key = $(element).attr('name'); // are there multiple columns or just one? // with multiple we have an array of Objects, with one we have an array of whatever the value type is var num_columns = row.children('.' + Settings.DATA_COL_CLASS).length var newName = setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : ""); input.attr("name", newName); if (isDropdown) { // default values for hidden inputs inside child selects gets cleared so we need to remind it var selectElement = $(element).children("select"); selectElement.attr("data-hidden-input", newName); $(element).children("input").val(selectElement.val()); } } if (isArray && !focusChanged) { input.focus(); focusChanged = true; } // if we are adding a dropdown, we should go ahead and make its select // element is visible if (isDropdown) { $(element).children("select").attr("style", ""); } if (isCheckbox) { $(input).find("input").attr("data-changed", "true"); } else { input.attr("data-changed", "true"); } } else { console.log("Unknown table element"); } }); if (!isArray) { keyInput.focus(); } input_clone.children('td').each(function () { if ($(this).attr("name") !== keepField) { $(this).find("input").val($(this).children('input').attr('data-default')); } }); if (isArray) { updateDataChangedForSiblingRows(row, true) // the addition of any table row should remove the empty-array-row row.siblings('.empty-array-row').remove() } badgeForDifferences($(table)) row.after(input_clone) } function deleteTableRow($row) { var $table = $row.closest('table'); var categoryName = $row.data("category"); var isArray = $table.data('setting-type') === 'array'; $row.empty(); if (!isArray) { if ($row.attr('name')) { $row.html(""); } else { // for rows that didn't have a key, simply remove the row $row.remove(); } } else { if ($table.find('.' + Settings.DATA_ROW_CLASS + "[data-category='" + categoryName + "']").length <= 1) { // This is the last row of the category, so delete the header $table.find('.' + Settings.DATA_CATEGORY_CLASS + "[data-category='" + categoryName + "']").remove(); } if ($table.find('.' + Settings.DATA_ROW_CLASS).length > 1) { updateDataChangedForSiblingRows($row); // this isn't the last row - we can just remove it $row.remove(); } else { // this is the last row, we can't remove it completely since we need to post an empty array $row .removeClass(Settings.DATA_ROW_CLASS) .removeClass(Settings.NEW_ROW_CLASS) .removeAttr("data-category") .addClass('empty-array-row') .html(""); } } // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated badgeForDifferences($table); } function addTableCategory($categoryInputRow) { var $input = $categoryInputRow.find("input").first(); var categoryValue = $input.prop("value"); if (!categoryValue || $categoryInputRow.closest("table").find("tr[data-category='" + categoryValue + "']").length !== 0) { $categoryInputRow.addClass("has-warning"); setTimeout(function () { $categoryInputRow.removeClass("has-warning"); }, 400); return; } var $rowInput = $categoryInputRow.next(".inputs").clone(); if (!$rowInput) { console.error("Error cloning inputs"); } var canAddRows = $categoryInputRow.data("can-add-rows"); var message = $categoryInputRow.data("message"); var categoryKey = $categoryInputRow.data("key"); var width = 0; $categoryInputRow .children("td") .each(function () { width += $(this).prop("colSpan") || 1; }); $input .prop("value", "") .focus(); $rowInput.find("td[name='" + categoryKey + "'] > input").first() .prop("value", categoryValue); $rowInput .attr("data-category", categoryValue) .addClass(Settings.NEW_ROW_CLASS); var $newCategoryRow = $(makeTableCategoryHeader(categoryKey, categoryValue, width, true, " - " + message)); $newCategoryRow.addClass(Settings.NEW_ROW_CLASS); $categoryInputRow .before($newCategoryRow) .before($rowInput); if (canAddRows) { $rowInput.removeAttr("hidden"); } else { addTableRow($rowInput); } } function deleteTableCategory($categoryHeaderRow) { var categoryName = $categoryHeaderRow.data("category"); $categoryHeaderRow .closest("table") .find("tr[data-category='" + categoryName + "']") .each(function () { if ($(this).hasClass(Settings.DATA_ROW_CLASS)) { deleteTableRow($(this)); } else { $(this).remove(); } }); } function toggleTableCategory($categoryHeaderRow) { var $icon = $categoryHeaderRow.find("." + Settings.TOGGLE_CATEGORY_SPAN_CLASS).first(); var categoryName = $categoryHeaderRow.data("category"); var wasExpanded = $icon.hasClass(Settings.TOGGLE_CATEGORY_EXPANDED_CLASS); if (wasExpanded) { $icon .addClass(Settings.TOGGLE_CATEGORY_CONTRACTED_CLASS) .removeClass(Settings.TOGGLE_CATEGORY_EXPANDED_CLASS); } else { $icon .addClass(Settings.TOGGLE_CATEGORY_EXPANDED_CLASS) .removeClass(Settings.TOGGLE_CATEGORY_CONTRACTED_CLASS); } $categoryHeaderRow .closest("table") .find("tr[data-category='" + categoryName + "']") .toggleClass("contracted", wasExpanded); } function moveTableRow(row, move_up) { var table = $(row).closest('table') var isArray = table.data('setting-type') === 'array' if (!isArray) { return; } if (move_up) { var prev_row = row.prev() if (prev_row.hasClass(Settings.DATA_ROW_CLASS)) { prev_row.before(row) } } else { var next_row = row.next() if (next_row.hasClass(Settings.DATA_ROW_CLASS)) { next_row.after(row) } } // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated badgeForDifferences($(table)) } function updateDataChangedForSiblingRows(row, forceTrue) { // anytime a new row is added to an array we need to set data-changed for all sibling row inputs to true // unless it matches the inital set of values if (!forceTrue) { // figure out which group this row is in var panelParentID = row.closest('.panel').attr('id') // get the short name for the setting from the table var tableShortName = row.closest('table').data('short-name') // get a JSON representation of that section var panelSettingJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID][tableShortName] if (Settings.initialValues[panelParentID]) { var initialPanelSettingJSON = Settings.initialValues[panelParentID][tableShortName] } else { var initialPanelSettingJSON = {}; } // if they are equal, we don't need data-changed isTrue = !_.isEqual(panelSettingJSON, initialPanelSettingJSON) } else { isTrue = true } row.siblings('.' + Settings.DATA_ROW_CLASS).each(function(){ var hiddenInput = $(this).find('td.' + Settings.DATA_COL_CLASS + ' input') if (isTrue) { hiddenInput.attr('data-changed', isTrue) } else { hiddenInput.removeAttr('data-changed') } }) } function cleanupFormValues(node) { if (node.type && node.type === 'checkbox') { return { name: node.name, value: node.checked ? true : false }; } else { return false; } } function showErrorMessage(title, message) { swal(title, message) }