diff --git a/domain-server/resources/describe-settings.json b/domain-server/resources/describe-settings.json index d95e77bf11..db7a82c0d0 100644 --- a/domain-server/resources/describe-settings.json +++ b/domain-server/resources/describe-settings.json @@ -50,12 +50,12 @@ { "name": "paths", "label": "", - "help": "", + "help": "", "type": "table", "key": { "name": "path", "label": "Path", - "placeholder": "/garden" + "placeholder": "/" }, "columns": [ { @@ -63,13 +63,8 @@ "label": "Viewpoint", "placeholder": "/512,512,512" } - ], - "default": { - "/": { - "viewpoint": "/512,512,512" - } - } - } + ] + } ] }, { diff --git a/domain-server/resources/web/js/settings.js b/domain-server/resources/web/js/settings.js index 2313cbe6b1..f956d4a374 100644 --- a/domain-server/resources/web/js/settings.js +++ b/domain-server/resources/web/js/settings.js @@ -21,38 +21,34 @@ var Settings = { }; var viewHelpers = { - getFormGroup: function(groupName, setting, values, isAdvanced, isLocked) { - if (groupName) { - setting_name = groupName + "." + setting.name; - } else { - setting_name = setting.name; - } - + getFormGroup: function(keypath, setting, values, isAdvanced, isLocked) { form_group = "<div class='form-group " + (isAdvanced ? Settings.ADVANCED_CLASS : "") + "'>"; - - if (_.has(values, groupName) && _.has(values[groupName], setting.name)) { - setting_value = values[groupName][setting.name]; - } else if (_.has(setting, 'default')) { - setting_value = setting.default; - } else { - setting_value = ""; + + setting_value = _(values).valueForKeyPath(keypath); + + if (!setting_value) { + if (_.has(setting, 'default')) { + setting_value = setting.default; + } else { + setting_value = ""; + } } - + label_class = 'control-label'; if (isLocked) { label_class += ' locked'; } - + common_attrs = " class='" + (setting.type !== 'checkbox' ? 'form-control' : '') - + " " + Settings.TRIGGER_CHANGE_CLASS + "' data-short-name='" + setting.name + "' name='" + setting_name + "' " - + "id='" + setting_name + "'"; - + + " " + Settings.TRIGGER_CHANGE_CLASS + "' data-short-name='" + setting.name + "' name='" + keypath + "' " + + "id='" + keypath + "'"; + if (setting.type === 'checkbox') { if (setting.label) { form_group += "<label class='" + label_class + "'>" + setting.label + "</label>" } form_group += "<div class='checkbox" + (isLocked ? " disabled" : "") + "'>" - form_group += "<label for='" + setting_name + "'>" + form_group += "<label for='" + keypath + "'>" form_group += "<input type='checkbox'" + common_attrs + (setting_value ? "checked" : "") + (isLocked ? " disabled" : "") + "/>" form_group += " " + setting.help + "</label>"; form_group += "</div>" @@ -60,52 +56,52 @@ var viewHelpers = { input_type = _.has(setting, 'type') ? setting.type : "text" if (setting.label) { - form_group += "<label for='" + setting_name + "' class='" + label_class + "'>" + setting.label + "</label>"; + form_group += "<label for='" + keypath + "' class='" + label_class + "'>" + setting.label + "</label>"; } - + if (input_type === 'table') { - form_group += makeTable(setting, setting_name, setting_value, isLocked) + form_group += makeTable(setting, keypath, setting_value, isLocked) } else { if (input_type === 'select') { - form_group += "<select class='form-control' data-hidden-input='" + setting_name + "'>'" - + form_group += "<select class='form-control' data-hidden-input='" + keypath + "'>'" + _.each(setting.options, function(option) { - form_group += "<option value='" + option.value + "'" + + form_group += "<option value='" + option.value + "'" + (option.value == setting_value ? 'selected' : '') + ">" + option.label + "</option>" }) - + form_group += "</select>" - + form_group += "<input type='hidden'" + common_attrs + "value='" + setting_value + "'>" } else { - + if (input_type == 'integer') { input_type = "text" } - + form_group += "<input type='" + input_type + "'" + common_attrs + - "placeholder='" + (_.has(setting, 'placeholder') ? setting.placeholder : "") + + "placeholder='" + (_.has(setting, 'placeholder') ? setting.placeholder : "") + "' value='" + setting_value + "'" + (isLocked ? " disabled" : "") + "/>" } - + form_group += "<span class='help-block'>" + setting.help + "</span>" - } + } } - + form_group += "</div>" return form_group } } -$(document).ready(function(){ +$(document).ready(function(){ /* - * Clamped-width. + * Clamped-width. * Usage: * <div data-clampedwidth=".myParent">This long content will force clamped width</div> * * Author: LV */ - + $('[data-clampedwidth]').each(function () { var elem = $(this); var parentPanel = elem.data('clampedwidth'); @@ -117,52 +113,52 @@ $(document).ready(function(){ resizeFn(); $(window).resize(resizeFn); }) - + $('#settings-form').on('click', '.' + Settings.ADD_ROW_BUTTON_CLASS, function(){ addTableRow(this); }) - + $('#settings-form').on('click', '.' + Settings.DEL_ROW_BUTTON_CLASS, function(){ deleteTableRow(this); }) - + $('#settings-form').on('click', '.' + Settings.MOVE_UP_BUTTON_CLASS, function(){ moveTableRow(this, true); }) - + $('#settings-form').on('click', '.' + Settings.MOVE_DOWN_BUTTON_CLASS, function(){ moveTableRow(this, false); }) - + $('#settings-form').on('keypress', 'table input', function(e){ if (e.keyCode == 13) { // 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 = $(this).parent('td').next(); - + if (sibling.hasClass(Settings.DATA_COL_CLASS)) { // set focus to next input sibling.find('input').focus() } else if (sibling.hasClass(Settings.ADD_DEL_BUTTONS_CLASS)) { sibling.find('.' + Settings.ADD_ROW_BUTTON_CLASS).click() - + // set focus to the first input in the new row $(this).closest('table').find('tr.inputs input:first').focus() - } + } } }); - + $('#settings-form').on('change', '.' + Settings.TRIGGER_CHANGE_CLASS , function(){ // this input was changed, add the changed data attribute to it $(this).attr('data-changed', true) - + badgeSidebarForDifferences($(this)) }) - + $('#advanced-toggle-button').click(function(){ Settings.showAdvanced = !Settings.showAdvanced var advancedSelector = $('.' + Settings.ADVANCED_CLASS) - + if (Settings.showAdvanced) { advancedSelector.show() $(this).html("Hide advanced") @@ -170,48 +166,48 @@ $(document).ready(function(){ advancedSelector.hide() $(this).html("Show advanced") } - + $(this).blur() }) - + $('#settings-form').on('click', '#choose-domain-btn', function(){ chooseFromHighFidelityDomains($(this)) }) - + $('#settings-form').on('change', 'select', function(){ $("input[name='" + $(this).attr('data-hidden-input') + "']").val($(this).val()).change() }) var panelsSource = $('#panels-template').html() Settings.panelsTemplate = _.template(panelsSource) - + var sidebarTemplate = $('#list-group-template').html() Settings.sidebarTemplate = _.template(sidebarTemplate) - + // $('body').scrollspy({ target: '#setup-sidebar'}) - + reloadSettings(); }) function reloadSettings() { $.getJSON('/settings.json', function(data){ _.extend(data, viewHelpers) - + $('.nav-stacked').html(Settings.sidebarTemplate(data)) $('#panels').html(Settings.panelsTemplate(data)) - + Settings.initialValues = form2js('settings-form', ".", false, cleanupFormValues, true); - + // add tooltip to locked settings $('label.locked').tooltip({ placement: 'right', title: 'This setting is in the master config file and cannot be changed' }) - + if (!_.has(data["locked"], "metaverse") && !_.has(data["locked"]["metaverse"], "id")) { // append the domain selection modal, as long as it's not locked appendDomainSelectionModal() - } + } }); } @@ -228,20 +224,20 @@ $('body').on('click', '.save-button', function(e){ $("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); - + console.log(formJSON); - + // re-enable all inputs $("input").each(function(){ $(this).prop('disabled', false); }); - + // remove focus from the button $(this).blur() - + // POST the form JSON to the domain-server settings.json endpoint so the settings are saved $.ajax('/settings.json', { data: JSON.stringify(formJSON), @@ -258,41 +254,41 @@ $('body').on('click', '.save-button', function(e){ showErrorMessage("Error", SETTINGS_ERROR_MESSAGE) reloadSettings(); }); - + return false; }); -function makeTable(setting, setting_name, setting_value, isLocked) { +function makeTable(setting, keypath, setting_value, isLocked) { var isArray = !_.has(setting, 'key') - + if (!isArray && setting.can_order) { setting.can_order = false; } - + var html = ""; - + if (setting.help) { html += "<span class='help-block'>" + setting.help + "</span>" } - html += "<table class='table table-bordered " + (isLocked ? "locked-table" : "") + "' data-short-name='" + setting.name + "' name='" + setting_name - + "' data-setting-type='" + (isArray ? 'array' : 'hash') + "'>" - + html += "<table class='table table-bordered " + (isLocked ? "locked-table" : "") + "' data-short-name='" + setting.name + + "' name='" + keypath + "' data-setting-type='" + (isArray ? 'array' : 'hash') + "'>" + // Column names html += "<tr class='headers'>" - + if (setting.numbered === true) { html += "<td class='number'><strong>#</strong></td>" // Row number } - + if (setting.key) { html += "<td class='key'><strong>" + setting.key.label + "</strong></td>" // Key } - + _.each(setting.columns, function(col) { html += "<td class='data'><strong>" + col.label + "</strong></td>" // Data }) - + if (!isLocked) { if (setting.can_order) { html += "<td class=" + Settings.REORDER_BUTTONS_CLASSES + @@ -300,39 +296,39 @@ function makeTable(setting, setting_name, setting_value, isLocked) { } html += "<td class=" + Settings.ADD_DEL_BUTTONS_CLASSES + "></td></tr>" } - + // populate rows in the table from existing values var row_num = 1 - + _.each(setting_value, function(row, indexOrName) { - html += "<tr class='" + Settings.DATA_ROW_CLASS + "'" + (isArray ? "" : "name='" + setting_name + "." + indexOrName + "'") + ">" - + html += "<tr class='" + Settings.DATA_ROW_CLASS + "'" + (isArray ? "" : "name='" + keypath + "." + indexOrName + "'") + ">" + if (setting.numbered === true) { html += "<td class='numbered'>" + row_num + "</td>" } - + if (setting.key) { html += "<td class='key'>" + indexOrName + "</td>" } - + _.each(setting.columns, function(col) { html += "<td class='" + Settings.DATA_COL_CLASS + "'>" - + if (isArray) { rowIsObject = setting.columns.length > 1 colValue = rowIsObject ? row[col.name] : row html += colValue - + // for arrays we add a hidden input to this td so that values can be posted appropriately - html += "<input type='hidden' name='" + setting_name + "[" + indexOrName + "]" + html += "<input type='hidden' name='" + keypath + "[" + indexOrName + "]" + (rowIsObject ? "." + col.name : "") + "' value='" + colValue + "'/>" } else if (row.hasOwnProperty(col.name)) { - html += row[col.name] + html += row[col.name] } - + html += "</td>" }) - + if (!isLocked) { if (setting.can_order) { html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES+ @@ -342,61 +338,61 @@ function makeTable(setting, setting_name, setting_value, isLocked) { html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES + "'><span class='" + Settings.DEL_ROW_SPAN_CLASSES + "'></span></td>" } - + html += "</tr>" - + row_num++ }) - + // populate inputs in the table for new values if (!isLocked) { html += makeTableInputs(setting) } html += "</table>" - + return html; } function makeTableInputs(setting) { var html = "<tr class='inputs'>" - + if (setting.numbered === true) { html += "<td class='numbered'></td>" } - + if (setting.key) { html += "<td class='key' name='" + setting.key.name + "'>\ <input type='text' class='form-control' placeholder='" + (_.has(setting.key, 'placeholder') ? setting.key.placeholder : "") + "' value=''>\ </td>" } - + _.each(setting.columns, function(col) { html += "<td class='" + Settings.DATA_COL_CLASS + "'name='" + col.name + "'>\ <input type='text' class='form-control' placeholder='" + (col.placeholder ? col.placeholder : "") + "'\ value='" + (col.default ? col.default : "") + "' data-default='" + (col.default ? col.default : "") + "'>\ </td>" }) - + if (setting.can_order) { html += "<td class='" + Settings.REORDER_BUTTONS_CLASSES + "'></td>" } html += "<td class='" + Settings.ADD_DEL_BUTTONS_CLASSES + "'><span class='glyphicon glyphicon-plus " + Settings.ADD_ROW_BUTTON_CLASS + "'></span></td>" html += "</tr>" - + return html } function badgeSidebarForDifferences(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]; - + // get a JSON representation of that section var panelJSON = form2js(panelParentID, ".", false, cleanupFormValues, true)[panelParentID]; } else { @@ -407,32 +403,32 @@ function badgeSidebarForDifferences(changedElement) { } var badgeValue = 0 - + // 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]) + if ((!_.has(initialPanelJSON, setting) && panelJSON[setting] !== "") || + (!_.isEqual(panelJSON[setting], initialPanelJSON[setting]) && (panelJSON[setting] !== "" || _.has(initialPanelJSON, setting)))) { badgeValue += 1 } } - + // update the list-group-item badge to have the new value if (badgeValue == 0) { badgeValue = "" } - + $("a[href='#" + panelParentID + "'] .badge").html(badgeValue); } function addTableRow(add_glyphicon) { var row = $(add_glyphicon).closest('tr') - + var table = row.parents('table') var isArray = table.data('setting-type') === 'array' - + var columns = row.parent().children('.' + Settings.DATA_ROW_CLASS) - + if (!isArray) { // Check key spaces var key = row.children(".key").children("input").val() @@ -453,7 +449,7 @@ function addTableRow(add_glyphicon) { return } } - + // Check empty fields var empty = false; _.each(row.children('.' + Settings.DATA_COL_CLASS + ' input'), function(element) { @@ -462,23 +458,23 @@ function addTableRow(add_glyphicon) { return } }) - + if (empty) { showErrorMessage("Error", "Empty field(s)") return } - + var input_clone = row.clone() - + // Change input row to data row var table = row.parents("table") - var setting_name = table.attr("name") + var setting_name = table.attr("name") var full_name = setting_name + "." + key row.addClass(Settings.DATA_ROW_CLASS + " " + Settings.NEW_ROW_CLASS) row.removeClass("inputs") - + _.each(row.children(), function(element) { - if ($(element).hasClass("numbered")) { + if ($(element).hasClass("numbered")) { // Index row var numbers = columns.children(".numbered") if (numbers.length > 0) { @@ -498,88 +494,88 @@ function addTableRow(add_glyphicon) { var input = $(element).children("input") $(element).html(input.val()) input.remove() - } else if ($(element).hasClass(Settings.DATA_COL_CLASS)) { + } else if ($(element).hasClass(Settings.DATA_COL_CLASS)) { // Hide inputs var input = $(element).children("input") input.attr("type", "hidden") - + if (isArray) { var row_index = row.siblings('.' + Settings.DATA_ROW_CLASS).length 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 - input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) + input.attr("name", setting_name + "[" + row_index + "]" + (num_columns > 1 ? "." + key : "")) } else { input.attr("name", full_name + "." + $(element).attr("name")) } - + input.attr("data-changed", "true") - + $(element).append(input.val()) } else { console.log("Unknown table element") } }) - + input_clone.find('input').each(function(){ $(this).val($(this).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() } - + badgeSidebarForDifferences($(table)) - + row.parent().append(input_clone) } function deleteTableRow(delete_glyphicon) { var row = $(delete_glyphicon).closest('tr') - + var table = $(row).closest('table') var isArray = table.data('setting-type') === 'array' - + row.empty(); - + if (!isArray) { - row.html("<input type='hidden' class='form-control' name='" + row.html("<input type='hidden' class='form-control' name='" + row.attr('name') + "' data-changed='true' value=''>"); } else { 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) row.addClass('empty-array-row') - - row.html("<input type='hidden' class='form-control' name='" + table.attr("name").replace('[]', '') + + row.html("<input type='hidden' class='form-control' name='" + table.attr("name").replace('[]', '') + "' data-changed='true' value=''>"); } } - + // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated badgeSidebarForDifferences($(table)) } function moveTableRow(move_glyphicon, move_up) { var row = $(move_glyphicon).closest('tr') - + 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)) { @@ -591,7 +587,7 @@ function moveTableRow(move_glyphicon, move_up) { next_row.after(row) } } - + // we need to fire a change event on one of the remaining inputs so that the sidebar badge is updated badgeSidebarForDifferences($(table)) } @@ -599,23 +595,23 @@ function moveTableRow(move_glyphicon, move_up) { 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] var initialPanelSettingJSON = Settings.initialValues[panelParentID][tableShortName] - + // 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) { @@ -631,19 +627,19 @@ function showRestartModal() { backdrop: 'static', keyboard: false }); - + var secondsElapsed = 0; var numberOfSecondsToWait = 3; - + var refreshSpan = $('span#refresh-time') refreshSpan.html(numberOfSecondsToWait + " seconds"); - + // call ourselves every 1 second to countdown var refreshCountdown = setInterval(function(){ secondsElapsed++; secondsLeft = numberOfSecondsToWait - secondsElapsed refreshSpan.html(secondsLeft + (secondsLeft == 1 ? " second" : " seconds")) - + if (secondsElapsed == numberOfSecondsToWait) { location.reload(true); clearInterval(refreshCountdown); @@ -666,22 +662,22 @@ function showErrorMessage(title, message) { function chooseFromHighFidelityDomains(clickedButton) { // setup the modal to help user pick their domain if (Settings.initialValues.metaverse.access_token) { - + // add a spinner to the choose button clickedButton.html("Loading domains...") clickedButton.attr('disabled', 'disabled') - + // get a list of user domains from data-web data_web_domains_url = "https://metaverse.highfidelity.com/api/v1/domains?access_token=" $.getJSON(data_web_domains_url + Settings.initialValues.metaverse.access_token, function(data){ - + modal_buttons = { cancel: { label: 'Cancel', className: 'btn-default' } } - + if (data.data.domains.length) { // setup a select box for the returned domains modal_body = "<p>Choose the High Fidelity domain you want this domain-server to represent.<br/>This will set your domain ID on the settings page.</p>" @@ -705,25 +701,25 @@ function chooseFromHighFidelityDomains(clickedButton) { window.open("https://metaverse.highfidelity.com/user/domains", '_blank'); } } - modal_body = "<p>You do not have any domains in your High Fidelity account." + + modal_body = "<p>You do not have any domains in your High Fidelity account." + "<br/><br/>Go to your domains page to create a new one. Once your domain is created re-open this dialog to select it.</p>" } - - + + bootbox.dialog({ title: "Choose matching domain", message: modal_body, buttons: modal_buttons }) - + // remove the spinner from the choose button clickedButton.html("Choose from my domains") clickedButton.removeAttr('disabled') }) - + } else { bootbox.alert({ - message: "You must have an access token to query your High Fidelity domains.<br><br>" + + message: "You must have an access token to query your High Fidelity domains.<br><br>" + "Please follow the instructions on the settings page to add an access token.", title: "Access token required" }) diff --git a/domain-server/resources/web/stats/js/underscore-keypath.min.js b/domain-server/resources/web/js/underscore-keypath.min.js similarity index 100% rename from domain-server/resources/web/stats/js/underscore-keypath.min.js rename to domain-server/resources/web/js/underscore-keypath.min.js diff --git a/domain-server/resources/web/settings/index.shtml b/domain-server/resources/web/settings/index.shtml index ad6ed0f41c..9bbf913b1a 100644 --- a/domain-server/resources/web/settings/index.shtml +++ b/domain-server/resources/web/settings/index.shtml @@ -5,7 +5,7 @@ <div class="col-md-12"> <div class="alert" style="display:none;"></div> </div> - + <div class="col-md-3 col-sm-3" id="setup-sidebar-col"> <div id="setup-sidebar" data-clampedwidth="#setup-sidebar-col" class="hidden-xs" data-spy="affix" data-offset-top="55"> <script id="list-group-template" type="text/template"> @@ -19,18 +19,18 @@ </li> <% }); %> </script> - + <ul class="nav nav-pills nav-stacked"> </ul> - + <button id="advanced-toggle-button" hidden=true class="btn btn-info">Show advanced</button> <button class="btn btn-success save-button">Save and restart</button> </div> </div> - + <div class="col-md-9 col-sm-9 col-xs-12"> <form id="settings-form" role="form"> - + <script id="panels-template" type="text/template"> <% _.each(descriptions, function(group){ %> <% split_settings = _.partition(group.settings, function(value, index) { return !value.advanced }) %> @@ -38,24 +38,26 @@ <% if (isAdvanced) { %> <% $("a[href=#" + group.name + "]").addClass('advanced-setting').hide() %> <% } %> - + <% isGrouped = !!group.name %> <% panelID = isGrouped ? group.name : group.label %> - <div class="panel panel-default<%- (isAdvanced) ? ' advanced-setting' : '' %><%- (isGrouped) ? ' grouped' : '' %>" + <div class="panel panel-default<%- (isAdvanced) ? ' advanced-setting' : '' %><%- (isGrouped) ? ' grouped' : '' %>" id="<%- panelID %>"> <div class="panel-heading"> <h3 class="panel-title"><%- group.label %></h3> </div> <div class="panel-body"> <% _.each(split_settings[0], function(setting) { %> - <%= getFormGroup(group.name, setting, values, false, + <% keypath = isGrouped ? group.name + "." + setting.name : setting.name %> + <%= getFormGroup(keypath, setting, values, false, (_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %> <% }); %> <% if (!_.isEmpty(split_settings[1])) { %> <% $("#advanced-toggle-button").show() %> <% _.each(split_settings[1], function(setting) { %> - <%= getFormGroup(group.name, setting, values, true, + <% keypath = isGrouped ? group.name + "." + setting.name : setting.name %> + <%= getFormGroup(keypath, setting, values, true, (_.has(locked, group.name) && _.has(locked[group.name], setting.name))) %> <% }); %> <% }%> @@ -64,12 +66,12 @@ <% }); %> </script> - + <div id="panels"></div> - + </form> </div> - + <div class="col-xs-12 hidden-sm hidden-md hidden-lg"> <button class="btn btn-success save-button" id="small-save-button">Save and restart</button> </div> @@ -90,6 +92,7 @@ <!--#include virtual="footer.html"--> <script src='/js/underscore-min.js'></script> +<script src='/js/underscore-keypath.min.js'></script> <script src='/js/bootbox.min.js'></script> <script src='/js/sweet-alert.min.js'></script> <script src='/js/settings.js'></script> diff --git a/domain-server/resources/web/stats/index.shtml b/domain-server/resources/web/stats/index.shtml index dc7fe9678f..13967d4e36 100644 --- a/domain-server/resources/web/stats/index.shtml +++ b/domain-server/resources/web/stats/index.shtml @@ -9,6 +9,6 @@ <script src='js/json.human.js'></script> <script src='js/highcharts-custom.js'></script> <script src='/js/underscore-min.js'></script> -<script src='js/underscore-keypath.min.js'></script> +<script src='/js/underscore-keypath.min.js'></script> <script src='/js/bootbox.min.js'></script> <!--#include virtual="page-end.html"--> diff --git a/libraries/shared/src/HifiConfigVariantMap.cpp b/libraries/shared/src/HifiConfigVariantMap.cpp index c92260210e..3fe5d9ec3f 100644 --- a/libraries/shared/src/HifiConfigVariantMap.cpp +++ b/libraries/shared/src/HifiConfigVariantMap.cpp @@ -72,7 +72,7 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL int configIndex = argumentList.indexOf(CONFIG_FILE_OPTION); QString configFilePath; - + if (configIndex != -1) { // we have a config file - try and read it configFilePath = argumentList[configIndex + 1]; @@ -82,8 +82,8 @@ QVariantMap HifiConfigVariantMap::mergeCLParametersWithJSONConfig(const QStringL QCoreApplication::organizationName(), QCoreApplication::applicationName()); } - - + + return mergedMap; } @@ -94,23 +94,23 @@ HifiConfigVariantMap::HifiConfigVariantMap() : _userConfig(), _mergedConfig() { - + } void HifiConfigVariantMap::loadMasterAndUserConfig(const QStringList& argumentList) { // check if there is a master config file const QString MASTER_CONFIG_FILE_OPTION = "--master-config"; - + int masterConfigIndex = argumentList.indexOf(MASTER_CONFIG_FILE_OPTION); if (masterConfigIndex != -1) { QString masterConfigFilepath = argumentList[masterConfigIndex + 1]; - + loadMapFromJSONFile(_masterConfig, masterConfigFilepath); } - + // load the user config const QString USER_CONFIG_FILE_OPTION = "--user-config"; - + int userConfigIndex = argumentList.indexOf(USER_CONFIG_FILE_OPTION); if (userConfigIndex != -1) { _userConfigFilename = argumentList[userConfigIndex + 1]; @@ -119,26 +119,26 @@ void HifiConfigVariantMap::loadMasterAndUserConfig(const QStringList& argumentLi QCoreApplication::organizationName(), QCoreApplication::applicationName()); } - + loadMapFromJSONFile(_userConfig, _userConfigFilename); - + // the merged config is initially matched to the master config _mergedConfig = _masterConfig; - + // then we merge in anything missing from the user config addMissingValuesToExistingMap(_mergedConfig, _userConfig); } void HifiConfigVariantMap::loadMapFromJSONFile(QVariantMap& existingMap, const QString& filename) { QFile configFile(filename); - + if (configFile.exists()) { qCDebug(shared) << "Reading JSON config file at" << filename; configFile.open(QIODevice::ReadOnly); - + QJsonDocument configDocument = QJsonDocument::fromJson(configFile.readAll()); existingMap = configDocument.toVariant().toMap(); - + } else { qCDebug(shared) << "Could not find JSON config file at" << filename; } @@ -148,7 +148,7 @@ void HifiConfigVariantMap::addMissingValuesToExistingMap(QVariantMap& existingMa foreach(const QString& key, newMap.keys()) { if (existingMap.contains(key)) { // if this is just a regular value, we're done - we don't ovveride - + if (newMap[key].canConvert(QMetaType::QVariantMap) && existingMap[key].canConvert(QMetaType::QVariantMap)) { // there's a variant map below and the existing map has one too, so we need to keep recursing addMissingValuesToExistingMap(*static_cast<QVariantMap*>(existingMap[key].data()), newMap[key].toMap()); @@ -159,11 +159,11 @@ void HifiConfigVariantMap::addMissingValuesToExistingMap(QVariantMap& existingMa } } -const QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath) { +QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath) { int dotIndex = keyPath.indexOf('.'); - + QString firstKey = (dotIndex == -1) ? keyPath : keyPath.mid(0, dotIndex); - + if (variantMap.contains(firstKey)) { if (dotIndex == -1) { return &variantMap[firstKey]; @@ -171,6 +171,6 @@ const QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath) return valueForKeyPath(*static_cast<QVariantMap*>(variantMap[firstKey].data()), keyPath.mid(dotIndex + 1)); } } - + return NULL; } diff --git a/libraries/shared/src/HifiConfigVariantMap.h b/libraries/shared/src/HifiConfigVariantMap.h index 6bdeb15589..3566f446a2 100644 --- a/libraries/shared/src/HifiConfigVariantMap.h +++ b/libraries/shared/src/HifiConfigVariantMap.h @@ -17,26 +17,26 @@ class HifiConfigVariantMap { public: static QVariantMap mergeCLParametersWithJSONConfig(const QStringList& argumentList); - + HifiConfigVariantMap(); void loadMasterAndUserConfig(const QStringList& argumentList); - + const QVariantMap& getMasterConfig() const { return _masterConfig; } QVariantMap& getUserConfig() { return _userConfig; } QVariantMap& getMergedConfig() { return _mergedConfig; } - + const QString& getUserConfigFilename() const { return _userConfigFilename; } private: QString _userConfigFilename; - + QVariantMap _masterConfig; QVariantMap _userConfig; QVariantMap _mergedConfig; - + void loadMapFromJSONFile(QVariantMap& existingMap, const QString& filename); void addMissingValuesToExistingMap(QVariantMap& existingMap, const QVariantMap& newMap); }; -const QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath); +QVariant* valueForKeyPath(QVariantMap& variantMap, const QString& keyPath); #endif // hifi_HifiConfigVariantMap_h