Formatting wiht prettier

This commit is contained in:
ManeraKai 2024-07-21 21:22:09 +03:00
parent 8bf25954a3
commit cf8216da00
No known key found for this signature in database
GPG Key ID: 5ABC31FFD562E337
19 changed files with 3238 additions and 3270 deletions

3
.prettierignore Normal file
View File

@ -0,0 +1,3 @@
src/_locales/
.github/
.gitea/

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"printWidth": 120,
"semi": false,
"singleQuote": false,
"trailingComma": "es5",
"arrowParens": "avoid"
}

View File

@ -11,34 +11,42 @@ A browser extension that redirects YouTube, Twitter, TikTok... requests to alter
</a> </a>
## Translate ## Translate
<a href="https://hosted.weblate.org/projects/libredirect/extension"> <a href="https://hosted.weblate.org/projects/libredirect/extension">
<img src ="./img/weblate.svg"> <img src ="./img/weblate.svg">
</a> </a>
## Development ## Development
Install [Node.js](https://nodejs.org/) Install [Node.js](https://nodejs.org/)
```bash ```bash
git clone https://github.com/libredirect/browser_extension git clone https://github.com/libredirect/browser_extension
cd browser_extension cd browser_extension
npm install npm install
npm run html # Generates html using Pug npm run html # Generates html using Pug
``` ```
#### Run on Firefox #### Run on Firefox
```bash ```bash
npm run start npm run start
``` ```
#### Build a zip package for Firefox #### Build a zip package for Firefox
```bash ```bash
npm run build npm run build
``` ```
#### Install the zip package on Firefox (temporarily) #### Install the zip package on Firefox (temporarily)
1. Type in the address bar: `about:debugging#/runtime/this-firefox` 1. Type in the address bar: `about:debugging#/runtime/this-firefox`
2. Press `Load Temporary Add-on...` 2. Press `Load Temporary Add-on...`
3. Select `libredirect-VERSION.zip` from `web-ext-artifacts` folder 3. Select `libredirect-VERSION.zip` from `web-ext-artifacts` folder
#### Install the zip package on Firefox ESR, Developer Edition, Nightly #### Install the zip package on Firefox ESR, Developer Edition, Nightly
1. Type in the address bar: `about:config` 1. Type in the address bar: `about:config`
2. Set `xpinstall.signatures.required` to `false` 2. Set `xpinstall.signatures.required` to `false`
3. Type in the address bar: `about:addons` 3. Type in the address bar: `about:addons`
@ -46,6 +54,7 @@ npm run build
5. Select `libredirect-VERSION.zip` from `web-ext-artifacts` folder 5. Select `libredirect-VERSION.zip` from `web-ext-artifacts` folder
#### Run on Chromium #### Run on Chromium
1. Open `chrome://extensions` 1. Open `chrome://extensions`
2. Enable `dev mode` 2. Enable `dev mode`
3. Select `load unpacked extension` 3. Select `load unpacked extension`

View File

@ -1,39 +1,39 @@
{ {
"name": "libredirect", "name": "libredirect",
"description": "Redirects YouTube, Twitter, TikTok and more to privacy friendly frontends.", "description": "Redirects YouTube, Twitter, TikTok and more to privacy friendly frontends.",
"engines": { "engines": {
"node": ">=16.13.1", "node": ">=16.13.1",
"npm": ">=8.1.2" "npm": ">=8.1.2"
}, },
"scripts": { "scripts": {
"start": "web-ext run", "start": "web-ext run",
"nightly": "web-ext run --firefox=/home/esmail/software/firefox_nightly/firefox", "nightly": "web-ext run --firefox=/home/esmail/software/firefox_nightly/firefox",
"android": "web-ext run -t firefox-android --adb-device emulator-5554 --firefox-apk org.mozilla.fenix", "android": "web-ext run -t firefox-android --adb-device emulator-5554 --firefox-apk org.mozilla.fenix",
"build": "web-ext build", "build": "web-ext build",
"test": "web-ext lint", "test": "web-ext lint",
"html": "pug --basedir ./ --obj ./src/config.json src/pages/options/index.pug --out src/pages/options/ && pug --basedir ./ --obj ./src/config.json src/pages/popup/popup.pug --out src/pages/popup/" "html": "pug --basedir ./ --obj ./src/config.json src/pages/options/index.pug --out src/pages/options/ && pug --basedir ./ --obj ./src/config.json src/pages/popup/popup.pug --out src/pages/popup/"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/libredirect/libredirect.git" "url": "git+https://github.com/libredirect/libredirect.git"
}, },
"author": "LibRedirect", "author": "LibRedirect",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"bugs": { "bugs": {
"url": "https://github.com/libredirect/libredirect/issues" "url": "https://github.com/libredirect/libredirect/issues"
}, },
"homepage": "https://libredirect.github.io", "homepage": "https://libredirect.github.io",
"devDependencies": { "devDependencies": {
"pug-cli": "^1.0.0-alpha6", "pug-cli": "^1.0.0-alpha6",
"web-ext": "^7.2.0" "web-ext": "^7.2.0"
}, },
"webExt": { "webExt": {
"sourceDir": "./src/", "sourceDir": "./src/",
"run": { "run": {
"browserConsole": true "browserConsole": true
}, },
"build": { "build": {
"overwriteDest": true "overwriteDest": true
} }
} }
} }

View File

@ -1,34 +1,34 @@
window.browser = window.browser || window.chrome window.browser = window.browser || window.chrome
function localisePage() { function localisePage() {
/** /**
* @param {string} tag * @param {string} tag
*/ */
function getMessage(tag) { function getMessage(tag) {
return tag.replace(/__MSG_(\w+)__/g, (_match, v1) => { return tag.replace(/__MSG_(\w+)__/g, (_match, v1) => {
return v1 ? browser.i18n.getMessage(v1) : null return v1 ? browser.i18n.getMessage(v1) : null
}) })
} }
const elements = document.querySelectorAll("[data-localise]") const elements = document.querySelectorAll("[data-localise]")
for (let i in elements) for (let i in elements)
if (elements.hasOwnProperty(i)) { if (elements.hasOwnProperty(i)) {
const obj = elements[i] const obj = elements[i]
const tag = obj.getAttribute("data-localise").toString() const tag = obj.getAttribute("data-localise").toString()
const msg = getMessage(tag) const msg = getMessage(tag)
if (msg && msg !== tag) obj.textContent = msg if (msg && msg !== tag) obj.textContent = msg
} }
const placeholders = document.querySelectorAll("[data-localise-placeholder]") const placeholders = document.querySelectorAll("[data-localise-placeholder]")
for (let i in placeholders) for (let i in placeholders)
if (placeholders.hasOwnProperty(i)) { if (placeholders.hasOwnProperty(i)) {
const obj = placeholders[i] const obj = placeholders[i]
const tag = obj.getAttribute("data-localise-placeholder").toString() const tag = obj.getAttribute("data-localise-placeholder").toString()
const msg = getMessage(tag) const msg = getMessage(tag)
if (msg && msg !== tag) obj.placeholder = msg if (msg && msg !== tag) obj.placeholder = msg
} }
} }
export default { export default {
localisePage, localisePage,
} }

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ window.browser = window.browser || window.chrome
* @returns {T} * @returns {T}
*/ */
function getRandomInstance(instances) { function getRandomInstance(instances) {
return instances[~~(instances.length * Math.random())] return instances[~~(instances.length * Math.random())]
} }
/** /**
@ -14,27 +14,28 @@ function getRandomInstance(instances) {
* @returns {T} * @returns {T}
*/ */
function getNextInstance(currentInstanceUrl, instances) { function getNextInstance(currentInstanceUrl, instances) {
const currentInstanceIndex = instances.indexOf(currentInstanceUrl); const currentInstanceIndex = instances.indexOf(currentInstanceUrl)
if (currentInstanceIndex === -1) return getRandomInstance(instances); if (currentInstanceIndex === -1) return getRandomInstance(instances)
const nextInstanceIndex = (currentInstanceIndex + 1) % instances.length; const nextInstanceIndex = (currentInstanceIndex + 1) % instances.length
return instances[nextInstanceIndex]; return instances[nextInstanceIndex]
} }
/** /**
* @param {string} str * @param {string} str
*/ */
function camelCase(str) { function camelCase(str) {
return str.charAt(0).toUpperCase() + str.slice(1) return str.charAt(0).toUpperCase() + str.slice(1)
} }
/** /**
* @param {URL} url * @param {URL} url
*/ */
function protocolHost(url) { function protocolHost(url) {
if (url.username && url.password) return `${url.protocol}//${url.username}:${url.password}@${url.host}` if (url.username && url.password) return `${url.protocol}//${url.username}:${url.password}@${url.host}`
if (url.pathname == "/TekstoLibre/" && url.host.endsWith("github.io")) // workaround if (url.pathname == "/TekstoLibre/" && url.host.endsWith("github.io"))
return `${url.protocol}//${url.host}${url.pathname.slice(0, -1)}` // workaround
return `${url.protocol}//${url.host}` return `${url.protocol}//${url.host}${url.pathname.slice(0, -1)}`
return `${url.protocol}//${url.host}`
} }
/** /**
@ -59,14 +60,14 @@ function protocolHost(url) {
* @returns {Promise<Config>} * @returns {Promise<Config>}
*/ */
function getConfig() { function getConfig() {
return new Promise(resolve => { return new Promise(resolve => {
fetch("/config.json") fetch("/config.json")
.then(response => response.text()) .then(response => response.text())
.then(json => { .then(json => {
resolve(JSON.parse(json)) resolve(JSON.parse(json))
return return
}) })
}) })
} }
/** /**
@ -78,106 +79,108 @@ function getConfig() {
* @returns {Promise<Object.<string, Option | string[]>>} * @returns {Promise<Object.<string, Option | string[]>>}
*/ */
function getOptions() { function getOptions() {
return new Promise(resolve => browser.storage.local.get("options", r => resolve(r.options))) return new Promise(resolve => browser.storage.local.get("options", r => resolve(r.options)))
} }
function getPingCache() { function getPingCache() {
return new Promise(resolve => browser.storage.local.get("pingCache", r => resolve(r.pingCache ?? {}))) return new Promise(resolve => browser.storage.local.get("pingCache", r => resolve(r.pingCache ?? {})))
} }
function getBlacklist(options) { function getBlacklist(options) {
return new Promise(resolve => { return new Promise(resolve => {
let url let url
if (options.fetchInstances == 'github') url = 'https://raw.githubusercontent.com/libredirect/instances/main/blacklist.json' if (options.fetchInstances == "github")
else if (options.fetchInstances == 'codeberg') url = 'https://codeberg.org/LibRedirect/instances/raw/branch/main/blacklist.json' url = "https://raw.githubusercontent.com/libredirect/instances/main/blacklist.json"
else return resolve('disabled') else if (options.fetchInstances == "codeberg")
const http = new XMLHttpRequest() url = "https://codeberg.org/LibRedirect/instances/raw/branch/main/blacklist.json"
http.open("GET", url, true) else return resolve("disabled")
http.onreadystatechange = () => { const http = new XMLHttpRequest()
if (http.status === 200 && http.readyState == XMLHttpRequest.DONE) http.open("GET", url, true)
resolve(JSON.parse(http.responseText)) http.onreadystatechange = () => {
} if (http.status === 200 && http.readyState == XMLHttpRequest.DONE) resolve(JSON.parse(http.responseText))
http.onerror = () => resolve() }
http.ontimeout = () => resolve() http.onerror = () => resolve()
http.send(null) http.ontimeout = () => resolve()
}) http.send(null)
})
} }
function getList(options) { function getList(options) {
return new Promise(resolve => { return new Promise(resolve => {
let url let url
if (options.fetchInstances == 'github') url = 'https://raw.githubusercontent.com/libredirect/instances/main/data.json' if (options.fetchInstances == "github")
else if (options.fetchInstances == 'codeberg') url = 'https://codeberg.org/LibRedirect/instances/raw/branch/main/data.json' url = "https://raw.githubusercontent.com/libredirect/instances/main/data.json"
else return resolve('disabled') else if (options.fetchInstances == "codeberg")
const http = new XMLHttpRequest() url = "https://codeberg.org/LibRedirect/instances/raw/branch/main/data.json"
http.open("GET", url, true) else return resolve("disabled")
http.onreadystatechange = () => { const http = new XMLHttpRequest()
if (http.status === 200 && http.readyState == XMLHttpRequest.DONE) http.open("GET", url, true)
return resolve(JSON.parse(http.responseText)) http.onreadystatechange = () => {
} if (http.status === 200 && http.readyState == XMLHttpRequest.DONE) return resolve(JSON.parse(http.responseText))
http.onerror = () => resolve() }
http.ontimeout = () => resolve() http.onerror = () => resolve()
http.send(null) http.ontimeout = () => resolve()
}) http.send(null)
})
} }
/** /**
* @param {string} href * @param {string} href
*/ */
function pingOnce(href) { function pingOnce(href) {
return new Promise(async resolve => { return new Promise(async resolve => {
let started let started
let http = new XMLHttpRequest() let http = new XMLHttpRequest()
http.timeout = 5000 http.timeout = 5000
http.ontimeout = () => resolve(5000) http.ontimeout = () => resolve(5000)
http.onerror = () => resolve() http.onerror = () => resolve()
http.onreadystatechange = () => { http.onreadystatechange = () => {
if (http.readyState == 2) { if (http.readyState == 2) {
if (http.status == 200) { if (http.status == 200) {
let ended = new Date().getTime() let ended = new Date().getTime()
http.abort() http.abort()
resolve(ended - started) resolve(ended - started)
} else { } else {
resolve(5000 + http.status) resolve(5000 + http.status)
} }
} }
} }
http.open("GET", `${href}?_=${new Date().getTime()}`, true) http.open("GET", `${href}?_=${new Date().getTime()}`, true)
started = new Date().getTime() started = new Date().getTime()
http.send(null) http.send(null)
}) })
} }
/** /**
* @param {string} href * @param {string} href
*/ */
function ping(href) { function ping(href) {
return new Promise(async resolve => { return new Promise(async resolve => {
let average = 0 let average = 0
let time let time
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
time = await pingOnce(href) time = await pingOnce(href)
if (i == 0) continue if (i == 0) continue
if (time >= 5000) { if (time >= 5000) {
resolve(time) resolve(time)
return return
} }
average += time average += time
} }
average = parseInt(average / 3) average = parseInt(average / 3)
resolve(average) resolve(average)
}) })
} }
export default { export default {
getRandomInstance, getRandomInstance,
getNextInstance, getNextInstance,
protocolHost, protocolHost,
getList, getList,
getBlacklist, getBlacklist,
camelCase, camelCase,
getConfig, getConfig,
getOptions, getOptions,
getPingCache, getPingCache,
ping, ping,
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,91 +1,82 @@
{ {
"name": "__MSG_extensionName__", "name": "__MSG_extensionName__",
"description": "__MSG_extensionDescription__", "description": "__MSG_extensionDescription__",
"version": "2.8.5", "version": "2.8.5",
"manifest_version": 2, "manifest_version": 2,
"browser_specific_settings": { "browser_specific_settings": {
"gecko": { "gecko": {
"id": "7esoorv3@alefvanoon.anonaddy.me", "id": "7esoorv3@alefvanoon.anonaddy.me",
"strict_min_version": "89.0" "strict_min_version": "89.0"
}, },
"gecko_android": { "gecko_android": {
"strict_min_version": "113.0" "strict_min_version": "113.0"
} }
}, },
"background": { "background": {
"page": "pages/background/background.html", "page": "pages/background/background.html",
"persistent": true "persistent": true
}, },
"icons": { "icons": {
"16": "assets/images/libredirect-16.png", "16": "assets/images/libredirect-16.png",
"32": "assets/images/libredirect-32.png", "32": "assets/images/libredirect-32.png",
"48": "assets/images/libredirect-48.png", "48": "assets/images/libredirect-48.png",
"128": "assets/images/libredirect-128.png" "128": "assets/images/libredirect-128.png"
}, },
"permissions": [ "permissions": ["webRequest", "webRequestBlocking", "storage", "clipboardWrite", "contextMenus", "<all_urls>"],
"webRequest", "optional_permissions": ["bookmarks"],
"webRequestBlocking", "browser_action": {
"storage", "default_title": "__MSG_extensionName__",
"clipboardWrite", "browser_style": false,
"contextMenus", "default_popup": "pages/popup/popup.html",
"<all_urls>" "default_icon": {
], "16": "assets/images/libredirect-16.png",
"optional_permissions": [ "32": "assets/images/libredirect-32.png",
"bookmarks" "48": "assets/images/libredirect-48.png",
], "128": "assets/images/libredirect-128.png"
"browser_action": { }
"default_title": "__MSG_extensionName__", },
"browser_style": false, "options_ui": {
"default_popup": "pages/popup/popup.html", "page": "pages/options/index.html",
"default_icon": { "browser_style": false,
"16": "assets/images/libredirect-16.png", "open_in_tab": true
"32": "assets/images/libredirect-32.png", },
"48": "assets/images/libredirect-48.png", "chrome_settings_overrides": {
"128": "assets/images/libredirect-128.png" "search_provider": {
} "name": "__MSG_extensionName__",
}, "keyword": "@libredirect",
"options_ui": { "favicon_url": "https://raw.githubusercontent.com/libredirect/libredirect/master/src/assets/images/libredirect-16.png",
"page": "pages/options/index.html", "search_url": "https://search.libredirect.invalid/?q={searchTerms}",
"browser_style": false, "encoding": "UTF-8",
"open_in_tab": true "is_default": false
}, }
"chrome_settings_overrides": { },
"search_provider": { "commands": {
"name": "__MSG_extensionName__", "switchInstance": {
"keyword": "@libredirect", "suggested_key": {
"favicon_url": "https://raw.githubusercontent.com/libredirect/libredirect/master/src/assets/images/libredirect-16.png", "default": "Alt+Shift+L"
"search_url": "https://search.libredirect.invalid/?q={searchTerms}", },
"encoding": "UTF-8", "description": "__MSG_switchInstance__"
"is_default": false },
} "copyRaw": {
}, "suggested_key": {
"commands": { "default": "Alt+Shift+C"
"switchInstance": { },
"suggested_key": { "description": "Copies the original link. Ex: Copies the original twitter link while in the nitter website"
"default": "Alt+Shift+L" },
}, "reverse": {
"description": "__MSG_switchInstance__" "suggested_key": {
}, "default": "Alt+Shift+O"
"copyRaw": { },
"suggested_key": { "description": "Redirect to the original link. Ex: Redirects to the original twitter link while in the nitter website"
"default": "Alt+Shift+C" },
}, "redirect": {
"description": "Copies the original link. Ex: Copies the original twitter link while in the nitter website" "suggested_key": {
}, "default": "Alt+Shift+R"
"reverse": { },
"suggested_key": { "description": "Redirect link. Ex: Redirects original twitter link to nitter"
"default": "Alt+Shift+O" }
}, },
"description": "Redirect to the original link. Ex: Redirects to the original twitter link while in the nitter website" "default_locale": "en",
}, "update_url": "https://raw.githubusercontent.com/libredirect/libredirect/master/src/updates/updates.xml",
"redirect": { "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAroWDSoSRZ1scj+eJRrvnhJbrqXTKnhQuxs6+AJg16sqr0bsMdFV+MSY4i4xnK+K5WOYkBliWXgUyk/wzicoAjOnSJddrL/Md4FuWHI2NVIkrlsLOrYkygi5OLqGPajRH/w8Cdmg7KzEpXe/OnYV0/qS8li8huEdTzdeLdhfbiVl1j3DOr4OJALQ7mPeeNFHFo/oVQ+OkSezWLezA5jUGfhtzPYV6u1TXzX7lCi8E/BbDbwkvvXOMcjXCv08kjdLOY2djCA2a6zr0xAb3q8DlexAMZ8vMof7AQRFtBKhLc9n9VFoipMMdBOVQQj/eIcRILBrmkcZNnJxFKiHNJ+NcZQIDAQAB"
"suggested_key": {
"default": "Alt+Shift+R"
},
"description": "Redirect link. Ex: Redirects original twitter link to nitter"
}
},
"default_locale": "en",
"update_url": "https://raw.githubusercontent.com/libredirect/libredirect/master/src/updates/updates.xml",
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAroWDSoSRZ1scj+eJRrvnhJbrqXTKnhQuxs6+AJg16sqr0bsMdFV+MSY4i4xnK+K5WOYkBliWXgUyk/wzicoAjOnSJddrL/Md4FuWHI2NVIkrlsLOrYkygi5OLqGPajRH/w8Cdmg7KzEpXe/OnYV0/qS8li8huEdTzdeLdhfbiVl1j3DOr4OJALQ7mPeeNFHFo/oVQ+OkSezWLezA5jUGfhtzPYV6u1TXzX7lCi8E/BbDbwkvvXOMcjXCv08kjdLOY2djCA2a6zr0xAb3q8DlexAMZ8vMof7AQRFtBKhLc9n9VFoipMMdBOVQQj/eIcRILBrmkcZNnJxFKiHNJ+NcZQIDAQAB"
} }

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!doctype html>
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<script type="module" src="background.js"></script> <script type="module" src="background.js"></script>
</head> </head>
</html> </html>

View File

@ -7,310 +7,324 @@ const isChrome = browser.runtime.getBrowserInfo === undefined
window.browser = window.browser || window.chrome window.browser = window.browser || window.chrome
browser.runtime.onInstalled.addListener(async details => { browser.runtime.onInstalled.addListener(async details => {
if (details.previousVersion != browser.runtime.getManifest().version) { if (details.previousVersion != browser.runtime.getManifest().version) {
// ^Used to prevent this running when debugging with auto-reload // ^Used to prevent this running when debugging with auto-reload
if (details.reason == "install") { if (details.reason == "install") {
if (!(await utils.getOptions())) { if (!(await utils.getOptions())) {
await servicesHelper.initDefaults() await servicesHelper.initDefaults()
} }
browser.runtime.openOptionsPage() browser.runtime.openOptionsPage()
} } else if (details.reason == "update") {
else if (details.reason == "update") { if (details.previousVersion == "2.5.2") {
if (details.previousVersion == '2.5.2') { await servicesHelper.upgradeOptions()
await servicesHelper.upgradeOptions() await servicesHelper.processUpdate()
await servicesHelper.processUpdate() } else {
} else { await servicesHelper.processUpdate()
await servicesHelper.processUpdate() }
} }
} }
}
}) })
let tabIdRedirects = {} let tabIdRedirects = {}
// true == Always redirect, false == Never redirect, null/undefined == follow options for services // true == Always redirect, false == Never redirect, null/undefined == follow options for services
browser.webRequest.onBeforeRequest.addListener( browser.webRequest.onBeforeRequest.addListener(
details => { details => {
const url = new URL(details.url) const url = new URL(details.url)
const old_href = url.href const old_href = url.href
if (new RegExp(/^chrome-extension:\/{2}.*\/instances\/.*.json$/).test(url.href) && details.type == "xmlhttprequest") return if (new RegExp(/^chrome-extension:\/{2}.*\/instances\/.*.json$/).test(url.href) && details.type == "xmlhttprequest")
let initiator return
try { let initiator
if (details.originUrl) initiator = new URL(details.originUrl) try {
else if (details.initiator && details.initiator !== "null") initiator = new URL(details.initiator) if (details.originUrl) initiator = new URL(details.originUrl)
} catch { else if (details.initiator && details.initiator !== "null") initiator = new URL(details.initiator)
return null } catch {
} return null
if (tabIdRedirects[details.tabId] == false) return null }
let newUrl = servicesHelper.redirect(url, details.type, initiator, tabIdRedirects[details.tabId], details.incognito) if (tabIdRedirects[details.tabId] == false) return null
let newUrl = servicesHelper.redirect(url, details.type, initiator, tabIdRedirects[details.tabId], details.incognito)
if (details.frameAncestors && details.frameAncestors.length > 0 && servicesHelper.isException(new URL(details.frameAncestors[0].url))) newUrl = null if (
details.frameAncestors &&
details.frameAncestors.length > 0 &&
servicesHelper.isException(new URL(details.frameAncestors[0].url))
)
newUrl = null
if (servicesHelper.isException(url)) { if (servicesHelper.isException(url)) {
if (details.type == "main_frame") if (details.type == "main_frame") newUrl = "BYPASSTAB"
newUrl = "BYPASSTAB" else return null
else }
return null
}
if (!newUrl) { if (!newUrl) {
const match = url.href.match(/^https?:\/{2}.*\.libredirect\.invalid.*/) const match = url.href.match(/^https?:\/{2}.*\.libredirect\.invalid.*/)
if (match) { if (match) {
browser.tabs.update({ browser.tabs.update({
url: browser.runtime.getURL(`/pages/messages/no_instance.html`) url: browser.runtime.getURL(`/pages/messages/no_instance.html`),
}); })
} }
} }
if (newUrl) { if (newUrl) {
if (newUrl === "CANCEL") { if (newUrl === "CANCEL") {
console.log(`Canceled ${url}`) console.log(`Canceled ${url}`)
return { cancel: true } return { cancel: true }
} }
if (newUrl === "BYPASSTAB") { if (newUrl === "BYPASSTAB") {
console.log(`Bypassed ${details.tabId} ${url}`) console.log(`Bypassed ${details.tabId} ${url}`)
if (tabIdRedirects[details.tabId] != false) tabIdRedirects[details.tabId] = false if (tabIdRedirects[details.tabId] != false) tabIdRedirects[details.tabId] = false
return null return null
} }
console.log("Redirecting", old_href, "=>", newUrl) console.log("Redirecting", old_href, "=>", newUrl)
return { redirectUrl: newUrl } return { redirectUrl: newUrl }
} }
return null return null
}, },
{ urls: ["<all_urls>"] }, { urls: ["<all_urls>"] },
["blocking"] ["blocking"]
) )
browser.tabs.onRemoved.addListener(tabId => { browser.tabs.onRemoved.addListener(tabId => {
if (tabIdRedirects[tabId] != undefined) { if (tabIdRedirects[tabId] != undefined) {
delete tabIdRedirects[tabId] delete tabIdRedirects[tabId]
console.log(`Removed tab ${tabId} from tabIdRedirects`) console.log(`Removed tab ${tabId} from tabIdRedirects`)
} }
}) })
browser.commands.onCommand.addListener(async command => { browser.commands.onCommand.addListener(async command => {
browser.tabs.query({ active: true, currentWindow: true }, async tabs => { browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
const url = new URL(tabs[0].url) const url = new URL(tabs[0].url)
switch (command) { switch (command) {
case "switchInstance": { case "switchInstance": {
const newUrl = await servicesHelper.switchInstance(url) const newUrl = await servicesHelper.switchInstance(url)
if (newUrl) browser.tabs.update({ url: newUrl }) if (newUrl) browser.tabs.update({ url: newUrl })
break break
} }
case "copyRaw": case "copyRaw":
servicesHelper.copyRaw(url) servicesHelper.copyRaw(url)
break break
case "redirect": case "redirect":
browser.tabs.query({ active: true, currentWindow: true }, async tabs => { browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
if (tabs[0].url) { if (tabs[0].url) {
const url = new URL(tabs[0].url) const url = new URL(tabs[0].url)
const newUrl = servicesHelper.redirect(url, "main_frame", null, true) const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
if (newUrl) { if (newUrl) {
browser.tabs.update(tabs[0].id, { url: newUrl }, () => { browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
tabIdRedirects[tabs[0].id] = true tabIdRedirects[tabs[0].id] = true
}) })
} }
} }
}) })
break break
case "reverse": case "reverse":
browser.tabs.query({ active: true, currentWindow: true }, async tabs => { browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
if (tabs[0].url) { if (tabs[0].url) {
const url = new URL(tabs[0].url) const url = new URL(tabs[0].url)
const newUrl = await servicesHelper.reverse(url) const newUrl = await servicesHelper.reverse(url)
if (newUrl) { if (newUrl) {
browser.tabs.update(tabs[0].id, { url: newUrl }, () => { browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
tabIdRedirects[tabs[0].id] = false tabIdRedirects[tabs[0].id] = false
}) })
} }
} }
}) })
break break
} }
}) })
}) })
browser.contextMenus.create({ id: "settingsTab", title: browser.i18n.getMessage("settings"), contexts: ["browser_action"] }) browser.contextMenus.create({
browser.contextMenus.create({ id: "switchInstanceTab", title: browser.i18n.getMessage("switchInstance"), contexts: ["browser_action"] }) id: "settingsTab",
browser.contextMenus.create({ id: "copyReverseTab", title: 'Copy Original', contexts: ["browser_action"] }) title: browser.i18n.getMessage("settings"),
browser.contextMenus.create({ id: "redirectTab", title: 'Redirect', contexts: ["browser_action"] }) contexts: ["browser_action"],
browser.contextMenus.create({ id: "reverseTab", title: 'Redirect To Original', contexts: ["browser_action"] }) })
browser.contextMenus.create({
id: "switchInstanceTab",
title: browser.i18n.getMessage("switchInstance"),
contexts: ["browser_action"],
})
browser.contextMenus.create({ id: "copyReverseTab", title: "Copy Original", contexts: ["browser_action"] })
browser.contextMenus.create({ id: "redirectTab", title: "Redirect", contexts: ["browser_action"] })
browser.contextMenus.create({ id: "reverseTab", title: "Redirect To Original", contexts: ["browser_action"] })
browser.contextMenus.create({ id: "redirectLink", title: 'Redirect', contexts: ["link"] }) browser.contextMenus.create({ id: "redirectLink", title: "Redirect", contexts: ["link"] })
browser.contextMenus.create({ id: "redirectLinkInNewTab", title: 'Redirect In New Tab', contexts: ["link"] }) browser.contextMenus.create({ id: "redirectLinkInNewTab", title: "Redirect In New Tab", contexts: ["link"] })
browser.contextMenus.create({ id: "reverseLink", title: 'Redirect To Original', contexts: ["link"] }) browser.contextMenus.create({ id: "reverseLink", title: "Redirect To Original", contexts: ["link"] })
browser.contextMenus.create({ id: "reverseLinkInNewTab", title: 'Redirect To Original In New Tab', contexts: ["link"] }) browser.contextMenus.create({ id: "reverseLinkInNewTab", title: "Redirect To Original In New Tab", contexts: ["link"] })
browser.contextMenus.create({ id: "copyReverseLink", title: 'Copy Original', contexts: ["link"] }) browser.contextMenus.create({ id: "copyReverseLink", title: "Copy Original", contexts: ["link"] })
browser.contextMenus.create({ id: "bypassLink", title: 'Bypass', contexts: ["link"] }) browser.contextMenus.create({ id: "bypassLink", title: "Bypass", contexts: ["link"] })
browser.contextMenus.create({ id: "bypassLinkInNewTab", title: 'Bypass In New Tab', contexts: ["link"] }) browser.contextMenus.create({ id: "bypassLinkInNewTab", title: "Bypass In New Tab", contexts: ["link"] })
if (!isChrome) { if (!isChrome) {
browser.contextMenus.create({ id: "redirectBookmark", title: 'Redirect', contexts: ["bookmark"] }) browser.contextMenus.create({ id: "redirectBookmark", title: "Redirect", contexts: ["bookmark"] })
browser.contextMenus.create({ id: "redirectBookmarkInNewTab", title: 'Redirect In New Tab', contexts: ["bookmark"] }) browser.contextMenus.create({ id: "redirectBookmarkInNewTab", title: "Redirect In New Tab", contexts: ["bookmark"] })
browser.contextMenus.create({ id: "reverseBookmark", title: 'Redirect To Original', contexts: ["bookmark"] }) browser.contextMenus.create({ id: "reverseBookmark", title: "Redirect To Original", contexts: ["bookmark"] })
browser.contextMenus.create({ id: "reverseBookmarkInNewTab", title: 'Redirect To Original In New Tab', contexts: ["bookmark"] }) browser.contextMenus.create({
browser.contextMenus.create({ id: "copyReverseBookmark", title: 'Copy Original', contexts: ["bookmark"] }) id: "reverseBookmarkInNewTab",
browser.contextMenus.create({ id: "bypassBookmark", title: 'Bypass', contexts: ["bookmark"] }) title: "Redirect To Original In New Tab",
browser.contextMenus.create({ id: "bypassBookmarkInNewTab", title: 'Bypass In New Tab', contexts: ["bookmark"] }) contexts: ["bookmark"],
})
browser.contextMenus.create({ id: "copyReverseBookmark", title: "Copy Original", contexts: ["bookmark"] })
browser.contextMenus.create({ id: "bypassBookmark", title: "Bypass", contexts: ["bookmark"] })
browser.contextMenus.create({ id: "bypassBookmarkInNewTab", title: "Bypass In New Tab", contexts: ["bookmark"] })
} }
browser.contextMenus.onClicked.addListener(async (info) => { browser.contextMenus.onClicked.addListener(async info => {
switch (info.menuItemId) { switch (info.menuItemId) {
case 'switchInstanceTab': { case "switchInstanceTab": {
const url = new URL(info.pageUrl) const url = new URL(info.pageUrl)
const newUrl = await servicesHelper.switchInstance(url) const newUrl = await servicesHelper.switchInstance(url)
if (newUrl) browser.tabs.update({ url: newUrl }) if (newUrl) browser.tabs.update({ url: newUrl })
return return
} }
case 'settingsTab': case "settingsTab":
browser.runtime.openOptionsPage() browser.runtime.openOptionsPage()
return return
case 'copyReverseTab': case "copyReverseTab":
browser.tabs.query({ active: true, currentWindow: true }, async tabs => { browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
if (tabs[0].url) { if (tabs[0].url) {
const url = new URL(tabs[0].url) const url = new URL(tabs[0].url)
servicesHelper.copyRaw(url) servicesHelper.copyRaw(url)
} }
}) })
return return
case 'reverseTab': case "reverseTab":
browser.tabs.query({ active: true, currentWindow: true }, async tabs => { browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
if (tabs[0].url) { if (tabs[0].url) {
const url = new URL(tabs[0].url) const url = new URL(tabs[0].url)
const newUrl = await servicesHelper.reverse(url) const newUrl = await servicesHelper.reverse(url)
if (newUrl) { if (newUrl) {
browser.tabs.update(tabs[0].id, { url: newUrl }, () => { browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
tabIdRedirects[tabs[0].id] = false tabIdRedirects[tabs[0].id] = false
}) })
} }
} }
}) })
return return
case 'redirectTab': case "redirectTab":
browser.tabs.query({ active: true, currentWindow: true }, async tabs => { browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
if (tabs[0].url) { if (tabs[0].url) {
const url = new URL(tabs[0].url) const url = new URL(tabs[0].url)
const newUrl = servicesHelper.redirect(url, "main_frame", null, true) const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
if (newUrl) { if (newUrl) {
browser.tabs.update(tabs[0].id, { url: newUrl }, () => { browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
tabIdRedirects[tabs[0].id] = true tabIdRedirects[tabs[0].id] = true
}) })
} }
} }
}) })
return return
case 'copyReverseLink': { case "copyReverseLink": {
const url = new URL(info.linkUrl) const url = new URL(info.linkUrl)
await servicesHelper.copyRaw(url) await servicesHelper.copyRaw(url)
return return
} }
case 'redirectLink': case "redirectLink":
case 'redirectLinkInNewTab': { case "redirectLinkInNewTab": {
const url = new URL(info.linkUrl) const url = new URL(info.linkUrl)
const newUrl = servicesHelper.redirect(url, "main_frame", null, true) const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
if (newUrl) { if (newUrl) {
if (info.menuItemId == "redirectLink") browser.tabs.update({ url: newUrl }) if (info.menuItemId == "redirectLink") browser.tabs.update({ url: newUrl })
else browser.tabs.create({ url: newUrl }) else browser.tabs.create({ url: newUrl })
} }
return return
} }
case 'reverseLink': case "reverseLink":
case 'reverseLinkInNewTab': { case "reverseLinkInNewTab": {
const url = new URL(info.linkUrl) const url = new URL(info.linkUrl)
const newUrl = await servicesHelper.reverse(url) const newUrl = await servicesHelper.reverse(url)
if (newUrl) { if (newUrl) {
if (info.menuItemId == "reverseLink") { if (info.menuItemId == "reverseLink") {
browser.tabs.update({ url: newUrl }, tab => { browser.tabs.update({ url: newUrl }, tab => {
tabIdRedirects[tab.id] = false tabIdRedirects[tab.id] = false
}) })
} else { } else {
browser.tabs.create({ url: newUrl }, tab => { browser.tabs.create({ url: newUrl }, tab => {
tabIdRedirects[tab.id] = false tabIdRedirects[tab.id] = false
}) })
} }
} }
return return
} }
case 'bypassLink': case "bypassLink":
case 'bypassLinkInNewTab': { case "bypassLinkInNewTab": {
const url = new URL(info.linkUrl) const url = new URL(info.linkUrl)
if (info.menuItemId == "bypassLink") { if (info.menuItemId == "bypassLink") {
browser.tabs.update({ url: url.href }, tab => { browser.tabs.update({ url: url.href }, tab => {
tabIdRedirects[tab.id] = false tabIdRedirects[tab.id] = false
}) })
} else { } else {
browser.tabs.create({ url: url.href }, tab => { browser.tabs.create({ url: url.href }, tab => {
tabIdRedirects[tab.id] = false tabIdRedirects[tab.id] = false
}) })
} }
return return
} }
case 'copyReverseBookmark': case "copyReverseBookmark":
browser.bookmarks.get(info.bookmarkId, bookmarks => { browser.bookmarks.get(info.bookmarkId, bookmarks => {
const url = new URL(bookmarks[0].url) const url = new URL(bookmarks[0].url)
servicesHelper.copyRaw(url) servicesHelper.copyRaw(url)
}); })
return return
case 'redirectBookmark': case "redirectBookmark":
case 'redirectBookmarkInNewTab': case "redirectBookmarkInNewTab":
browser.bookmarks.get(info.bookmarkId, bookmarks => { browser.bookmarks.get(info.bookmarkId, bookmarks => {
const url = new URL(bookmarks[0].url) const url = new URL(bookmarks[0].url)
const newUrl = servicesHelper.redirect(url, "main_frame", null, true) const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
if (newUrl) { if (newUrl) {
if (info.menuItemId == 'redirectBookmark') browser.tabs.update({ url: newUrl }) if (info.menuItemId == "redirectBookmark") browser.tabs.update({ url: newUrl })
else browser.tabs.create({ url: newUrl }) else browser.tabs.create({ url: newUrl })
} }
}) })
return return
case 'reverseBookmark': case "reverseBookmark":
case 'reverseBookmarkInNewTab': case "reverseBookmarkInNewTab":
browser.bookmarks.get(info.bookmarkId, async bookmarks => { browser.bookmarks.get(info.bookmarkId, async bookmarks => {
const url = new URL(bookmarks[0].url) const url = new URL(bookmarks[0].url)
const newUrl = await servicesHelper.reverse(url) const newUrl = await servicesHelper.reverse(url)
if (newUrl) { if (newUrl) {
if (info.menuItemId == "reverseBookmark") { if (info.menuItemId == "reverseBookmark") {
browser.tabs.update({ url: newUrl }, tab => { browser.tabs.update({ url: newUrl }, tab => {
tabIdRedirects[tab.id] = false tabIdRedirects[tab.id] = false
}) })
} else { } else {
browser.tabs.create({ url: newUrl }, tab => { browser.tabs.create({ url: newUrl }, tab => {
tabIdRedirects[tab.id] = false tabIdRedirects[tab.id] = false
}) })
} }
} }
}) })
return return
case 'bypassBookmark': case "bypassBookmark":
case 'bypassBookmarkInNewTab': case "bypassBookmarkInNewTab":
browser.bookmarks.get(info.bookmarkId, async bookmarks => { browser.bookmarks.get(info.bookmarkId, async bookmarks => {
const url = new URL(bookmarks[0].url) const url = new URL(bookmarks[0].url)
if (info.menuItemId == "bypassBookmark") { if (info.menuItemId == "bypassBookmark") {
browser.tabs.update({ url: url.href }, tab => tabIdRedirects[tab.id] = false) browser.tabs.update({ url: url.href }, tab => (tabIdRedirects[tab.id] = false))
} else { } else {
browser.tabs.create({ url: url.href }, tab => tabIdRedirects[tab.id] = false) browser.tabs.create({ url: url.href }, tab => (tabIdRedirects[tab.id] = false))
} }
return return
}) })
} }
}) })
browser.runtime.onMessage.addListener((request, sender, sendResponse) => { browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request == "reverseTab") { if (request == "reverseTab") {
browser.tabs.query({ active: true, currentWindow: true }, async tabs => { browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
if (tabs[0].url) { if (tabs[0].url) {
const url = new URL(tabs[0].url) const url = new URL(tabs[0].url)
const newUrl = await servicesHelper.reverse(url) const newUrl = await servicesHelper.reverse(url)
if (newUrl) browser.tabs.update(tabs[0].id, { url: newUrl }, () => tabIdRedirects[tabs[0].id] = false) if (newUrl) browser.tabs.update(tabs[0].id, { url: newUrl }, () => (tabIdRedirects[tabs[0].id] = false))
} }
}) })
} } else if (request == "redirectTab") {
else if (request == "redirectTab") { browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
browser.tabs.query({ active: true, currentWindow: true }, async tabs => { if (tabs[0].url) {
if (tabs[0].url) { const url = new URL(tabs[0].url)
const url = new URL(tabs[0].url) const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
const newUrl = servicesHelper.redirect(url, "main_frame", null, true) if (newUrl) browser.tabs.update(tabs[0].id, { url: newUrl }, () => (tabIdRedirects[tabs[0].id] = true))
if (newUrl) browser.tabs.update(tabs[0].id, { url: newUrl }, () => tabIdRedirects[tabs[0].id] = true) }
} })
}) }
}
}) })

