From 720a9bc9a5f1e3fb375f6abaec5addb81d83499e Mon Sep 17 00:00:00 2001 From: kkapsner Date: Mon, 2 Jul 2018 00:29:41 +0200 Subject: [PATCH] Added feature to hide settings. --- _locales/de/messages.json | 9 ++++ _locales/en/messages.json | 9 ++++ lib/settingDefinitions.js | 9 ++++ lib/settings.js | 89 ++++++++++++++++++++++++++++---------- options/notVisible.svg | 76 ++++++++++++++++++++++++++++++++ options/options.css | 29 ++++++++++++- options/options.js | 89 ++++++++++++++++++++++++++++---------- options/optionsGui.js | 53 ++++++++++++++++++++++- options/settingsDisplay.js | 1 + options/visible.svg | 71 ++++++++++++++++++++++++++++++ releaseNotes.txt | 1 + 11 files changed, 387 insertions(+), 49 deletions(-) create mode 100644 options/notVisible.svg create mode 100644 options/visible.svg diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 63f94e4..4daf29b 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -91,6 +91,15 @@ "description": "" }, + "displayHiddenSettings_title": { + "message": "Versteckte Einstellungen anzeigen", + "description": "" + }, + "displayHiddenSettings_description": { + "message": "Aktivieren, um versteckte Einstellungen anzuzeigen.", + "description": "" + }, + "allowPDFCanvas_description": { "message": "Die native pdf.js verwendet um den Inhalt von PDFs anzuzeigen. Wenn viele Nachfragedialoge erscheinen oder die PDF-Ansicht nicht funktioniert, müssen diese erlaubt werden.", "description": "" diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 1fa778f..dbb834c 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -91,6 +91,15 @@ "description": "" }, + "displayHiddenSettings_title": { + "message": "Display hidden settings", + "description": "" + }, + "displayHiddenSettings_description": { + "message": "Activate to display the hidden settings.", + "description": "" + }, + "allowPDFCanvas_description": { "message": "Firefox's native PDF reader uses the API to display PDF content. If too many ask dialogs appear or the PDF reader does not work at all, these have to be allowed.", "description": "" diff --git a/lib/settingDefinitions.js b/lib/settingDefinitions.js index 882ac35..1f58b41 100644 --- a/lib/settingDefinitions.js +++ b/lib/settingDefinitions.js @@ -18,6 +18,15 @@ {name: "url", defaultValue: ""} ] }, + { + name: "hiddenSettings", + hideContainer: true, + defaultValue: {} + }, + { + name: "displayHiddenSettings", + defaultValue: false + }, { name: "urls", defaultValue: [], diff --git a/lib/settings.js b/lib/settings.js index 2a8309c..cc6d1c9 100644 --- a/lib/settings.js +++ b/lib/settings.js @@ -39,6 +39,7 @@ eventHandler.all = eventHandler.any; const settings = {}; let urlContainer; + let hideContainer; function isDefinitionInvalid(settingDefinition, newValue){ if (newValue === undefined && settingDefinition.optional){ @@ -219,6 +220,28 @@ } } + scope.on = function onSettingsChange(name, callback, url){ + if (Array.isArray(name)){ + name.forEach(function(name){ + onSettingsChange(name, callback, url); + }); + } + else { + if (eventHandler.hasOwnProperty(name)){ + if (!url){ + url = defaultSymbol; + } + if (!eventHandler[name].hasOwnProperty(url)){ + eventHandler[name][url] = []; + } + eventHandler[name][url].push(callback); + } + else { + logging.warning("Unable to register event handler for unknown setting", name); + } + } + }; + settingDefinitions.forEach(function(settingDefinition){ if (settingDefinition.urlContainer){ urlContainer = settingDefinition; @@ -255,7 +278,7 @@ settingDefinition.set = createSetter(settingDefinition); settingDefinition.reset = createResetter(settingDefinition); - + if (settingDefinition.urlSpecific){ if (!urlContainer){ logging.error("Unable to use url specific settings without url-container"); @@ -277,6 +300,41 @@ enumerable: true } ); + + if (settingDefinition.hideContainer){ + hideContainer = settingDefinition; + let changeListeners = {}; + settingDefinition.setHideByName = function(name, value){ + logging.verbose("set hide of", name, "to", value); + const hideStore = settingDefinition.get(); + hideStore[name] = value; + settingDefinition.set(hideStore); + (changeListeners[name] || []).forEach(function(listener){ + listener(value); + }); + }; + settingDefinition.getHideByName = function(name){ + const hideStore = settingDefinition.get(); + return hideStore[name] || false; + }; + settingDefinition.onHideChange = function(name, listener){ + if (!changeListeners[name]){ + changeListeners[name] = []; + } + changeListeners[name].push(listener); + }; + settingDefinition.on(function(event){ + const value = event.newValue; + Object.keys(value).forEach(function(name){ + if (value[name]){ + (changeListeners[name] || []).forEach(function(listener){ + listener(true); + }); + } + }); + }); + settingDefinition.hideAble = false; + } }); scope.getDefinition = function(name){ @@ -288,6 +346,13 @@ return undefined; } }; + + scope.getContainers = function(){ + return { + url: Object.create(urlContainer), + hide: Object.create(hideContainer) + }; + }; scope.set = function(name, ...args){ var foundDefinition = definitionsByName[name]; @@ -316,28 +381,6 @@ }).forEach(...args); }; - scope.on = function onSettingsChange(name, callback, url){ - if (Array.isArray(name)){ - name.forEach(function(name){ - onSettingsChange(name, callback, url); - }); - } - else { - if (eventHandler.hasOwnProperty(name)){ - if (!url){ - url = defaultSymbol; - } - if (!eventHandler[name].hasOwnProperty(url)){ - eventHandler[name][url] = []; - } - eventHandler[name][url].push(callback); - } - else { - logging.warning("Unable to register event handler for unknown setting", name); - } - } - }; - const resetSymbol = Symbol("reset"); function changeValue(name, newValue){ var settingDefinition = scope.getDefinition(name); diff --git a/options/notVisible.svg b/options/notVisible.svg new file mode 100644 index 0000000..08b0a3e --- /dev/null +++ b/options/notVisible.svg @@ -0,0 +1,76 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/options/options.css b/options/options.css index e8b4635..b2b1ac3 100644 --- a/options/options.css +++ b/options/options.css @@ -42,13 +42,14 @@ header .bookmarkNotice { } .settings .settingRow .content { + display: block; overflow: visible; padding: 0.5em 0; border-top: 1px solid #c1c1c1; position: relative; } -.settings .settingRow td:first-child .content { +.settings .settingRow td:first-child .content, td.hideColumn label { margin-left: 0.6em; } .settings .settingRow td:last-child .content { @@ -159,4 +160,30 @@ input[type=""], input[type="text"], input[type="number"], select { } .urlValues .notSpecifiedForUrl { opacity: 0.5; +} + +td.hideColumn { + width: 35px; +} + +.content.hideContent { + position: relative; +} +.content .hide, .displayHidden { + opacity: 0; + position: absolute; +} +.content .hide ~ .display, .displayHidden ~ .display { + display: inline-block; + margin-right: 5px; + width: 30px; + height: 20px; + background-repeat: no-repeat; + background-position-y: 50%; +} +.content .hide ~ .display, .displayHidden ~ .display { + background-image: url(visible.svg); +} +.content .hide:checked ~ .display, .displayHidden:checked ~ .display { + background-image: url(notVisible.svg); } \ No newline at end of file diff --git a/options/options.js b/options/options.js index 9490547..b09c605 100644 --- a/options/options.js +++ b/options/options.js @@ -66,6 +66,9 @@ }); document.body.appendChild(table); + const displayHidden = settings.getDefinition(settingsDisplay.displayHidden); + table.appendChild(optionsGui.createThead(displayHidden)); + let lastSection = null; let addSection = function addSection(name){ let body = document.createElement("tbody"); @@ -73,7 +76,7 @@ let row = document.createElement("tr"); row.className = "section"; let cell = document.createElement("td"); - cell.colSpan = 2; + cell.colSpan = 3; row.appendChild(cell); let heading = document.createElement("h2"); heading.textContent = browser.i18n.getMessage("section_" + name); @@ -108,6 +111,7 @@ }; addSection(); + const {hide: hideContainer} = settings.getContainers(); settingsDisplay.forEach(function(display){ if (typeof display === "string"){ addSection(display); @@ -129,32 +133,69 @@ } } if (setting){ - var row = optionsGui.createSettingRow(setting); - let section = lastSection; - section.addRow(row); - if (display.displayDependencies){ - var displayDependencies = display.displayDependencies; - displayDependencies = Array.isArray(displayDependencies)? - displayDependencies: - [displayDependencies]; - var computeDependencies = function(){ - logging.verbose("evaluate display dependencies for", setting); - row.classList[( - displayDependencies.some(function(displayDependency){ - return Object.keys(displayDependency).every(function(key){ - return displayDependency[key].indexOf(settings[key]) !== -1; - }); - }) - )? "remove": "add"]("hidden"); - section.updateDisplay(); - }; - computeDependencies(); - displayDependencies.forEach(function(displayDependency){ - Object.keys(displayDependency).forEach(function(name){ - settings.on(name, computeDependencies); + let hideChangeListeners = []; + setting.setHide = function setHide(value){ + if (hideContainer){ + hideContainer.setHideByName(display.name, value); + if (computeDependencies){ + computeDependencies(); + } + } + }; + setting.onHideChange = function(listener){ + hideChangeListeners.push(listener); + }; + setting.getHide = function getHide(){ + if (hideContainer){ + return hideContainer.getHideByName(display.name); + } + else { + return false; + } + }; + if (hideContainer){ + hideContainer.onHideChange(display.name, function(value){ + if (computeDependencies){ + computeDependencies(); + } + hideChangeListeners.forEach(function(listener){ + listener(value); }); }); } + + var row = optionsGui.createSettingRow(setting); + let section = lastSection; + section.addRow(row); + if (!display.displayDependencies){ + display.displayDependencies = {}; + } + var displayDependencies = display.displayDependencies; + displayDependencies = Array.isArray(displayDependencies)? + displayDependencies: + [displayDependencies]; + var computeDependencies = function(){ + logging.verbose("evaluate display dependencies for", setting); + row.classList[( + ( + displayHidden.get() || + !setting.getHide() + ) && + displayDependencies.some(function(displayDependency){ + return Object.keys(displayDependency).every(function(key){ + return displayDependency[key].indexOf(settings[key]) !== -1; + }); + }) + )? "remove": "add"]("hidden"); + section.updateDisplay(); + }; + computeDependencies(); + displayDependencies.forEach(function(displayDependency){ + Object.keys(displayDependency).forEach(function(name){ + settings.on(name, computeDependencies); + }); + }); + displayHidden.on(computeDependencies); } } }); diff --git a/options/optionsGui.js b/options/optionsGui.js index cc583ec..462ab36 100644 --- a/options/optionsGui.js +++ b/options/optionsGui.js @@ -303,10 +303,36 @@ c.appendChild(interaction); return c; } - + + function createHide(setting){ + var label = document.createElement("label"); + label.className = "content hideContent"; + var input = document.createElement("input"); + input.type = "checkbox"; + input.className = "hide"; + input.checked = setting.getHide(); + input.addEventListener("change", function(){ + setting.setHide(this.checked); + }); + setting.onHideChange(function(value){ + input.checked = value; + }); + + label.appendChild(input); + var display = document.createElement("span"); + display.className = "display"; + label.appendChild(display); + return label; + } + function createSettingRow(setting){ var tr = document.createElement("tr"); tr.className = "settingRow"; + + var hide = document.createElement("td"); + hide.className = "hideColumn"; + hide.appendChild(createHide(setting)); + tr.appendChild(hide); var left = document.createElement("td"); left.appendChild(createDescription(setting)); @@ -320,4 +346,29 @@ } scope.createSettingRow = createSettingRow; + + function createThead(displayHidden){ + const tHead = document.createElement("thead"); + const headRow = document.createElement("tr"); + const hideHeadCell = document.createElement("td"); + hideHeadCell.className = "hideColumn"; + hideHeadCell.title = browser.i18n.getMessage(displayHidden.name + "_description"); + const label = document.createElement("label"); + const input = createInput(displayHidden); + input.className = "displayHidden"; + label.appendChild(input); + const display = document.createElement("span"); + display.className = "display"; + label.appendChild(display); + hideHeadCell.appendChild(label); + headRow.appendChild(hideHeadCell); + + const restHeadCell = document.createElement("td"); + restHeadCell.colSpan = 2; + headRow.appendChild(restHeadCell); + tHead.appendChild(headRow); + return tHead; + } + + scope.createThead = createThead; }()); \ No newline at end of file diff --git a/options/settingsDisplay.js b/options/settingsDisplay.js index 588d28f..d42173e 100644 --- a/options/settingsDisplay.js +++ b/options/settingsDisplay.js @@ -402,6 +402,7 @@ } } ]; + settingsDisplay.displayHidden = "displayHiddenSettings"; if ((typeof module) !== "undefined"){ module.exports = settingsDisplay; diff --git a/options/visible.svg b/options/visible.svg new file mode 100644 index 0000000..fc2af13 --- /dev/null +++ b/options/visible.svg @@ -0,0 +1,71 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/releaseNotes.txt b/releaseNotes.txt index 2bce050..0448569 100644 --- a/releaseNotes.txt +++ b/releaseNotes.txt @@ -5,6 +5,7 @@ Version 0.4.6: new features: - Can protect Audio API + - Settings can be hidden fixes: -