diff --git a/css/style.css b/css/style.css index 05ec624..792b49d 100644 --- a/css/style.css +++ b/css/style.css @@ -191,6 +191,10 @@ input#trackname:focus:invalid { cursor: auto; } +#profile_editor { + display: none; +} + #profile_buttons { padding-top: 4px; } @@ -425,7 +429,6 @@ table.dataTable.display tbody tr.even:hover { * leaflet-sidebar-v2 */ -.leaflet-sidebar-pane#tab_profile, .leaflet-sidebar-pane#tab_data, .leaflet-sidebar-pane#tab_itinerary { /* Full height for content with inner scrolling, @@ -433,6 +436,30 @@ table.dataTable.display tbody tr.even:hover { height: 100%; } +.leaflet-sidebar-pane#tab_profile { + min-height: 100%; +} + +.leaflet-sidebar-pane#tab_profile .form-group { + margin-bottom: 5px; +} + +.leaflet-sidebar-pane#tab_profile label { + font-weight: 700; + margin-bottom: 0; +} + +.leaflet-sidebar-pane#tab_profile input[type='checkbox'] { + margin-right: 5px; + vertical-align: middle; +} + +.leaflet-sidebar-pane#tab_profile .help-block { + display: block; + color: #737373; + margin-bottom: 0.5rem; +} + .leaflet-sidebar-content { /* for optional-layers-tree */ overflow-x: auto; diff --git a/index.html b/index.html index 8772ae2..43491ce 100644 --- a/index.html +++ b/index.html @@ -559,8 +559,8 @@ @@ -648,39 +648,60 @@

Custom profile + >Customize profile