View File

@ -1,26 +1,24 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<head> <meta charset="UTF-8" />
<meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="../stylesheets/styles.css" rel="stylesheet" />
<link href="../stylesheets/styles.css" rel="stylesheet">
<title>No instances found</title> <title>No instances found</title>
<style> <style>
#body { #body {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100vh; height: 100vh;
} }
</style> </style>
</head> </head>
<body> <body>
<div id="body"> <div id="body">
<h1>You have no instance selected for this frontend</h1> <h1>You have no instance selected for this frontend</h1>
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,19 +1,19 @@
import utils from "../../assets/javascripts/utils.js" import utils from "../../assets/javascripts/utils.js"
let config, let config,
options, options,
blacklist, blacklist,
redirects, redirects,
divs = {} divs = {}
for (const a of document.getElementById("links").getElementsByTagName("a")) { for (const a of document.getElementById("links").getElementsByTagName("a")) {
if (!a.href.includes("https://")) { if (!a.href.includes("https://")) {
a.addEventListener("click", e => { a.addEventListener("click", e => {
const path = a.getAttribute("href").replace("#", "") const path = a.getAttribute("href").replace("#", "")
loadPage(path) loadPage(path)
e.preventDefault() e.preventDefault()
}) })
} }
} }
config = await utils.getConfig() config = await utils.getConfig()
@ -23,180 +23,180 @@ options = await utils.getOptions()
* @param {string} service * @param {string} service
*/ */
async function changeFrontendsSettings(service) { async function changeFrontendsSettings(service) {
options = await utils.getOptions() options = await utils.getOptions()
const opacityDiv = document.getElementById(`${service}-opacity`) const opacityDiv = document.getElementById(`${service}-opacity`)
if (document.getElementById(`${service}-enabled`).checked) { if (document.getElementById(`${service}-enabled`).checked) {
opacityDiv.style.pointerEvents = 'auto' opacityDiv.style.pointerEvents = "auto"
opacityDiv.style.opacity = 1 opacityDiv.style.opacity = 1
opacityDiv.style.userSelect = 'auto' opacityDiv.style.userSelect = "auto"
} else { } else {
opacityDiv.style.pointerEvents = 'none' opacityDiv.style.pointerEvents = "none"
opacityDiv.style.opacity = 0.4 opacityDiv.style.opacity = 0.4
opacityDiv.style.userSelect = 'none' opacityDiv.style.userSelect = "none"
} }
for (const frontend in config.services[service].frontends) { for (const frontend in config.services[service].frontends) {
if (config.services[service].frontends[frontend].instanceList) { if (config.services[service].frontends[frontend].instanceList) {
const frontendDiv = document.getElementById(frontend) const frontendDiv = document.getElementById(frontend)
if (typeof divs[service].frontend !== "undefined") { if (typeof divs[service].frontend !== "undefined") {
if ( if (
frontend == divs[service].frontend.value frontend == divs[service].frontend.value ||
|| (config.services[service].frontends[divs[service].frontend.value].desktopApp &&
(config.services[service].frontends[divs[service].frontend.value].desktopApp && divs[service].embedFrontend && frontend == divs[service].embedFrontend.value) divs[service].embedFrontend &&
) { frontend == divs[service].embedFrontend.value)
frontendDiv.style.display = "" ) {
if (config.services[service].frontends[frontend].localhost === true) { frontendDiv.style.display = ""
document.getElementById(`${service}-instance-div`).style.display = "" if (config.services[service].frontends[frontend].localhost === true) {
document.getElementById(`${service}-instance-div`).style.display = ""
if (options[service].instance == "localhost") { if (options[service].instance == "localhost") {
frontendDiv.style.display = "none" frontendDiv.style.display = "none"
} }
} else { } else {
document.getElementById(`${service}-instance-div`).style.display = "none" document.getElementById(`${service}-instance-div`).style.display = "none"
} }
} else { } else {
frontendDiv.style.display = "none" frontendDiv.style.display = "none"
} }
} }
} }
} }
if (document.getElementById(`${service}-redirectType`)) { if (document.getElementById(`${service}-redirectType`)) {
const frontend = options[service].frontend const frontend = options[service].frontend
if (config.services[service].frontends[frontend].embeddable) { if (config.services[service].frontends[frontend].embeddable) {
document.getElementById(`${service}-redirectType`).innerHTML = ` document.getElementById(`${service}-redirectType`).innerHTML = `
<option value="both" data-localise="__MSG_both__">both</options> <option value="both" data-localise="__MSG_both__">both</options>
<option value="sub_frame" data-localise="__MSG_onlyEmbedded__">Only Embedded</option> <option value="sub_frame" data-localise="__MSG_onlyEmbedded__">Only Embedded</option>
<option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option> <option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option>
` `
} } else if (
else if (config.services[service].frontends[frontend].desktopApp && Object.values(config.services[service].frontends).some(frontend => frontend.embeddable)) { config.services[service].frontends[frontend].desktopApp &&
document.getElementById(`${service}-redirectType`).innerHTML = ` Object.values(config.services[service].frontends).some(frontend => frontend.embeddable)
) {
document.getElementById(`${service}-redirectType`).innerHTML = `
<option value="both" data-localise="__MSG_both__">both</options> <option value="both" data-localise="__MSG_both__">both</options>
<option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option> <option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option>
` `
if (options[service].redirectType == "sub_frame") { if (options[service].redirectType == "sub_frame") {
options[service].redirectType = "main_frame" options[service].redirectType = "main_frame"
browser.storage.local.set({ options }) browser.storage.local.set({ options })
} }
} else { } else {
document.getElementById(`${service}-redirectType`).innerHTML = document.getElementById(`${service}-redirectType`).innerHTML =
'<option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option>' '<option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option>'
options[service].redirectType = "main_frame" options[service].redirectType = "main_frame"
browser.storage.local.set({ options }) browser.storage.local.set({ options })
} }
document.getElementById(`${service}-redirectType`).value = options[service].redirectType document.getElementById(`${service}-redirectType`).value = options[service].redirectType
if (config.services[service].frontends[frontend].desktopApp && options[service].redirectType != "main_frame") { if (config.services[service].frontends[frontend].desktopApp && options[service].redirectType != "main_frame") {
document.getElementById(`${service}-embedFrontend-div`).style.display = '' document.getElementById(`${service}-embedFrontend-div`).style.display = ""
document.getElementById(divs[service].embedFrontend.value).style.display = '' document.getElementById(divs[service].embedFrontend.value).style.display = ""
} } else if (
else if (config.services[service].frontends[frontend].desktopApp && options[service].redirectType == "main_frame") { config.services[service].frontends[frontend].desktopApp &&
document.getElementById(`${service}-embedFrontend-div`).style.display = 'none' options[service].redirectType == "main_frame"
document.getElementById(divs[service].embedFrontend.value).style.display = 'none' ) {
} else { document.getElementById(`${service}-embedFrontend-div`).style.display = "none"
document.getElementById(`${service}-embedFrontend-div`).style.display = 'none' document.getElementById(divs[service].embedFrontend.value).style.display = "none"
} } else {
} document.getElementById(`${service}-embedFrontend-div`).style.display = "none"
const frontend_name_element = document.getElementById(`${service}_page`).getElementsByClassName("frontend_name")[0] }
frontend_name_element.href = config.services[service].frontends[divs[service].frontend.value].url }
const frontend_name_element = document.getElementById(`${service}_page`).getElementsByClassName("frontend_name")[0]
frontend_name_element.href = config.services[service].frontends[divs[service].frontend.value].url
} }
/** /**
* @param {string} path * @param {string} path
*/ */
async function loadPage(path) { async function loadPage(path) {
options = await utils.getOptions() options = await utils.getOptions()
for (const section of document.getElementById("pages").getElementsByTagName("section")) section.style.display = "none" for (const section of document.getElementById("pages").getElementsByTagName("section")) section.style.display = "none"
document.getElementById(`${path}_page`).style.display = "block" document.getElementById(`${path}_page`).style.display = "block"
for (const element of document.getElementsByClassName("title")) { for (const element of document.getElementsByClassName("title")) {
const a = element.getElementsByTagName('a')[0] const a = element.getElementsByTagName("a")[0]
if (a.getAttribute("href") == `#${path}`) { if (a.getAttribute("href") == `#${path}`) {
element.classList.add("selected") element.classList.add("selected")
} else { } else {
element.classList.remove("selected") element.classList.remove("selected")
} }
} }
for (const service in config.services) { for (const service in config.services) {
if (options[service].enabled) { if (options[service].enabled) {
document.getElementById(`${service}-link`).style.opacity = 1 document.getElementById(`${service}-link`).style.opacity = 1
} else { } else {
document.getElementById(`${service}-link`).style.opacity = 0.4 document.getElementById(`${service}-link`).style.opacity = 0.4
} }
} }
window.history.pushState({ id: "100" }, "Page 2", `/pages/options/index.html#${path}`) window.history.pushState({ id: "100" }, "Page 2", `/pages/options/index.html#${path}`)
if (path != 'general') { if (path != "general") {
const service = path; const service = path
divs[service] = {} divs[service] = {}
for (const option in config.services[service].options) { for (const option in config.services[service].options) {
divs[service][option] = document.getElementById(`${service}-${option}`) divs[service][option] = document.getElementById(`${service}-${option}`)
if (typeof config.services[service].options[option] == "boolean") divs[service][option].checked = options[service][option] if (typeof config.services[service].options[option] == "boolean")
else divs[service][option].value = options[service][option] divs[service][option].checked = options[service][option]
divs[service][option].addEventListener("change", async () => { else divs[service][option].value = options[service][option]
let options = await utils.getOptions() divs[service][option].addEventListener("change", async () => {
if (typeof config.services[service].options[option] == "boolean") let options = await utils.getOptions()
options[service][option] = divs[service][option].checked if (typeof config.services[service].options[option] == "boolean")
else options[service][option] = divs[service][option].checked
options[service][option] = divs[service][option].value else options[service][option] = divs[service][option].value
browser.storage.local.set({ options }) browser.storage.local.set({ options })
changeFrontendsSettings(service) changeFrontendsSettings(service)
}) })
} }
changeFrontendsSettings(service) changeFrontendsSettings(service)
blacklist = await utils.getBlacklist(options) blacklist = await utils.getBlacklist(options)
redirects = await utils.getList(options) redirects = await utils.getList(options)
for (const frontend in config.services[service].frontends) { for (const frontend in config.services[service].frontends) {
if (config.services[service].frontends[frontend].instanceList) { if (config.services[service].frontends[frontend].instanceList) {
if (redirects == 'disabled' || blacklist == 'disabled') { if (redirects == "disabled" || blacklist == "disabled") {
document.getElementById(frontend).getElementsByClassName('clearnet')[0].style.display = 'none' document.getElementById(frontend).getElementsByClassName("clearnet")[0].style.display = "none"
document.getElementById(frontend).getElementsByClassName('ping')[0].style.display = 'none' document.getElementById(frontend).getElementsByClassName("ping")[0].style.display = "none"
} } else if (!redirects || !blacklist) {
else if (!redirects || !blacklist) { document
document.getElementById(frontend) .getElementById(frontend)
.getElementsByClassName('clearnet')[0] .getElementsByClassName("clearnet")[0]
.getElementsByClassName("checklist")[0] .getElementsByClassName("checklist")[0]
.getElementsByClassName('loading')[0] .getElementsByClassName("loading")[0].innerHTML = "Could not fetch instances."
.innerHTML = 'Could not fetch instances.' } else {
} createList(frontend)
else { }
createList(frontend) }
} }
}
}
for (const frontend in config.services[service].frontends) { for (const frontend in config.services[service].frontends) {
if (config.services[service].frontends[frontend].instanceList) { if (config.services[service].frontends[frontend].instanceList) {
processCustomInstances(frontend) processCustomInstances(frontend)
document.getElementById(`ping-${frontend}`).addEventListener("click", async () => { document.getElementById(`ping-${frontend}`).addEventListener("click", async () => {
document.getElementById(`ping-${frontend}`).getElementsByTagName('x')[0].innerHTML = "Pinging..." document.getElementById(`ping-${frontend}`).getElementsByTagName("x")[0].innerHTML = "Pinging..."
await ping(frontend) await ping(frontend)
document.getElementById(`ping-${frontend}`).getElementsByTagName('x')[0].innerHTML = "Ping instances" document.getElementById(`ping-${frontend}`).getElementsByTagName("x")[0].innerHTML = "Ping instances"
}) })
} }
} }
} }
} }
async function calcCustomInstances(frontend) { async function calcCustomInstances(frontend) {
let options = await utils.getOptions() let options = await utils.getOptions()
let customInstances = options[frontend] let customInstances = options[frontend]
const pingCache = await utils.getPingCache() const pingCache = await utils.getPingCache()
document.getElementById(frontend).getElementsByClassName("custom-checklist")[0].innerHTML = customInstances document.getElementById(frontend).getElementsByClassName("custom-checklist")[0].innerHTML = customInstances
.map( .map(x => {
x => { const time = pingCache[x]
const time = pingCache[x]; if (time) {
if (time) { var { color, text } = processTime(time)
var { color, text } = processTime(time); }
} const timeText = time ? `<span class="ping" style="color:${color};">${text}</span>` : ""
const timeText = time const custom = isCustomInstance(frontend, x) ? "" : `<span>custom</span>`
? `<span class="ping" style="color:${color};">${text}</span>` return `<div>
: "";
const custom = isCustomInstance(frontend, x) ? "" : `<span>custom</span>`
return `<div>
<x> <x>
<a href="${x}" target="_blank">${x}</a> <a href="${x}" target="_blank">${x}</a>
${timeText} ${timeText}
@ -209,87 +209,97 @@ async function calcCustomInstances(frontend) {
</button> </button>
</div> </div>
<hr>` <hr>`
}) })
.join("\n") .join("\n")
for (const item of customInstances) { for (const item of customInstances) {
document.getElementById(frontend).getElementsByClassName(`clear-${item}`)[0].addEventListener("click", async () => { document
const index = customInstances.indexOf(item) .getElementById(frontend)
if (index > -1) customInstances.splice(index, 1) .getElementsByClassName(`clear-${item}`)[0]
options = await utils.getOptions() .addEventListener("click", async () => {
options[frontend] = customInstances const index = customInstances.indexOf(item)
browser.storage.local.set({ options }, async () => { if (index > -1) customInstances.splice(index, 1)
calcCustomInstances(frontend) options = await utils.getOptions()
createList(frontend) options[frontend] = customInstances
}) browser.storage.local.set({ options }, async () => {
}) calcCustomInstances(frontend)
} createList(frontend)
})
})
}
} }
async function processCustomInstances(frontend) { async function processCustomInstances(frontend) {
calcCustomInstances(frontend) calcCustomInstances(frontend)
document.getElementById(frontend).getElementsByClassName("custom-instance-form")[0].addEventListener("submit", async event => { document
event.preventDefault() .getElementById(frontend)
let options = await utils.getOptions() .getElementsByClassName("custom-instance-form")[0]
let customInstances = options[frontend] .addEventListener("submit", async event => {
let frontendCustomInstanceInput = document.getElementById(frontend).getElementsByClassName("custom-instance")[0] event.preventDefault()
try { let options = await utils.getOptions()
var url = new URL(frontendCustomInstanceInput.value) let customInstances = options[frontend]
} catch (error) { let frontendCustomInstanceInput = document.getElementById(frontend).getElementsByClassName("custom-instance")[0]
return try {
} var url = new URL(frontendCustomInstanceInput.value)
let protocolHostVar = utils.protocolHost(url) } catch (error) {
if (frontendCustomInstanceInput.validity.valid) { return
if (!customInstances.includes(protocolHostVar)) { }
customInstances.push(protocolHostVar) let protocolHostVar = utils.protocolHost(url)
options = await utils.getOptions() if (frontendCustomInstanceInput.validity.valid) {
options[frontend] = customInstances if (!customInstances.includes(protocolHostVar)) {
browser.storage.local.set({ options }, () => { customInstances.push(protocolHostVar)
frontendCustomInstanceInput.value = "" options = await utils.getOptions()
calcCustomInstances(frontend) options[frontend] = customInstances
}) browser.storage.local.set({ options }, () => {
} frontendCustomInstanceInput.value = ""
} calcCustomInstances(frontend)
}) })
}
}
})
} }
/** /**
* @param {string} frontend * @param {string} frontend
*/ */
async function createList(frontend) { async function createList(frontend) {
const pingCache = await utils.getPingCache() const pingCache = await utils.getPingCache()
const options = await utils.getOptions() const options = await utils.getOptions()
for (const network in config.networks) { for (const network in config.networks) {
const checklist = document.getElementById(frontend).getElementsByClassName(network)[0].getElementsByClassName("checklist")[0] const checklist = document
.getElementById(frontend)
.getElementsByClassName(network)[0]
.getElementsByClassName("checklist")[0]
if (!redirects[frontend]) { if (!redirects[frontend]) {
checklist.innerHTML = '<div class="block block-option">No instances found.</div>' checklist.innerHTML = '<div class="block block-option">No instances found.</div>'
break break
} }
const instances = redirects[frontend][network] const instances = redirects[frontend][network]
if (!instances || instances.length === 0) continue if (!instances || instances.length === 0) continue
document.getElementById(frontend).getElementsByClassName("custom-instance")[0].placeholder = redirects[frontend].clearnet[0] document.getElementById(frontend).getElementsByClassName("custom-instance")[0].placeholder =
redirects[frontend].clearnet[0]
instances.sort((a, b) => blacklist.cloudflare.includes(a) && !blacklist.cloudflare.includes(b)) instances.sort((a, b) => blacklist.cloudflare.includes(a) && !blacklist.cloudflare.includes(b))
const content = instances const content = instances.map(x => {
.map(x => { const cloudflare = blacklist.cloudflare.includes(x)
const cloudflare = blacklist.cloudflare.includes(x) ? ? `<a target="_blank" href="https://libredirect.github.io/docs.html#instances">
`<a target="_blank" href="https://libredirect.github.io/docs.html#instances">
<span style="color:red;">cloudflare</span> <span style="color:red;">cloudflare</span>
</a>` : "" </a>`
: ""
let time = pingCache[x] let time = pingCache[x]
let timeText = "" let timeText = ""
if (time) { if (time) {
const { color, text } = processTime(time) const { color, text } = processTime(time)
timeText = `<span class="ping" style="color:${color};">${text}</span>` timeText = `<span class="ping" style="color:${color};">${text}</span>`
} }
const chosen = options[frontend].includes(x) ? `<span style="color:grey;">chosen</span>` : "" const chosen = options[frontend].includes(x) ? `<span style="color:grey;">chosen</span>` : ""
const warnings = [cloudflare, timeText, chosen].join(" ") const warnings = [cloudflare, timeText, chosen].join(" ")
return `<div class="frontend"> return `<div class="frontend">
<x> <x>
<a href="${x}" target="_blank">${x}</a> <a href="${x}" target="_blank">${x}</a>
${warnings} ${warnings}
@ -300,30 +310,29 @@ async function createList(frontend) {
</svg> </svg>
</button> </button>
</div>` </div>`
}) })
checklist.innerHTML = [ checklist.innerHTML = [
`<div class="block block-option"> `<div class="block block-option">
<label>${utils.camelCase(network)}</label> <label>${utils.camelCase(network)}</label>
</div>`, </div>`,
...content, ...content,
"<br>" "<br>",
].join("\n<hr>\n") ].join("\n<hr>\n")
for (const instance of instances) { for (const instance of instances) {
checklist.getElementsByClassName(`add-${instance}`)[0] checklist.getElementsByClassName(`add-${instance}`)[0].addEventListener("click", async () => {
.addEventListener("click", async () => { let options = await utils.getOptions()
let options = await utils.getOptions() if (!options[frontend].includes(instance)) {
if (!options[frontend].includes(instance)) { options[frontend].push(instance)
options[frontend].push(instance) browser.storage.local.set({ options }, () => {
browser.storage.local.set({ options }, () => { calcCustomInstances(frontend)
calcCustomInstances(frontend) createList(frontend)
createList(frontend) })
}) }
} })
}) }
} }
}
} }
const r = window.location.href.match(/#(.*)/) const r = window.location.href.match(/#(.*)/)
@ -334,59 +343,57 @@ else loadPage("general")
* @param {string} frontend * @param {string} frontend
*/ */
async function ping(frontend) { async function ping(frontend) {
const instanceElements = [ const instanceElements = [
...document.getElementById(frontend).getElementsByClassName("custom-checklist")[0].getElementsByTagName('x'), ...document.getElementById(frontend).getElementsByClassName("custom-checklist")[0].getElementsByTagName("x"),
...document.getElementById(frontend).getElementsByClassName('clearnet')[0].getElementsByTagName('x') ...document.getElementById(frontend).getElementsByClassName("clearnet")[0].getElementsByTagName("x"),
] ]
let pingCache = await utils.getPingCache() let pingCache = await utils.getPingCache()
let redundancyList = {} let redundancyList = {}
for (const element of instanceElements) { for (const element of instanceElements) {
let span = element.getElementsByClassName('ping')[0] let span = element.getElementsByClassName("ping")[0]
if (!span) span = document.createElement('span') if (!span) span = document.createElement("span")
span.classList = ['ping'] span.classList = ["ping"]
span.innerHTML = '<span style="color:lightblue">pinging...</span>' span.innerHTML = '<span style="color:lightblue">pinging...</span>'
element.appendChild(span) element.appendChild(span)
const href = element.getElementsByTagName('a')[0].href const href = element.getElementsByTagName("a")[0].href
const innerHTML = element.getElementsByTagName('a')[0].innerHTML const innerHTML = element.getElementsByTagName("a")[0].innerHTML
const time = redundancyList[innerHTML] ?? await utils.ping(href) const time = redundancyList[innerHTML] ?? (await utils.ping(href))
const { color, text } = processTime(time) const { color, text } = processTime(time)
span.innerHTML = `<span style="color:${color};">${text}</span>` span.innerHTML = `<span style="color:${color};">${text}</span>`
pingCache[innerHTML] = time pingCache[innerHTML] = time
redundancyList[innerHTML] = time redundancyList[innerHTML] = time
browser.storage.local.set({ pingCache }) browser.storage.local.set({ pingCache })
} }
} }
/** /**
* @param {number} time * @param {number} time
*/ */
function processTime(time) { function processTime(time) {
let text let text
let color let color
if (time < 5000) { if (time < 5000) {
text = `${time}ms` text = `${time}ms`
if (time <= 1000) color = "green" if (time <= 1000) color = "green"
else if (time <= 2000) color = "orange" else if (time <= 2000) color = "orange"
} } else if (time >= 5000) {
else if (time >= 5000) { color = "red"
color = "red" if (time == 5000) text = "5000ms+"
if (time == 5000) text = "5000ms+" if (time > 5000) text = `Error: ${time - 5000}`
if (time > 5000) text = `Error: ${time - 5000}` } else {
} color = "red"
else { text = "Server not found"
color = "red" }
text = 'Server not found' return { color, text }
}
return { color, text }
} }
function isCustomInstance(frontend, instance) { function isCustomInstance(frontend, instance) {
for (const network in config.networks) { for (const network in config.networks) {
if (!redirects[frontend]) return false; if (!redirects[frontend]) return false
const instances = redirects[frontend][network] const instances = redirects[frontend][network]
if (instances.includes(instance)) return true if (instances.includes(instance)) return true
} }
return false return false
} }

View File

@ -5,46 +5,46 @@ import utils from "../../assets/javascripts/utils.js"
import servicesHelper from "../../assets/javascripts/services.js" import servicesHelper from "../../assets/javascripts/services.js"
if (!(await utils.getOptions())) { if (!(await utils.getOptions())) {
await servicesHelper.initDefaults() await servicesHelper.initDefaults()
} }
async function changeTheme() { async function changeTheme() {
switch ((await utils.getOptions()).theme) { switch ((await utils.getOptions()).theme) {
case "dark": case "dark":
document.body.classList.add("dark-theme") document.body.classList.add("dark-theme")
document.body.classList.remove("light-theme") document.body.classList.remove("light-theme")
for (const element of document.body.getElementsByClassName('dark')) { for (const element of document.body.getElementsByClassName("dark")) {
element.style.display = 'none'; element.style.display = "none"
} }
break break
case "light": case "light":
document.body.classList.add("light-theme") document.body.classList.add("light-theme")
document.body.classList.remove("dark-theme") document.body.classList.remove("dark-theme")
for (const element of document.body.getElementsByClassName('light')) { for (const element of document.body.getElementsByClassName("light")) {
element.style.display = 'none'; element.style.display = "none"
} }
break break
default: default:
if (matchMedia("(prefers-color-scheme: light)").matches) { if (matchMedia("(prefers-color-scheme: light)").matches) {
document.body.classList.add("light-theme") document.body.classList.add("light-theme")
document.body.classList.remove("dark-theme") document.body.classList.remove("dark-theme")
for (const element of document.body.getElementsByClassName('light')) { for (const element of document.body.getElementsByClassName("light")) {
element.style.display = 'none'; element.style.display = "none"
} }
} else { } else {
document.body.classList.add("dark-theme") document.body.classList.add("dark-theme")
document.body.classList.remove("light-theme") document.body.classList.remove("light-theme")
for (const element of document.body.getElementsByClassName('dark')) { for (const element of document.body.getElementsByClassName("dark")) {
element.style.display = 'none'; element.style.display = "none"
} }
} }
} }
} }
changeTheme() changeTheme()
if (["ar", "iw", "ku", "fa", "ur"].includes(browser.i18n.getUILanguage())) { if (["ar", "iw", "ku", "fa", "ur"].includes(browser.i18n.getUILanguage())) {
document.getElementsByTagName("body")[0].classList.add("rtl") document.getElementsByTagName("body")[0].classList.add("rtl")
document.getElementsByTagName("body")[0].dir = "rtl" document.getElementsByTagName("body")[0].dir = "rtl"
} }
localise.localisePage() localise.localisePage()

View File

@ -7,29 +7,29 @@ import servicesHelper from "../../../assets/javascripts/services.js"
const isChrome = browser.runtime.getBrowserInfo === undefined const isChrome = browser.runtime.getBrowserInfo === undefined
async function setOption(option, type, event) { async function setOption(option, type, event) {
let options = await utils.getOptions() let options = await utils.getOptions()
switch (type) { switch (type) {
case "select": case "select":
options[option] = event.target.options[event.target.options.selectedIndex].value options[option] = event.target.options[event.target.options.selectedIndex].value
break; break
case "checkbox": case "checkbox":
options[option] = event.target.checked options[option] = event.target.checked
break; break
case "range": case "range":
options[option] = event.target.value options[option] = event.target.value
break; break
} }
browser.storage.local.set({ options }) browser.storage.local.set({ options })
} }
const exportSettingsElement = document.getElementById("export-settings") const exportSettingsElement = document.getElementById("export-settings")
async function exportSettings() { async function exportSettings() {
const options = await utils.getOptions() const options = await utils.getOptions()
options.version = browser.runtime.getManifest().version options.version = browser.runtime.getManifest().version
let resultString = JSON.stringify(options, null, " ") let resultString = JSON.stringify(options, null, " ")
exportSettingsElement.href = "data:application/json;base64," + btoa(resultString) exportSettingsElement.href = "data:application/json;base64," + btoa(resultString)
exportSettingsElement.download = `libredirect-settings-v${options.version}.json` exportSettingsElement.download = `libredirect-settings-v${options.version}.json`
return return
} }
exportSettings() exportSettings()
document.getElementById("general_page").onclick = exportSettings document.getElementById("general_page").onclick = exportSettings
@ -37,35 +37,32 @@ document.getElementById("general_page").onclick = exportSettings
const importSettingsElement = document.getElementById("import-settings") const importSettingsElement = document.getElementById("import-settings")
const importSettingsElementText = document.getElementById("import_settings_text") const importSettingsElementText = document.getElementById("import_settings_text")
importSettingsElement.addEventListener("change", () => { importSettingsElement.addEventListener("change", () => {
function importError() { function importError() {
const oldHTML = importSettingsElementText.innerHTML const oldHTML = importSettingsElementText.innerHTML
importSettingsElementText.innerHTML = '<span style="color:red;">Error!</span>' importSettingsElementText.innerHTML = '<span style="color:red;">Error!</span>'
setTimeout(() => (importSettingsElementText.innerHTML = oldHTML), 1000) setTimeout(() => (importSettingsElementText.innerHTML = oldHTML), 1000)
} }
importSettingsElementText.innerHTML = "..." importSettingsElementText.innerHTML = "..."
let file = importSettingsElement.files[0] let file = importSettingsElement.files[0]
const reader = new FileReader() const reader = new FileReader()
reader.readAsText(file) reader.readAsText(file)
reader.onload = async () => { reader.onload = async () => {
const data = JSON.parse(reader.result) const data = JSON.parse(reader.result)
if ( if ("theme" in data && data.version == browser.runtime.getManifest().version) {
"theme" in data browser.storage.local.clear(async () => {
&& data.version == browser.runtime.getManifest().version browser.storage.local.set({ options: data }, () => {
) { location.reload()
browser.storage.local.clear(async () => { })
browser.storage.local.set({ options: data }, () => { })
location.reload() } else {
}) console.log("incompatible settings")
}) importError()
} else { }
console.log("incompatible settings") }
importError() reader.onerror = error => {
} console.log("error", error)
} importError()
reader.onerror = error => { }
console.log("error", error)
importError()
}
}) })
const exportSettingsSync = document.getElementById("export-settings-sync") const exportSettingsSync = document.getElementById("export-settings-sync")
@ -73,58 +70,57 @@ const importSettingsSync = document.getElementById("import-settings-sync")
const importSettingsSyncText = document.getElementById("import_settings_sync_text") const importSettingsSyncText = document.getElementById("import_settings_sync_text")
exportSettingsSync.addEventListener("click", async () => { exportSettingsSync.addEventListener("click", async () => {
let options = await utils.getOptions() let options = await utils.getOptions()
options.version = browser.runtime.getManifest().version options.version = browser.runtime.getManifest().version
browser.storage.sync.set({ options }, () => location.reload()) browser.storage.sync.set({ options }, () => location.reload())
}) })
importSettingsSync.addEventListener("click", () => { importSettingsSync.addEventListener("click", () => {
function importError() { function importError() {
importSettingsSyncText.innerHTML = '<span style="color:red;">Error!</span>' importSettingsSyncText.innerHTML = '<span style="color:red;">Error!</span>'
setTimeout(() => (importSettingsSyncText.innerHTML = oldHTML), 1000) setTimeout(() => (importSettingsSyncText.innerHTML = oldHTML), 1000)
} }
const oldHTML = importSettingsSyncText.innerHTML const oldHTML = importSettingsSyncText.innerHTML
importSettingsSyncText.innerHTML = "..." importSettingsSyncText.innerHTML = "..."
browser.storage.sync.get({ options }, r => { browser.storage.sync.get({ options }, r => {
const options = r.options const options = r.options
if (options.version == browser.runtime.getManifest().version) { if (options.version == browser.runtime.getManifest().version) {
browser.storage.local.set({ options }, () => location.reload()) browser.storage.local.set({ options }, () => location.reload())
} else { } else {
importError() importError()
} }
}) })
}) })
const resetSettings = document.getElementById("reset-settings") const resetSettings = document.getElementById("reset-settings")
resetSettings.addEventListener("click", async () => { resetSettings.addEventListener("click", async () => {
resetSettings.innerHTML = "..." resetSettings.innerHTML = "..."
await servicesHelper.initDefaults() await servicesHelper.initDefaults()
location.reload() location.reload()
}) })
const fetchInstancesElement = document.getElementById('fetch-instances') const fetchInstancesElement = document.getElementById("fetch-instances")
fetchInstancesElement.addEventListener('change', event => { fetchInstancesElement.addEventListener("change", event => {
setOption('fetchInstances', 'select', event) setOption("fetchInstances", "select", event)
location.reload() location.reload()
}) })
const redirectOnlyInIncognitoElement = document.getElementById('redirectOnlyInIncognito') const redirectOnlyInIncognitoElement = document.getElementById("redirectOnlyInIncognito")
redirectOnlyInIncognitoElement.addEventListener('change', event => { redirectOnlyInIncognitoElement.addEventListener("change", event => {
setOption('redirectOnlyInIncognito', 'checkbox', event) setOption("redirectOnlyInIncognito", "checkbox", event)
}) })
const bookmarksMenuElement = document.getElementById('bookmarksMenu') const bookmarksMenuElement = document.getElementById("bookmarksMenu")
bookmarksMenuElement.addEventListener('change', async event => { bookmarksMenuElement.addEventListener("change", async event => {
if (event.target.checked) if (event.target.checked)
browser.permissions.request({ permissions: ["bookmarks"] }, r => bookmarksMenuElement.checked = r) browser.permissions.request({ permissions: ["bookmarks"] }, r => (bookmarksMenuElement.checked = r))
else else browser.permissions.remove({ permissions: ["bookmarks"] }, r => (bookmarksMenuElement.checked = !r))
browser.permissions.remove({ permissions: ["bookmarks"] }, r => bookmarksMenuElement.checked = !r)
}) })
let themeElement = document.getElementById("theme") let themeElement = document.getElementById("theme")
themeElement.addEventListener("change", event => { themeElement.addEventListener("change", event => {
setOption("theme", "select", event) setOption("theme", "select", event)
location.reload() location.reload()
}) })
let nameCustomInstanceInput = document.getElementById("exceptions-custom-instance") let nameCustomInstanceInput = document.getElementById("exceptions-custom-instance")
@ -134,40 +130,44 @@ let instanceType = "url"
let config = await utils.getConfig() let config = await utils.getConfig()
for (const service in config.services) { for (const service in config.services) {
document.getElementById(service).addEventListener("change", async event => { document.getElementById(service).addEventListener("change", async event => {
let options = await utils.getOptions() let options = await utils.getOptions()
if (event.target.checked && !options.popupServices.includes(service)) options.popupServices.push(service) if (event.target.checked && !options.popupServices.includes(service)) options.popupServices.push(service)
else if (options.popupServices.includes(service)) { else if (options.popupServices.includes(service)) {
var index = options.popupServices.indexOf(service) var index = options.popupServices.indexOf(service)
if (index !== -1) options.popupServices.splice(index, 1) if (index !== -1) options.popupServices.splice(index, 1)
} }
browser.storage.local.set({ options }) browser.storage.local.set({ options })
}) })
} }
let options = await utils.getOptions() let options = await utils.getOptions()
themeElement.value = options.theme themeElement.value = options.theme
fetchInstancesElement.value = options.fetchInstances fetchInstancesElement.value = options.fetchInstances
redirectOnlyInIncognitoElement.checked = options.redirectOnlyInIncognito redirectOnlyInIncognitoElement.checked = options.redirectOnlyInIncognito
browser.permissions.contains({ permissions: ["bookmarks"] }, r => bookmarksMenuElement.checked = r) browser.permissions.contains({ permissions: ["bookmarks"] }, r => (bookmarksMenuElement.checked = r))
for (const service in config.services) document.getElementById(service).checked = options.popupServices.includes(service) for (const service in config.services)
document.getElementById(service).checked = options.popupServices.includes(service)
instanceTypeElement.addEventListener("change", event => { instanceTypeElement.addEventListener("change", event => {
instanceType = event.target.options[instanceTypeElement.selectedIndex].value instanceType = event.target.options[instanceTypeElement.selectedIndex].value
if (instanceType == "url") { if (instanceType == "url") {
nameCustomInstanceInput.setAttribute("type", "url") nameCustomInstanceInput.setAttribute("type", "url")
nameCustomInstanceInput.setAttribute("placeholder", "https://www.google.com") nameCustomInstanceInput.setAttribute("placeholder", "https://www.google.com")
} else if (instanceType == "regex") { } else if (instanceType == "regex") {
nameCustomInstanceInput.setAttribute("type", "text") nameCustomInstanceInput.setAttribute("type", "text")
nameCustomInstanceInput.setAttribute("placeholder", "https?://(www.|)youtube.com/") nameCustomInstanceInput.setAttribute("placeholder", "https?://(www.|)youtube.com/")
} }
}) })
let exceptionsCustomInstances = options.exceptions let exceptionsCustomInstances = options.exceptions
function calcExceptionsCustomInstances() { function calcExceptionsCustomInstances() {
document.getElementById("exceptions-custom-checklist").innerHTML = [...exceptionsCustomInstances.url, ...exceptionsCustomInstances.regex] document.getElementById("exceptions-custom-checklist").innerHTML = [
.map( ...exceptionsCustomInstances.url,
x => `<div> ...exceptionsCustomInstances.regex,
]
.map(
x => `<div>
${x} ${x}
<button class="add" id="clear-${x}"> <button class="add" id="clear-${x}">
<svg xmlns="https://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px" <svg xmlns="https://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px"
@ -177,40 +177,40 @@ function calcExceptionsCustomInstances() {
</button> </button>
</div> </div>
<hr>` <hr>`
) )
.join("\n") .join("\n")
for (const x of [...exceptionsCustomInstances.url, ...exceptionsCustomInstances.regex]) { for (const x of [...exceptionsCustomInstances.url, ...exceptionsCustomInstances.regex]) {
document.getElementById(`clear-${x}`).addEventListener("click", async () => { document.getElementById(`clear-${x}`).addEventListener("click", async () => {
let index let index
index = exceptionsCustomInstances.url.indexOf(x) index = exceptionsCustomInstances.url.indexOf(x)
if (index > -1) exceptionsCustomInstances.url.splice(index, 1) if (index > -1) exceptionsCustomInstances.url.splice(index, 1)
else { else {
index = exceptionsCustomInstances.regex.indexOf(x) index = exceptionsCustomInstances.regex.indexOf(x)
if (index > -1) exceptionsCustomInstances.regex.splice(index, 1) if (index > -1) exceptionsCustomInstances.regex.splice(index, 1)
} }
options = await utils.getOptions() options = await utils.getOptions()
options.exceptions = exceptionsCustomInstances options.exceptions = exceptionsCustomInstances
browser.storage.local.set({ options }) browser.storage.local.set({ options })
calcExceptionsCustomInstances() calcExceptionsCustomInstances()
}) })
} }
} }
calcExceptionsCustomInstances() calcExceptionsCustomInstances()
document.getElementById("custom-exceptions-instance-form").addEventListener("submit", async event => { document.getElementById("custom-exceptions-instance-form").addEventListener("submit", async event => {
event.preventDefault() event.preventDefault()
let val let val
if (instanceType == "url" && nameCustomInstanceInput.validity.valid) { if (instanceType == "url" && nameCustomInstanceInput.validity.valid) {
val = nameCustomInstanceInput.value val = nameCustomInstanceInput.value
if (!exceptionsCustomInstances.url.includes(val)) exceptionsCustomInstances.url.push(val) if (!exceptionsCustomInstances.url.includes(val)) exceptionsCustomInstances.url.push(val)
} else if (instanceType == "regex") { } else if (instanceType == "regex") {
val = nameCustomInstanceInput.value val = nameCustomInstanceInput.value
if (val.trim() != "" && !exceptionsCustomInstances.regex.includes(val)) exceptionsCustomInstances.regex.push(val) if (val.trim() != "" && !exceptionsCustomInstances.regex.includes(val)) exceptionsCustomInstances.regex.push(val)
} }
if (val) { if (val) {
options = await utils.getOptions() options = await utils.getOptions()
options.exceptions = exceptionsCustomInstances options.exceptions = exceptionsCustomInstances
browser.storage.local.set({ options }, () => nameCustomInstanceInput.value = "") browser.storage.local.set({ options }, () => (nameCustomInstanceInput.value = ""))
} }
calcExceptionsCustomInstances() calcExceptionsCustomInstances()
}) })

View File

@ -5,15 +5,15 @@ import servicesHelper from "../../assets/javascripts/services.js"
import utils from "../../assets/javascripts/utils.js" import utils from "../../assets/javascripts/utils.js"
document.getElementById("more-options").href = browser.runtime.getURL("pages/options/index.html") document.getElementById("more-options").href = browser.runtime.getURL("pages/options/index.html")
document.getElementById("more-options").setAttribute('target', '_blank') document.getElementById("more-options").setAttribute("target", "_blank")
await browser.runtime.getPlatformInfo(r => { await browser.runtime.getPlatformInfo(r => {
switch (r.os) { switch (r.os) {
case "fuchsia": case "fuchsia":
case "ios": case "ios":
case "android": case "android":
document.getElementsByTagName("html")[0].classList.add("mobile") document.getElementsByTagName("html")[0].classList.add("mobile")
} }
}) })
const allSites = document.getElementById("all_sites") const allSites = document.getElementById("all_sites")
@ -24,94 +24,96 @@ const config = await utils.getConfig()
const divs = {} const divs = {}
for (const service in config.services) { for (const service in config.services) {
divs[service] = {} divs[service] = {}
divs[service].all = allSites.getElementsByClassName(service)[0] divs[service].all = allSites.getElementsByClassName(service)[0]
divs[service].current = currSite.getElementsByClassName(service)[0] divs[service].current = currSite.getElementsByClassName(service)[0]
divs[service].all_toggle = allSites.getElementsByClassName(`${service}-enabled`)[0] divs[service].all_toggle = allSites.getElementsByClassName(`${service}-enabled`)[0]
divs[service].all_toggle.addEventListener("change", async () => { divs[service].all_toggle.addEventListener("change", async () => {
const options = await utils.getOptions() const options = await utils.getOptions()
options[service].enabled = divs[service].all_toggle.checked options[service].enabled = divs[service].all_toggle.checked
browser.storage.local.set({ options }) browser.storage.local.set({ options })
}) })
allSites.getElementsByClassName(`${service}-change_instance`)[0].addEventListener("click", () => { allSites.getElementsByClassName(`${service}-change_instance`)[0].addEventListener("click", () => {
browser.tabs.query({ active: true, currentWindow: true }, async tabs => { browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
if (tabs[0].url) { if (tabs[0].url) {
const url = new URL(tabs[0].url) const url = new URL(tabs[0].url)
browser.tabs.update({ url: await servicesHelper.switchInstance(url, service) }) browser.tabs.update({ url: await servicesHelper.switchInstance(url, service) })
} }
}) })
}) })
divs[service].current_toggle = currSite.getElementsByClassName(`${service}-enabled`)[0] divs[service].current_toggle = currSite.getElementsByClassName(`${service}-enabled`)[0]
divs[service].current_toggle.addEventListener("change", async () => { divs[service].current_toggle.addEventListener("change", async () => {
const options = await utils.getOptions() const options = await utils.getOptions()
options[service].enabled = divs[service].current_toggle.checked options[service].enabled = divs[service].current_toggle.checked
browser.storage.local.set({ options }) browser.storage.local.set({ options })
}) })
currSite.getElementsByClassName(`${service}-change_instance`)[0].addEventListener("click", () => { currSite.getElementsByClassName(`${service}-change_instance`)[0].addEventListener("click", () => {
browser.tabs.query({ active: true, currentWindow: true }, async tabs => { browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
if (tabs[0].url) { if (tabs[0].url) {
const url = new URL(tabs[0].url) const url = new URL(tabs[0].url)
browser.tabs.update({ url: await servicesHelper.switchInstance(url, service) }) browser.tabs.update({ url: await servicesHelper.switchInstance(url, service) })
} }
}) })
}) })
} }
browser.tabs.query({ active: true, currentWindow: true }, async tabs => { browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
// Set visibility of control buttons // Set visibility of control buttons
if (tabs[0].url) { if (tabs[0].url) {
const hr = document.getElementById("hr") const hr = document.getElementById("hr")
var url = new URL(tabs[0].url) var url = new URL(tabs[0].url)
servicesHelper.switchInstance(url).then(r => { servicesHelper.switchInstance(url).then(r => {
if (r) { if (r) {
document.getElementById("change_instance_div").style.display = "" document.getElementById("change_instance_div").style.display = ""
hr.style.display = "" hr.style.display = ""
document.getElementById("change_instance").addEventListener("click", async () => document
browser.tabs.update({ url: await servicesHelper.switchInstance(url) }) .getElementById("change_instance")
) .addEventListener("click", async () => browser.tabs.update({ url: await servicesHelper.switchInstance(url) }))
} }
}) })
servicesHelper.reverse(url).then(r => { servicesHelper.reverse(url).then(r => {
if (r) { if (r) {
hr.style.display = "" hr.style.display = ""
document.getElementById("copy_original_div").style.display = "" document.getElementById("copy_original_div").style.display = ""
document.getElementById("copy_original").addEventListener("click", () => servicesHelper.copyRaw(url)) document.getElementById("copy_original").addEventListener("click", () => servicesHelper.copyRaw(url))
document.getElementById("redirect_to_original_div").style.display = "" document.getElementById("redirect_to_original_div").style.display = ""
document.getElementById("redirect_to_original").addEventListener("click", () => browser.runtime.sendMessage("reverseTab")) document
} .getElementById("redirect_to_original")
}) .addEventListener("click", () => browser.runtime.sendMessage("reverseTab"))
servicesHelper.redirectAsync(url, "main_frame", null, true).then(r => { }
if (r) { })
document.getElementById("redirect_div").style.display = "" servicesHelper.redirectAsync(url, "main_frame", null, true).then(r => {
hr.style.display = "" if (r) {
document.getElementById("redirect").addEventListener("click", () => browser.runtime.sendMessage("redirectTab")) document.getElementById("redirect_div").style.display = ""
} hr.style.display = ""
}) document.getElementById("redirect").addEventListener("click", () => browser.runtime.sendMessage("redirectTab"))
} }
})
}
const options = await utils.getOptions() const options = await utils.getOptions()
// Set visibility of all service buttons // Set visibility of all service buttons
for (const service of options.popupServices) { for (const service of options.popupServices) {
divs[service].all.classList.remove("hide") divs[service].all.classList.remove("hide")
divs[service].all_toggle.checked = options[service].enabled divs[service].all_toggle.checked = options[service].enabled
} }
// Set visibility of current page service button // Set visibility of current page service button
if (url) { if (url) {
const service = await servicesHelper.computeService(url) const service = await servicesHelper.computeService(url)
if (service) { if (service) {
divs[service].all.classList.add("hide") divs[service].all.classList.add("hide")
divs[service].current.classList.remove("hide") divs[service].current.classList.remove("hide")
divs[service].current_toggle.checked = options[service].enabled divs[service].current_toggle.checked = options[service].enabled
currentSiteDivider.style.display = "" currentSiteDivider.style.display = ""
} }
} }
}) })

