diff --git a/README.md b/README.md index d124296b..d61df1d9 100644 --- a/README.md +++ b/README.md @@ -67,26 +67,18 @@ npm update npm install ``` -To generate HTML that uses `config.json` (needed to develop/build the extension), run: +Generate the HTML pages: ``` -npm run ejs +npm run pug ``` -Afterwards, you will need to run it if you modify `config.json` or any files ending with .ejs. - ### Build the extension zip archive: ``` npm run build ``` -### Run automated tests - -``` -npm run test -``` - ### Test in Firefox ``` diff --git a/package.json b/package.json index 222b283d..63673432 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "start": "web-ext run", "build": "web-ext build", "test": "web-ext lint", - "instances": "python3 src/instances/get_instances.py && git update-index --assume-unchanged src/instances/blacklist.json src/instances/data.json", "pug": "pug --pretty --basedir ./ --obj ./src/config.json src/pages/options/index.pug --out src/pages/options/ && pug --pretty --basedir ./ --obj ./src/config.json src/pages/popup/popup.pug --out src/pages/popup/" }, "repository": { diff --git a/src/assets/javascripts/general.js b/src/assets/javascripts/general.js index b2ad7c25..0295eb07 100644 --- a/src/assets/javascripts/general.js +++ b/src/assets/javascripts/general.js @@ -1,4 +1,7 @@ "use strict" + +import utils from "./utils.js" + window.browser = window.browser || window.chrome let exceptions @@ -10,36 +13,16 @@ function isException(url) { } function init() { - return new Promise(resolve => { - browser.storage.local.get("options", r => { - if (r.options) exceptions = r.options.exceptions - resolve() - }) + return new Promise(async resolve => { + const options = await utils.getOptions() + if (options) exceptions = options.exceptions + resolve() }) } init() browser.storage.onChanged.addListener(init) -async function initDefaults() { - return new Promise(resolve => - browser.storage.local.set( - { - options: { - exceptions: { - url: [], - regex: [], - }, - theme: "detect", - popupServices: ["youtube", "twitter", "tiktok", "imgur", "reddit", "quora", "translate", "maps"], - }, - }, - () => resolve() - ) - ) -} - export default { isException, - initDefaults, } diff --git a/src/assets/javascripts/services.js b/src/assets/javascripts/services.js index 159e39e2..181dec00 100644 --- a/src/assets/javascripts/services.js +++ b/src/assets/javascripts/services.js @@ -1,20 +1,14 @@ -window.browser = window.browser || window.chrome - import utils from "./utils.js" +window.browser = window.browser || window.chrome + let config, options function init() { return new Promise(async resolve => { - browser.storage.local.get(["options"], r => { - options = r.options - fetch("/config.json") - .then(response => response.text()) - .then(configData => { - config = JSON.parse(configData) - resolve() - }) - }) + options = await utils.getOptions() + config = await utils.getConfig() + resolve() }) } @@ -29,8 +23,8 @@ function all(service, frontend, options, config) { instances.push(...options[frontend]) } } - } else { - instances.push(...options[frontend]) + } else if (options[frontend]) { + instances = options[frontend] } return instances } @@ -393,34 +387,28 @@ function redirect(url, type, initiator, forceRedirection, tabId) { } function computeService(url, returnFrontend) { - return new Promise(resolve => { - fetch("/config.json") - .then(response => response.text()) - .then(configData => { - const config = JSON.parse(configData) - browser.storage.local.get(["redirects", "options"], r => { - const options = r.options - for (const service in config.services) { - if (regexArray(service, url, config)) { - resolve(service) - return - } else { - for (const frontend in config.services[service].frontends) { - if (all(service, frontend, options, config).includes(utils.protocolHost(url))) { - if (returnFrontend) resolve([service, frontend, utils.protocolHost(url)]) - else resolve(service) - return - } - } - } + return new Promise(async resolve => { + const config = await utils.getConfig() + const options = await utils.getOptions() + for (const service in config.services) { + if (regexArray(service, url, config)) { + resolve(service) + return + } else { + for (const frontend in config.services[service].frontends) { + if (all(service, frontend, options, config).includes(utils.protocolHost(url))) { + if (returnFrontend) resolve([service, frontend, utils.protocolHost(url)]) + else resolve(service) + return } - resolve() - }) - }) + } + } + } + resolve() }) } -function switchInstance(url) { +function _switchInstance(url) { return new Promise(async resolve => { await init() const protocolHost = utils.protocolHost(url) @@ -499,102 +487,109 @@ function reverse(url, urlString) { function initDefaults() { return new Promise(resolve => { - fetch("/config.json") - .then(response => response.text()) - .then(configData => { - browser.storage.local.get(["options"], r => { - let options = r.options - let config = JSON.parse(configData) - for (const service in config.services) { - options[service] = {} - for (const defaultOption in config.services[service].options) { - options[service][defaultOption] = config.services[service].options[defaultOption] - } - for (const frontend in config.services[service].frontends) { - if (config.services[service].frontends[frontend].instanceList) { - options[frontend] = [] - } - } + browser.storage.local.clear(async () => { + let config = await utils.getConfig() + let options = {} + for (const service in config.services) { + options[service] = {} + for (const defaultOption in config.services[service].options) { + options[service][defaultOption] = config.services[service].options[defaultOption] + } + for (const frontend in config.services[service].frontends) { + if (config.services[service].frontends[frontend].instanceList) { + options[frontend] = [] } - browser.storage.local.set( - { options }, - () => resolve() - ) - }) - }) - }) -} + } + } + options['exceptions'] = { + url: [], + regex: [], + } + options['theme'] = "detect" + options['popupServices'] = ["youtube", "twitter", "tiktok", "imgur", "reddit", "quora", "translate", "maps"] -function backupOptions() { - return new Promise(resolve => { - browser.storage.local.get( - "options", r => { - const oldOptions = r.options - browser.storage.local.clear(() => { - browser.storage.local.set({ oldOptions }, - () => resolve() - ) - }) - - }) + browser.storage.local.set({ options }, + () => resolve() + ) + }) }) } function upgradeOptions() { - return new Promise(resolve => { - fetch("/config.json") - .then(response => response.text()) - .then(configData => { - browser.storage.local.get(["oldOptions", "options"], r => { - const oldOptions = r.oldOptions - let options = r.options - const config = JSON.parse(configData) + return new Promise(async resolve => { + const oldOptions = await utils.getOptions() + const config = await utils.getConfig() - options.exceptions = oldOptions.exceptions - options.theme = oldOptions.theme - options.popupServices = oldOptions.popupServices + let options = {} - for (const service in config.services) { - options[service] = oldOptions[service] - options[service].remove("embedFrontend") + options.exceptions = oldOptions.exceptions + options.theme = oldOptions.theme + options.popupServices = oldOptions.popupServices - for (const frontend in network.services[service].frontends) { - options[frontend] = [ - ...oldOptions[frontend].clearnet.enabled, - ...oldOptions[frontend].clearnet.custom - ] - } + for (const service in config.services) { + if (service in oldOptions) { + options[service] = oldOptions[service] + delete options[service].embedFrontend + } + else { + options[service] = {} + for (const defaultOption in config.services[service].options) { + options[service][defaultOption] = config.services[service].options[defaultOption] + } + for (const frontend in config.services[service].frontends) { + if (config.services[service].frontends[frontend].instanceList) { + options[frontend] = [] } - browser.storage.local.set({ options }, () => { - browser.storage.local.remove("oldOptions", () => { - resolve() - }) - }) - }) + } + } + + for (const frontend in config.services[service].frontends) { + if (config.services[service].frontends[frontend].instanceList) { + if (frontend in oldOptions) { + options[frontend] = [ + ...oldOptions[frontend].clearnet.enabled, + ...oldOptions[frontend].clearnet.custom + ] + } + else { + options[frontend] = [] + } + } + } + } + + browser.storage.local.clear(() => { + browser.storage.local.set({ options }, () => { + resolve() }) + }) }) } function processUpdate() { - return new Promise(resolve => { - fetch("/config.json") - .then(response => response.text()) - .then(configJson => { - let config = JSON.parse(configJson) - browser.storage.local.get(["options"], async r => { - let options = r.options - for (const service in config.services) { - if (!options[service]) options[service] = {} - for (const defaultOption in config.services[service].options) { - if (options[service][defaultOption] === undefined) { - options[service][defaultOption] = config.services[service].options[defaultOption] - } - } - } - browser.storage.local.set({ options }) - resolve() - }) - }) + return new Promise(async resolve => { + let config = await utils.getConfig() + let options = await utils.getOptions() + for (const service in config.services) { + if (!options[service]) options[service] = {} + for (const defaultOption in config.services[service].options) { + if (options[service][defaultOption] === undefined) { + options[service][defaultOption] = config.services[service].options[defaultOption] + } + } + + for (const frontend in config.services[service].frontends) { + if (options[frontend] === undefined && config.services[service].frontends[frontend].instanceList) { + options[frontend] = [] + } + else if (frontend in options && frontend in !config.services[service].frontends) { + delete options[frontend] + } + } + } + browser.storage.local.set({ options }, () => { + resolve() + }) }) } @@ -636,14 +631,69 @@ function modifyContentSecurityPolicy(details) { } } +function copyRaw(test, copyRawElement) { + return new Promise(resolve => { + browser.tabs.query({ active: true, currentWindow: true }, async tabs => { + let currTab = tabs[0] + if (currTab) { + let url + try { + url = new URL(currTab.url) + } catch { + resolve() + return + } + + const newUrl = await reverse(url) + + if (newUrl) { + resolve(newUrl) + if (test) return + navigator.clipboard.writeText(newUrl) + if (copyRawElement) { + const textElement = copyRawElement.getElementsByTagName("h4")[0] + const oldHtml = textElement.innerHTML + textElement.innerHTML = browser.i18n.getMessage("copied") + setTimeout(() => (textElement.innerHTML = oldHtml), 1000) + } + } + } + resolve() + }) + }) +} + +function switchInstance(test) { + return new Promise(resolve => { + browser.tabs.query({ active: true, currentWindow: true }, async tabs => { + let currTab = tabs[0] + if (currTab) { + let url + try { + url = new URL(currTab.url) + } catch { + resolve() + return + } + const newUrl = await _switchInstance(url) + + if (newUrl) { + if (!test) browser.tabs.update({ url: newUrl }) + resolve(true) + } else resolve() + } + }) + }) +} + export default { redirect, computeService, - switchInstance, reverse, initDefaults, upgradeOptions, - backupOptions, processUpdate, modifyContentSecurityPolicy, + copyRaw, + switchInstance } diff --git a/src/assets/javascripts/utils.js b/src/assets/javascripts/utils.js index 3a7bd839..c8a771d9 100644 --- a/src/assets/javascripts/utils.js +++ b/src/assets/javascripts/utils.js @@ -1,7 +1,5 @@ window.browser = window.browser || window.chrome -import servicesHelper from "./services.js" - function getRandomInstance(instances) { return instances[~~(instances.length * Math.random())] } @@ -15,59 +13,23 @@ function protocolHost(url) { return `${url.protocol}//${url.host}` } -function copyRaw(test, copyRawElement) { +function getConfig() { return new Promise(resolve => { - browser.tabs.query({ active: true, currentWindow: true }, async tabs => { - let currTab = tabs[0] - if (currTab) { - let url - try { - url = new URL(currTab.url) - } catch { - resolve() - return - } - - const newUrl = await servicesHelper.reverse(url) - - if (newUrl) { - resolve(newUrl) - if (test) return - navigator.clipboard.writeText(newUrl) - if (copyRawElement) { - const textElement = copyRawElement.getElementsByTagName("h4")[0] - const oldHtml = textElement.innerHTML - textElement.innerHTML = browser.i18n.getMessage("copied") - setTimeout(() => (textElement.innerHTML = oldHtml), 1000) - } - } - } - resolve() - }) + fetch("/config.json") + .then(response => response.text()) + .then(json => { + resolve(JSON.parse(json)) + return + }) }) } -function switchInstance(test) { - return new Promise(resolve => { - browser.tabs.query({ active: true, currentWindow: true }, async tabs => { - let currTab = tabs[0] - if (currTab) { - let url - try { - url = new URL(currTab.url) - } catch { - resolve() - return - } - const newUrl = await servicesHelper.switchInstance(url) - - if (newUrl) { - if (!test) browser.tabs.update({ url: newUrl }) - resolve(true) - } else resolve() - } +function getOptions() { + return new Promise(resolve => + browser.storage.local.get("options", r => { + resolve(r.options) }) - }) + ) } function getBlacklist() { @@ -102,9 +64,9 @@ function getList() { export default { getRandomInstance, protocolHost, - switchInstance, - copyRaw, getList, getBlacklist, camelCase, + getConfig, + getOptions } diff --git a/src/manifest.json b/src/manifest.json index a1b8e8da..1b5a9634 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "name": "__MSG_extensionName__", "description": "__MSG_extensionDescription__", - "version": "2.3.4", + "version": "3.0.0", "manifest_version": 2, "browser_specific_settings": { "gecko": { diff --git a/src/pages/background/background.js b/src/pages/background/background.js index 021ad4cc..5c6ec521 100644 --- a/src/pages/background/background.js +++ b/src/pages/background/background.js @@ -9,33 +9,17 @@ window.browser = window.browser || window.chrome browser.runtime.onInstalled.addListener(async details => { if (details.previousVersion != browser.runtime.getManifest().version) { // ^Used to prevent this running when debugging with auto-reload - browser.runtime.openOptionsPage() - switch (details.reason) { - case "install": - browser.storage.local.get("options", async r => { - if (!r.options) { - await generalHelper.initDefaults() - await servicesHelper.initDefaults() - } - }) - break - case "update": - switch (details.previousVersion) { - case "2.3.4": - browser.storage.local.get("options", async r => { - if (!r.options) { - await servicesHelper.backupOptions() - await generalHelper.initDefaults() - await servicesHelper.initDefaults() - await servicesHelper.upgradeOptions() - } - }) - break - default: - await servicesHelper.processUpdate() - } + if (details.reason == "install") { + if (!(await utils.getOptions())) { + await servicesHelper.initDefaults() + } + } + else if (details.reason == "update") { + await servicesHelper.upgradeOptions() + // await servicesHelper.processUpdate() } } + browser.runtime.openOptionsPage() }) let tabIdRedirects = {} @@ -86,8 +70,8 @@ browser.tabs.onRemoved.addListener(tabId => { }) browser.commands.onCommand.addListener(command => { - if (command === "switchInstance") utils.switchInstance() - else if (command == "copyRaw") utils.copyRaw() + if (command === "switchInstance") servicesHelper.switchInstance() + else if (command == "copyRaw") servicesHelper.copyRaw() }) browser.contextMenus.create({ @@ -134,7 +118,7 @@ browser.contextMenus.onClicked.addListener((info, tab) => { return new Promise(async resolve => { switch (info.menuItemId) { case "switchInstance": - utils.switchInstance() + servicesHelper.switchInstance() resolve() return case "settings": @@ -142,7 +126,7 @@ browser.contextMenus.onClicked.addListener((info, tab) => { resolve() return case "copyRaw": - utils.copyRaw() + servicesHelper.copyRaw() resolve() return case "toggleTab": @@ -155,13 +139,11 @@ browser.contextMenus.onClicked.addListener((info, tab) => { const url = new URL(tab.url) const service = await servicesHelper.computeService(url) if (service) { - browser.storage.local.get("options", async r => { - if (r.options[service].enabled) tabIdRedirects[tab.id] = false - else tabIdRedirects[tab.id] = true - await handleToggleTab(tab) - resolve() - return - }) + if ((await utils.getOptions())[service].enabled) tabIdRedirects[tab.id] = false + else tabIdRedirects[tab.id] = true + await handleToggleTab(tab) + resolve() + return } else { tabIdRedirects[tab.id] = false await handleToggleTab(tab) diff --git a/src/pages/options/index.js b/src/pages/options/index.js index 81cccb44..a7be0418 100644 --- a/src/pages/options/index.js +++ b/src/pages/options/index.js @@ -13,19 +13,9 @@ for (const a of document.getElementById("links").getElementsByTagName("a")) { }) } -await new Promise(resolve => { - fetch("/config.json").then(response => response.text()) - .then(data => { - config = JSON.parse(data) - resolve() - }) -}) -await new Promise(resolve => { - browser.storage.local.get("options", r => { - options = r.options - resolve() - }) -}) + +config = await utils.getConfig() +options = await utils.getOptions() function changeFrontendsSettings(service) { for (const frontend in config.services[service].frontends) { @@ -46,12 +36,15 @@ function loadPage(path) { for (const section of document.getElementById("pages").getElementsByTagName("section")) section.style.display = "none" document.getElementById(`${path}_page`).style.display = "block" - for (const a of document.getElementById("links").getElementsByTagName("a")) - if (a.getAttribute("href") == `#${path}`) a.classList.add("selected") - else a.classList.remove("selected") + for (const a of document.getElementById("links").getElementsByTagName("a")) { + if (a.getAttribute("href") == `#${path}`) { + a.classList.add("selected") + } else { + a.classList.remove("selected") + } + } - let stateObj = { id: "100" } - window.history.pushState(stateObj, "Page 2", `/pages/options/index.html#${path}`) + window.history.pushState({ id: "100" }, "Page 2", `/pages/options/index.html#${path}`) if (path != 'general' && path != 'about') { const service = path; @@ -62,16 +55,14 @@ function loadPage(path) { if (typeof config.services[service].options[option] == "boolean") divs[service][option].checked = options[service][option] else divs[service][option].value = options[service][option] - divs[service][option].addEventListener("change", () => { - browser.storage.local.get("options", r => { - let options = r.options - if (typeof config.services[service].options[option] == "boolean") - options[service][option] = divs[service][option].checked - else - options[service][option] = divs[service][option].value - browser.storage.local.set({ options }) - changeFrontendsSettings(service) - }) + divs[service][option].addEventListener("change", async () => { + let options = await utils.getOptions() + if (typeof config.services[service].options[option] == "boolean") + options[service][option] = divs[service][option].checked + else + options[service][option] = divs[service][option].value + browser.storage.local.set({ options }) + changeFrontendsSettings(service) }) } @@ -88,7 +79,7 @@ function loadPage(path) { for (const frontend in config.services[service].frontends) { if (config.services[service].frontends[frontend].instanceList) { - processDefaultCustomInstances(frontend, document) + processCustomInstances(frontend, document) } } @@ -104,18 +95,9 @@ function loadPage(path) { } } -async function processDefaultCustomInstances(frontend, document) { - let customInstances = [] - let options - await new Promise(async resolve => - browser.storage.local.get(["options"], r => { - customInstances = r.options[frontend] - options = r.options - resolve() - }) - ) - - localise.localisePage() +async function processCustomInstances(frontend, document) { + let options = await utils.getOptions() + let customInstances = options[frontend] function calcCustomInstances() { document.getElementById(frontend).getElementsByClassName("custom-checklist")[0].innerHTML = customInstances @@ -133,10 +115,12 @@ async function processDefaultCustomInstances(frontend, document) { ) .join("\n") + customInstances = options[frontend] for (const item of customInstances) { document.getElementById(frontend).getElementsByClassName(`clear-${item}`)[0].addEventListener("click", async () => { let index = customInstances.indexOf(item) if (index > -1) customInstances.splice(index, 1) + options = await utils.getOptions() options[frontend] = customInstances browser.storage.local.set({ options }, () => calcCustomInstances()) }) @@ -144,7 +128,6 @@ async function processDefaultCustomInstances(frontend, document) { } calcCustomInstances() document.getElementById(frontend).getElementsByClassName("custom-instance-form")[0].addEventListener("submit", async event => { - event.preventDefault(); event.preventDefault() let frontendCustomInstanceInput = document.getElementById(frontend).getElementsByClassName("custom-instance")[0] let url @@ -157,6 +140,7 @@ async function processDefaultCustomInstances(frontend, document) { if (frontendCustomInstanceInput.validity.valid) { if (!customInstances.includes(protocolHostVar)) { customInstances.push(protocolHostVar) + options = await utils.getOptions() options[frontend] = customInstances browser.storage.local.set({ options }, () => { frontendCustomInstanceInput.value = "" @@ -172,32 +156,32 @@ function createList(frontend, networks, document, redirects, blacklist) { if (redirects[frontend]) { if (redirects[frontend][network].length > 0) { document.getElementById(frontend).getElementsByClassName(network)[0].getElementsByClassName("checklist")[0].innerHTML = [ - ` -