diff options
-rw-r--r-- | html/preferences_panel/pref.js | 120 | ||||
-rw-r--r-- | test/spec/LibreJSSpec.js | 2 |
2 files changed, 63 insertions, 59 deletions
diff --git a/html/preferences_panel/pref.js b/html/preferences_panel/pref.js index c3597e1..a1c6cb5 100644 --- a/html/preferences_panel/pref.js +++ b/html/preferences_panel/pref.js @@ -25,29 +25,32 @@ const LIST_NAMES = ["white", "black"]; - var Model = { + const Model = { lists: {}, prefs: null, malformedUrl(url) { - let error = null; try { - let objUrl = new URL(url); - url = objUrl.href; + const objUrl = new URL(url); + const newUrl = objUrl.href; if (!objUrl.protocol.startsWith("http")) { - error = "Please enter http:// or https:// URLs only"; - } else if (!/^[^*]+\*?$/.test(url)) { - error = "Only one single trailing path wildcard (/*) allowed"; + return "Please enter http:// or https:// URLs only"; + } else if (!/^[^*]+\*?$/.test(newUrl)) { + return "Only one single trailing path wildcard (/*) allowed"; } } catch (e) { if (/^https?:\/\/\*\./.test(url)) { return this.malformedUrl(url.replace("*.", "")); } - error = "Invalid URL"; - if (url && !url.includes("://")) error += ": missing protocol, either http:// or https://"; - else if (url.endsWith("://")) error += ": missing domain name"; + const prefix = "Invalid URL"; + if (url && !url.includes("://")) + return prefix + ": missing protocol, either http:// or https://"; + else if (url.endsWith("://")) + return prefix + ": missing domain name"; + else + return prefix; } - return error; + return null; }, async save(prefs = this.prefs) { @@ -63,7 +66,7 @@ }, async addToList(list, ...items) { - let other = list === Model.lists.black ? Model.lists.white : Model.lists.black; + const other = list === Model.lists.black ? Model.lists.white : Model.lists.black; this.saving = true; try { await Promise.all([ @@ -75,8 +78,9 @@ } } }; + Model.loading = (async () => { - let prefsNames = [ + const prefsNames = [ "whitelist", "blacklist", "subject", @@ -84,17 +88,17 @@ ]; Model.prefs = await browser.storage.local.get(prefsNames.map(name => `pref_${name}`)); - for (let listName of LIST_NAMES) { - let prefName = `pref_${listName}list`; + for (const listName of LIST_NAMES) { + const prefName = `pref_${listName}list`; await (Model.lists[listName] = new ListStore(prefName, Storage.CSV)) .load(Model.prefs[prefName]); } })(); - var Controller = { + const Controller = { init() { - let widgetsRoot = this.root = document.getElementById("widgets"); - for (let widget of widgetsRoot.querySelectorAll('[id^="pref_"]')) { + const widgetsRoot = this.root = document.getElementById("widgets"); + for (const widget of widgetsRoot.querySelectorAll('[id^="pref_"]')) { if (widget.id in Model.lists) { populateListUI(widget); } else if (widget.id in Model.prefs) { @@ -105,7 +109,7 @@ this.populateListUI(); this.syncAll(); - for (let ev in Listeners) { + for (const ev in Listeners) { widgetsRoot.addEventListener(ev, Listeners[ev]); } document.getElementById("site").onfocus = e => { @@ -126,7 +130,7 @@ }, async addSite(list) { - let url = document.getElementById("site").value.trim(); + const url = document.getElementById("site").value.trim(); if (url && !Model.malformedUrl(url)) { await this.addToList(list, url); @@ -138,7 +142,7 @@ this.syncAll(); }, async swapSelection(list) { - let origin = list === Model.lists.black ? "white" : "black"; + const origin = list === Model.lists.black ? "white" : "black"; await this.addToList(list, ...Array.prototype.map.call( document.querySelectorAll(`select#${origin} option:checked`), option => option.value) @@ -151,15 +155,15 @@ }, syncSiteUI() { - let widget = document.getElementById("site"); - let list2button = listName => document.getElementById(`cmd-${listName}list-site`); + const widget = document.getElementById("site"); + const list2button = listName => document.getElementById(`cmd-${listName}list-site`); - for (let bi of LIST_NAMES.map(list2button)) { + for (const bi of LIST_NAMES.map(list2button)) { bi.disabled = true; } - let url = widget.value.trim(); - let malformedUrl = url && Model.malformedUrl(url); + const url = widget.value.trim(); + const malformedUrl = url && Model.malformedUrl(url); widget.classList.toggle("error", !!malformedUrl); document.getElementById("site-error").textContent = malformedUrl || ""; if (!url) return; @@ -167,8 +171,8 @@ widget.value = url; } - for (let listName of LIST_NAMES) { - let list = Model.lists[listName]; + for (const listName of LIST_NAMES) { + const list = Model.lists[listName]; if (!list.contains(url)) { list2button(listName).disabled = false; } @@ -176,19 +180,18 @@ }, syncListsUI() { - let total = 0; - for (let id of ["black", "white"]) { - let selected = document.querySelectorAll(`select#${id} option:checked`).length; - let other = id === "black" ? "white" : "black"; + const total = ["black", "white"].reduce((cum, id) => { + const selected = document.querySelectorAll(`select#${id} option:checked`).length; + const other = id === "black" ? "white" : "black"; document.getElementById(`cmd-${other}list`).disabled = selected === 0; - total += selected; - } + return cum + selected; + }, 0); document.getElementById("cmd-delete").disabled = total === 0; }, async deleteSelection() { - for (let id of ["black", "white"]) { - let selection = document.querySelectorAll(`select#${id} option:checked`); + for (const id of ["black", "white"]) { + const selection = document.querySelectorAll(`select#${id} option:checked`); await Model.lists[id].remove(...Array.prototype.map.call(selection, option => option.value)); } this.populateListUI(); @@ -197,16 +200,16 @@ populateListUI(widget) { if (!widget) { - for (let id of ["white", "black"]) { + for (const id of ["white", "black"]) { this.populateListUI(document.getElementById(id)); } return; } widget.innerHTML = ""; - let items = [...Model.lists[widget.id].items].sort(); - let options = new DocumentFragment(); - for (let item of items) { - let option = document.createElement("option"); + const items = [...Model.lists[widget.id].items].sort(); + const options = new DocumentFragment(); + for (const item of items) { + const option = document.createElement("option"); option.value = option.textContent = option.title = item; options.appendChild(option); } @@ -214,7 +217,7 @@ } }; - var KeyEvents = { + const KeyEvents = { Delete(e) { if (e.target.matches("#lists select")) { Controller.deleteSelection(); @@ -227,7 +230,7 @@ }, KeyA(e) { if (e.target.matches("select") && e.ctrlKey) { - for (let o of e.target.options) { + for (const o of e.target.options) { o.selected = true; } Controller.syncListsUI(); @@ -235,14 +238,14 @@ } } - var Listeners = { + const Listeners = { async change(e) { - let { target } = e; - let { id } = target; + const { target } = e; + const { id } = target; if (id in Model.lists) { Controller.syncListsUI(); - let selection = target.querySelectorAll("option:checked"); + const selection = target.querySelectorAll("option:checked"); if (selection.length === 1) { document.getElementById("site").value = selection[0].value; } @@ -251,24 +254,25 @@ }, click(e) { - let { target } = e; + const { target } = e; - if (!/^cmd-(white|black|delete)(list-site)?/.test(target.id)) return; + const match = /^cmd-(white|black|delete)(list-site)?/.exec(target.id); + if (!match) return; e.preventDefault(); - let cmd = RegExp.$1; + const cmd = match[1]; if (cmd === "delete") { Controller.deleteSelection(); return; } - let list = Model.lists[cmd]; + const list = Model.lists[cmd]; if (list) { - Controller[RegExp.$2 ? "addSite" : "swapSelection"](list); + Controller[match[2] ? "addSite" : "swapSelection"](list); return; } }, keypress(e) { - let { code } = e; + const { code } = e; if (code && typeof KeyEvents[code] === "function") { if (KeyEvents[code](e) === false) { e.preventDefault(); @@ -278,15 +282,15 @@ }, async input(e) { - let { target } = e; - let { id } = target; + const { target } = e; + const { id } = target; if (!id) return; if (id === "site") { Controller.syncSiteUI(); - let url = target.value; + const url = target.value; if (url) { - let o = document.querySelector(`#lists select option[value="${url}"]`); + const o = document.querySelector(`#lists select option[value="${url}"]`); if (o) { o.scrollIntoView(); o.selected = true; @@ -302,7 +306,7 @@ } }; - window.addEventListener("DOMContentLoaded", async e => { + window.addEventListener("DOMContentLoaded", async _ => { await Model.loading; Controller.init(); }); diff --git a/test/spec/LibreJSSpec.js b/test/spec/LibreJSSpec.js index 3ec75c5..5632e9f 100644 --- a/test/spec/LibreJSSpec.js +++ b/test/spec/LibreJSSpec.js @@ -283,7 +283,7 @@ describe('LibreJS\' components', () => { }); }); - describe('Contact finder', () => { + describe('The contact finder', () => { it('should display the contact finder iframe', async (done) => { await browser.runtime.connect({ name: "port-from-cs" }).postMessage({ invoke_contact_finder: 1 }); // Direct await / async does not work as executeScript does not wait |