View File

@ -1,65 +1,65 @@
body { body {
width: 270px; width: 270px;
min-height: auto; min-height: auto;
} }
html, html,
body { body {
margin: 0; margin: 0;
} }
.hide { .hide {
display: none !important; display: none !important;
} }
.button { .button {
display: flex; display: flex;
margin: 0 auto; margin: 0 auto;
justify-content: space-between; justify-content: space-between;
} }
.button svg { .button svg {
width: 26px; width: 26px;
height: 26px; height: 26px;
} }
.bottom-button { .bottom-button {
width: 100%; width: 100%;
} }
.space { .space {
height: 10px; height: 10px;
} }
input { input {
height: 23px; height: 23px;
width: 46px; width: 46px;
} }
div.block label { div.block label {
margin: 0; margin: 0;
font-size: 18px; font-size: 18px;
font-weight: bold; font-weight: bold;
max-width: 180px; max-width: 180px;
} }
div.block label:hover { div.block label:hover {
cursor: pointer; cursor: pointer;
} }
div.block div { div.block div {
display: flex; display: flex;
} }
html.mobile body { html.mobile body {
width: 100%; width: 100%;
} }
html.mobile div.block label { html.mobile div.block label {
font-size: 24px; font-size: 24px;
} }
html.mobile .button svg { html.mobile .button svg {
width: 30px; width: 30px;
height: 30px; height: 30px;
} }