- -
-
- - - - Help +
+
+
+ + + +
+
+
+ +
+
+ + + + Help + + +
diff --git a/js/control/Profile.js b/js/control/Profile.js index fd7a955..f1f7af4 100644 --- a/js/control/Profile.js +++ b/js/control/Profile.js @@ -7,6 +7,15 @@ BR.Profile = L.Evented.extend({ lineNumbers: true }); + var that = this; + L.DomUtil.get('profile_advanced').addEventListener('click', function() { + that._toggleAdvanced(); + }); + L.DomUtil.get('profile_basic').addEventListener('click', function() { + that._toggleAdvanced(); + }); + + L.DomUtil.get('save').onclick = L.bind(this._save, this); L.DomUtil.get('upload').onclick = L.bind(this._upload, this); L.DomUtil.get('clear').onclick = L.bind(this.clear, this); @@ -31,8 +40,8 @@ BR.Profile = L.Evented.extend({ empty = !this.editor.getValue(), clean = this.editor.isClean(); - this.profileName = profileName; if (profileName && BR.conf.profilesUrl && (empty || clean)) { + this.profileName = profileName; if (!(profileName in this.cache)) { profileUrl = BR.conf.profilesUrl + profileName + '.brf'; BR.Util.get( @@ -73,17 +82,205 @@ BR.Profile = L.Evented.extend({ $(button).button('uploading'); evt.preventDefault(); + var that = this; this.fire('update', { profileText: profile, - callback: function() { + callback: function(err, profileId, profileText) { $(button).button('reset'); $(button).blur(); + if (!err) { + that.cache[profileId] = profileText; + } + } + }); + }, + + _buildCustomProfile: function() { + var profileText = this.cache[this.profileName]; + document.querySelectorAll('#profile_params input, #profile_params select').forEach(function(input) { + var name = input.name; + var value; + if (input.type == 'checkbox') { + value = input.checked; + } else { + value = input.value; + } + + var re = new RegExp( + '(assign\\s*' + + name + + '\\s*=?\\s*)([\\w.]*)(\\s*#\\s*%(.*)%\\s*(\\|\\s*(.*)\\s*\\|\\s*(.*)\\s*)?[\\r\\n])' + ); + profileText = profileText.replace(re, function(match, p1, p2, p3) { + return p1 + value + p3; + }); + }); + return profileText; + }, + + _save: function(evt) { + var profileText = this._buildCustomProfile(); + var that = this; + this.fire('update', { + profileText: profileText, + callback: function(err, profileId, profileText) { + if (!err) { + that.cache[profileId] = profileText; + } } }); }, _setValue: function(profileText) { - this.editor.setValue(profileText); - this.editor.markClean(); + if (L.DomUtil.get('profile_editor').style.display == 'flex') { + // Set value of the full editor and exit + this.editor.setValue(profileText); + this.editor.markClean(); + return; + } + + // Otherwise, create user friendly form + var global = profileText.split('---context:').filter(function(e) { + return e.startsWith('global'); + }); + if (global) { + // Remove ---context:global line + global = global[0].split('\n').slice(1); + + // Comment is mandatory + var assignRegex = /assign\s*(\w*)\s*=?\s*([\w\.]*)\s*#\s*%(.*)%\s*(\|\s*(.*)\s*\|\s*(.*)\s*)?$/; + var params = {}; + global.forEach(function(item) { + var match = item.match(assignRegex); + var value; + if (match) { + var name = match[1]; + var value = match[2]; + var description = match[5]; + + // Find out type + var paramType = match[6]; + var paramValues = {}; + if (paramType.match(/\[.*\]/)) { + paramType + .slice(1, -1) + .split(',') + .forEach(function(option) { + var splitOption = option.split('='); + var value = (splitOption[0] || '').replace(/^\s+|\s+$/g, ''); + var description = (splitOption[1] || '').replace(/^\s+|\s+$/g, ''); + paramValues[value] = description; + }); + paramType = 'select'; + } + + // Type is missing, let's try to induce it from value + if (!paramType) { + if (value == 'true' || value == 'false') { + paramType = 'boolean'; + } else { + paramType = 'number'; + } + } + + // Sanitize value according to type + if (paramType == 'boolean') { + value = value == 'true'; + } else if (paramType == 'number') { + value = Number.parseFloat(value); + if (Number.isNaN(value)) { + return; + } + } + + params[name] = { + description: description, + type: paramType, + value: value, + possible_values: paramValues + }; + } + }); + } + var paramsSection = L.DomUtil.get('profile_params'); + paramsSection.innerHTML = ''; + + if (!Object.keys(params).length) { + paramsSection.append(i18next.t('sidebar.profile.no_easy_configuration_warning')); + } + + Object.keys(params).forEach(function(param) { + var div = document.createElement('div'); + var label = document.createElement('label'); + + var paramType = params[param].type; + var paramName = i18next.exists('profileParameters.' + param + '.name') + ? i18next.t('profileParameters.' + param + '.name') + : param; + if (paramType == 'select') { + var select = document.createElement('select'); + select.name = paramName; + select.className = 'form-control'; + label.htmlFor = select.id = 'customize-profile-' + paramName; + + var paramValues = params[param].possible_values; + Object.keys(paramValues).forEach(function(paramValue) { + var option = document.createElement('option'); + option.value = paramValue; + option.append(paramValues[paramValue]); + select.appendChild(option); + }); + + label.append(paramName); + div.appendChild(label); + div.appendChild(select); + } else { + var input = document.createElement('input'); + input.name = paramName; + label.htmlFor = input.id = 'customize-profile-' + paramName; + if (paramType == 'number') { + input.type = 'number'; + input.value = params[param].value; + input.className = 'form-control'; + + label.append(paramName); + div.appendChild(label); + div.appendChild(input); + div.className = 'form-group'; + } else if (paramType == 'boolean') { + input.type = 'checkbox'; + input.checked = params[param].value; + + div.appendChild(input); + label.append(paramName); + div.appendChild(label); + } else { + // Unknown parameter type, skip it + return; + } + } + + var helpBlock = document.createElement('p'); + var description = i18next.exists('profileParameters.' + param + '.description') + ? i18next.t('profileParameters.' + param + '.description') + : params[param].description.replace(/^\s+|\s+$/g, ''); + helpBlock.innerHTML = description; + helpBlock.className = 'help-block'; + + div.appendChild(helpBlock); + paramsSection.appendChild(div); + }); + }, + + _toggleAdvanced: function() { + if (L.DomUtil.get('profile_editor').style.display == 'flex') { + L.DomUtil.get('profile_params_container').style.display = 'initial'; + L.DomUtil.get('profile_editor').style.display = 'none'; + this._setValue(this.editor.getValue()); + } else { + L.DomUtil.get('profile_params_container').style.display = 'none'; + L.DomUtil.get('profile_editor').style.display = 'flex'; + this._setValue(this._buildCustomProfile()); + } } }); diff --git a/js/index.js b/js/index.js index 568ce10..e6a8256 100644 --- a/js/index.js +++ b/js/index.js @@ -192,7 +192,7 @@ } if (evt.callback) { - evt.callback(); + evt.callback(err, profileId, evt.profileText); } }); }); diff --git a/locales/en.json b/locales/en.json index 3228655..1da8a25 100644 --- a/locales/en.json +++ b/locales/en.json @@ -167,8 +167,8 @@ } }, "sidebar": { - "custom-profile": { - "title": "Custom profile" + "customize-profile": { + "title": "Customize profile" }, "data": { "title": "Data" @@ -205,7 +205,11 @@ "profile": { "clear": "Clear", "help": "Help", + "no_easy_configuration_warning": "No easy configuration is available for this profile.", "placeholder": "Write your custom profile here.", + "save": "Save", + "switch_advanced": "Switch to advanced editor", + "switch_basic": "Switch to basic editor", "upload": "Upload" } },