View File

@ -1,450 +1,449 @@
body { body {
--text: #fff; --text: #fff;
--bg-main: #121212; --bg-main: #121212;
--bg-secondary: #202020; --bg-secondary: #202020;
--active: #fbc117; --active: #fbc117;
--danger: #f04141; --danger: #f04141;
--light-grey: #c3c3c3; --light-grey: #c3c3c3;
} }
@font-face { @font-face {
font-family: "Inter"; font-family: "Inter";
src: url("Inter-VariableFont_slnt,wght.ttf"); src: url("Inter-VariableFont_slnt,wght.ttf");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@font-face { @font-face {
font-family: "Vazirmatn"; font-family: "Vazirmatn";
src: url("Vazirmatn-VariableFont_wght.ttf"); src: url("Vazirmatn-VariableFont_wght.ttf");
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
body { body {
margin: auto; margin: auto;
padding: 0; padding: 0;
font-family: "Inter"; font-family: "Inter";
font-size: 16px; font-size: 16px;
background-color: var(--bg-main); background-color: var(--bg-main);
color: var(--text); color: var(--text);
} }
body * { body * {
font-family: "Inter"; font-family: "Inter";
} }
body.rtl { body.rtl {
font-family: "Vazirmatn"; font-family: "Vazirmatn";
} }
body.rtl * { body.rtl * {
font-family: "Vazirmatn"; font-family: "Vazirmatn";
} }
div.block input[type="checkbox"] { div.block input[type="checkbox"] {
appearance: none; appearance: none;
-moz-appearance: none; -moz-appearance: none;
-webkit-appearance: none; -webkit-appearance: none;
} }
.title { .title {
display: flex; display: flex;
align-items: center; align-items: center;
text-decoration: none; text-decoration: none;
width: min-content; width: min-content;
color: var(--text); color: var(--text);
transition: .1s; transition: 0.1s;
} }
.title:hover { .title:hover {
opacity: 1 !important; opacity: 1 !important;
} }
.title:hover a { .title:hover a {
color: var(--active); color: var(--active);
} }
img, img,
svg { svg {
margin-right: 10px; margin-right: 10px;
height: 26px; height: 26px;
width: 26px; width: 26px;
color: var(--text); color: var(--text);
} }
body.rtl img, body.rtl img,
body.rtl svg { body.rtl svg {
margin-right: 0px; margin-right: 0px;
margin-left: 10px; margin-left: 10px;
} }
input[type="url"], input[type="url"],
input[type="text"], input[type="text"],
select { select {
font-weight: bold; font-weight: bold;
box-sizing: border-box; box-sizing: border-box;
border-style: solid; border-style: solid;
border-color: #767676; border-color: #767676;
color: var(--text); color: var(--text);
font-size: 16px; font-size: 16px;
padding: 8px; padding: 8px;
background-color: var(--bg-secondary); background-color: var(--bg-secondary);
border: none; border: none;
margin: 0; margin: 0;
max-width: 500px; max-width: 500px;
border-radius: 3px; border-radius: 3px;
} }
input[type="url"], input[type="url"],
input[type="text"] { input[type="text"] {
width: 400px; width: 400px;
cursor: text; cursor: text;
} }
input:invalid { input:invalid {
color: var(--danger); color: var(--danger);
} }
.button svg { .button svg {
height: 18px; height: 18px;
width: 18px; width: 18px;
} }
section.block-option { section.block-option {
width: 750px; width: 750px;
margin: 0 50px; margin: 0 50px;
} }
section.block-option h2 { section.block-option h2 {
margin: 0; margin: 0;
} }
body.option { body.option {
display: flex; display: flex;
padding: 40px; padding: 40px;
width: 1160px; width: 1160px;
} }
section.links { section.links {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: column; flex-direction: column;
width: 350px; width: 350px;
max-height: 1030px; max-height: 1030px;
} }
section.links div { section.links div {
margin: 10px; margin: 10px;
width: max-content; width: max-content;
} }
a { a {
text-decoration: none; text-decoration: none;
color: var(--text); color: var(--text);
transition: 0.1s; transition: 0.1s;
} }
a:hover { a:hover {
color: var(--active); color: var(--active);
} }
section.links a { section.links a {
display: flex; display: flex;
align-items: center; align-items: center;
font-size: 18px; font-size: 18px;
text-decoration: none; text-decoration: none;
color: white; color: white;
transition: 0.1s; transition: 0.1s;
} }
section.links a:hover, section.links a:hover,
section.links .selected { section.links .selected {
opacity: 1 !important; opacity: 1 !important;
} }
section.links .selected a { section.links .selected a {
color: var(--active); color: var(--active);
} }
::placeholder { ::placeholder {
color: var(--text); color: var(--text);
opacity: 0.7; opacity: 0.7;
} }
hr { hr {
height: 2px; height: 2px;
margin: 0 15px; margin: 0 15px;
background-color: rgb(77, 77, 77); background-color: rgb(77, 77, 77);
border: none; border: none;
} }
div.block { div.block {
padding: 0 15px; padding: 0 15px;
justify-content: space-between; justify-content: space-between;
display: flex; display: flex;
align-items: center; align-items: center;
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
} }
div.block-option { div.block-option {
margin: 30px 0; margin: 30px 0;
} }
div.block-option label { div.block-option label {
margin-right: 5px; margin-right: 5px;
width: 80%; width: 80%;
min-width: 150px; min-width: 150px;
font-size: 18px; font-size: 18px;
} }
div.block-option h1 { div.block-option h1 {
margin: 0; margin: 0;
font-size: 28px; font-size: 28px;
color: var(--text); color: var(--text);
} }
div.block-option div { div.block-option div {
text-align: center; text-align: center;
} }
div.block input[type="checkbox"] { div.block input[type="checkbox"] {
width: 46px; width: 46px;
height: 24px; height: 24px;
background-color: var(--light-grey); background-color: var(--light-grey);
border-radius: 50px; border-radius: 50px;
transition: 0.4s; transition: 0.4s;
cursor: pointer; cursor: pointer;
} }
div.block input[type="checkbox"]:checked { div.block input[type="checkbox"]:checked {
background-color: var(--active); background-color: var(--active);
} }
div.block input[type="checkbox"]::before { div.block input[type="checkbox"]::before {
content: ""; content: "";
display: inline-block; display: inline-block;
width: 18px; width: 18px;
height: 18px; height: 18px;
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
top: 2.5px; top: 2.5px;
left: 3.5px; left: 3.5px;
background-color: white; background-color: white;
border-radius: 50%; border-radius: 50%;
transition: 0.3s; transition: 0.3s;
} }
body.rtl div.block input[type="checkbox"]::before { body.rtl div.block input[type="checkbox"]::before {
left: auto; left: auto;
right: 4px; right: 4px;
} }
div.block input[type="checkbox"]:checked::before { div.block input[type="checkbox"]:checked::before {
left: 24px; left: 24px;
} }
body.rtl div.block input[type="checkbox"]:checked::before { body.rtl div.block input[type="checkbox"]:checked::before {
left: auto; left: auto;
right: 24px; right: 24px;
} }
div.buttons { div.buttons {
display: flex; display: flex;
margin: 0 15px; margin: 0 15px;
margin-bottom: 15px; margin-bottom: 15px;
margin-top: 15px; margin-top: 15px;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
justify-content: start; justify-content: start;
} }
.button { .button {
color: var(--text); color: var(--text);
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
text-decoration: none; text-decoration: none;
cursor: pointer; cursor: pointer;
transition-duration: 0.1s; transition-duration: 0.1s;
} }
.button:hover { .button:hover {
color: var(--active); color: var(--active);
} }
.button svg { .button svg {
width: auto; width: auto;
height: auto; height: auto;
padding: 0; padding: 0;
margin-right: 5px; margin-right: 5px;
} }
.button:hover svg { .button:hover svg {
color: var(--active); color: var(--active);
} }
.button-inline { .button-inline {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
margin: 7.5px 0; margin: 7.5px 0;
background-color: var(--bg-secondary); background-color: var(--bg-secondary);
border-radius: 5px; border-radius: 5px;
padding: 10px; padding: 10px;
} }
.button:active { .button:active {
transform: translateY(1px); transform: translateY(1px);
} }
button svg { button svg {
color: var(--text); color: var(--text);
} }
div.checklist div { div.checklist div {
justify-content: space-between; justify-content: space-between;
margin: 5px 15px; margin: 5px 15px;
padding: 10px 0; padding: 10px 0;
word-wrap: break-word; word-wrap: break-word;
display: flex; display: flex;
} }
div.checklist a { div.checklist a {
text-decoration: none; text-decoration: none;
color: var(--text); color: var(--text);
} }
div.checklist a:hover { div.checklist a:hover {
text-decoration: underline; text-decoration: underline;
} }
div.custom-checklist x a { div.custom-checklist x a {
color: var(--active); color: var(--active);
} }
button.add { button.add {
background-color: transparent; background-color: transparent;
border: none; border: none;
padding: 0; padding: 0;
margin: 0; margin: 0;
text-decoration: none; text-decoration: none;
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
} }
body.light-theme { body.light-theme {
--text: black; --text: black;
--bg-main: white; --bg-main: white;
--bg-secondary: #e4e4e4; --bg-secondary: #e4e4e4;
--active: #fb9817; --active: #fb9817;
} }
body.light-theme select { body.light-theme select {
border: 1px solid black; border: 1px solid black;
} }
body.light-theme a { body.light-theme a {
color: black; color: black;
} }
body.light-theme a:hover { body.light-theme a:hover {
color: var(--active) color: var(--active);
} }
button { button {
background-color: transparent; background-color: transparent;
color: var(--text); color: var(--text);
border: none; border: none;
text-decoration: none; text-decoration: none;
display: inline-block; display: inline-block;
cursor: pointer; cursor: pointer;
border-radius: 5px; border-radius: 5px;
} }
body div section { body div section {
display: none; display: none;
} }
select:disabled { select:disabled {
opacity: 0.6; opacity: 0.6;
cursor: not-allowed; cursor: not-allowed;
} }
input:disabled { input:disabled {
opacity: 0.6; opacity: 0.6;
cursor: not-allowed; cursor: not-allowed;
} }
@media (max-width: 1250px) { @media (max-width: 1250px) {
body.option { body.option {
flex-direction: column; flex-direction: column;
width: 95vw; width: 95vw;
align-items: center; align-items: center;
padding: 40px 0px; padding: 40px 0px;
} }
section.links { section.links {
flex-direction: row; flex-direction: row;
width: 95vw; width: 95vw;
padding: 0 55px; padding: 0 55px;
} }
section.block-option { section.block-option {
width: 95vw; width: 95vw;
} }
div.checklist div x { div.checklist div x {
overflow: hidden; overflow: hidden;
} }
} }
html.mobile img, html.mobile img,
html.mobile svg { html.mobile svg {
margin-right: 10px; margin-right: 10px;
height: 30px; height: 30px;
width: 30px; width: 30px;
color: var(--text); color: var(--text);
} }
html.mobile div.block { html.mobile div.block {
padding: 0 15px; padding: 0 15px;
justify-content: space-between; justify-content: space-between;
display: flex; display: flex;
align-items: center; align-items: center;
margin-top: 20px; margin-top: 20px;
margin-bottom: 20px; margin-bottom: 20px;
} }
html.mobile div.block input[type="checkbox"] { html.mobile div.block input[type="checkbox"] {
width: 58px; width: 58px;
height: 30px; height: 30px;
} }
html.mobile div.block input[type="checkbox"]::before { html.mobile div.block input[type="checkbox"]::before {
width: 24px; width: 24px;
height: 24px; height: 24px;
top: 3px; top: 3px;
left: 3.5px; left: 3.5px;
} }
html.mobile div.block input[type="checkbox"]:checked::before { html.mobile div.block input[type="checkbox"]:checked::before {
left: 30px; left: 30px;
} }
html.mobile body.option { html.mobile body.option {
flex-direction: column; flex-direction: column;
width: 100%; width: 100%;
padding: 0; padding: 0;
align-items: center; align-items: center;
} }
html.mobile section.links { html.mobile section.links {
flex-direction: row; flex-direction: row;
width: 100%; width: 100%;
padding: 0 55px; padding: 0 55px;
} }
html.mobile section.block-option { html.mobile section.block-option {
width: 100%; width: 100%;
} }

View File

@ -47,7 +47,7 @@ Bandcamp - [https://thorwegian.bandcamp.com/track/just-because](https://thorwegi
Instructables - [https://instructables.com/DIY-Arduino-Obstacle-Avoiding-Car-at-Home/](https://instructables.com/DIY-Arduino-Obstacle-Avoiding-Car-at-Home/) Instructables - [https://instructables.com/DIY-Arduino-Obstacle-Avoiding-Car-at-Home/](https://instructables.com/DIY-Arduino-Obstacle-Avoiding-Car-at-Home/)
Web archive - [https://web.archive.org/web/20230131222432if_/https://www.dailymail.co.uk/news/article-11687675/Army-spied-lockdown-critics-Sceptics-including-Peter-Hitchens-suspected-watched.html](https://web.archive.org/web/20230131222432if_/https://www.dailymail.co.uk/news/article-11687675/Army-spied-lockdown-critics-Sceptics-including-Peter-Hitchens-suspected-watched.html) Web archive - [https://web.archive.org/web/20230131222432if\_/https://www.dailymail.co.uk/news/article-11687675/Army-spied-lockdown-critics-Sceptics-including-Peter-Hitchens-suspected-watched.html](https://web.archive.org/web/20230131222432if_/https://www.dailymail.co.uk/news/article-11687675/Army-spied-lockdown-critics-Sceptics-including-Peter-Hitchens-suspected-watched.html)
--- ---