aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/assets/javascripts/localise.js50
-rw-r--r--src/assets/javascripts/services.js1587
-rw-r--r--src/assets/javascripts/utils.js197
-rw-r--r--src/config.json2158
-rw-r--r--src/manifest.json171
-rw-r--r--src/pages/background/background.html10
-rw-r--r--src/pages/background/background.js576
-rw-r--r--src/pages/messages/no_instance.html36
-rw-r--r--src/pages/options/index.js585
-rw-r--r--src/pages/options/init.js66
-rw-r--r--src/pages/options/widgets/general.js274
-rw-r--r--src/pages/popup/popup.js172
-rw-r--r--src/pages/popup/style.css48
-rw-r--r--src/pages/stylesheets/styles.css463
14 files changed, 3171 insertions, 3222 deletions
diff --git a/src/assets/javascripts/localise.js b/src/assets/javascripts/localise.js
index c0936873..d26d07d4 100644
--- a/src/assets/javascripts/localise.js
+++ b/src/assets/javascripts/localise.js
@@ -1,34 +1,34 @@
window.browser = window.browser || window.chrome
function localisePage() {
- /**
- * @param {string} tag
- */
- function getMessage(tag) {
- return tag.replace(/__MSG_(\w+)__/g, (_match, v1) => {
- return v1 ? browser.i18n.getMessage(v1) : null
- })
- }
+ /**
+ * @param {string} tag
+ */
+ function getMessage(tag) {
+ return tag.replace(/__MSG_(\w+)__/g, (_match, v1) => {
+ return v1 ? browser.i18n.getMessage(v1) : null
+ })
+ }
- const elements = document.querySelectorAll("[data-localise]")
- for (let i in elements)
- if (elements.hasOwnProperty(i)) {
- const obj = elements[i]
- const tag = obj.getAttribute("data-localise").toString()
- const msg = getMessage(tag)
- if (msg && msg !== tag) obj.textContent = msg
- }
+ const elements = document.querySelectorAll("[data-localise]")
+ for (let i in elements)
+ if (elements.hasOwnProperty(i)) {
+ const obj = elements[i]
+ const tag = obj.getAttribute("data-localise").toString()
+ const msg = getMessage(tag)
+ if (msg && msg !== tag) obj.textContent = msg
+ }
- const placeholders = document.querySelectorAll("[data-localise-placeholder]")
- for (let i in placeholders)
- if (placeholders.hasOwnProperty(i)) {
- const obj = placeholders[i]
- const tag = obj.getAttribute("data-localise-placeholder").toString()
- const msg = getMessage(tag)
- if (msg && msg !== tag) obj.placeholder = msg
- }
+ const placeholders = document.querySelectorAll("[data-localise-placeholder]")
+ for (let i in placeholders)
+ if (placeholders.hasOwnProperty(i)) {
+ const obj = placeholders[i]
+ const tag = obj.getAttribute("data-localise-placeholder").toString()
+ const msg = getMessage(tag)
+ if (msg && msg !== tag) obj.placeholder = msg
+ }
}
export default {
- localisePage,
+ localisePage,
}
diff --git a/src/assets/javascripts/services.js b/src/assets/javascripts/services.js
index b9ee64e5..39347845 100644
--- a/src/assets/javascripts/services.js
+++ b/src/assets/javascripts/services.js
@@ -6,25 +6,25 @@ window.browser = window.browser || window.chrome
let config, options
async function init() {
- options = await utils.getOptions()
- config = await utils.getConfig()
+ options = await utils.getOptions()
+ config = await utils.getConfig()
}
init()
browser.storage.onChanged.addListener(init)
function all(service, frontend, options, config) {
- let instances = []
- if (!frontend) {
- for (const frontend in config.services[service].frontends) {
- if (options[frontend]) {
- instances.push(...options[frontend])
- }
- }
- } else if (options[frontend]) {
- instances = options[frontend]
- }
- return instances
+ let instances = []
+ if (!frontend) {
+ for (const frontend in config.services[service].frontends) {
+ if (options[frontend]) {
+ instances.push(...options[frontend])
+ }
+ }
+ } else if (options[frontend]) {
+ instances = options[frontend]
+ }
+ return instances
}
/**
@@ -34,17 +34,17 @@ function all(service, frontend, options, config) {
* @param {string} frontend
*/
function regexArray(service, url, config, frontend) {
- let targetList = config.services[service].targets
- if (frontend && 'excludeTargets' in config.services[service].frontends[frontend]) {
- targetList = targetList.filter(val =>
- !config.services[service].frontends[frontend].excludeTargets.includes(targetList.indexOf(val))
- )
- }
- for (const targetString in targetList) {
- const target = new RegExp(targetList[targetString])
- if (target.test(url.href)) return true
- }
- return false
+ let targetList = config.services[service].targets
+ if (frontend && "excludeTargets" in config.services[service].frontends[frontend]) {
+ targetList = targetList.filter(
+ val => !config.services[service].frontends[frontend].excludeTargets.includes(targetList.indexOf(val))
+ )
+ }
+ for (const targetString in targetList) {
+ const target = new RegExp(targetList[targetString])
+ if (target.test(url.href)) return true
+ }
+ return false
}
/**
@@ -54,8 +54,8 @@ function regexArray(service, url, config, frontend) {
* @param {boolean} forceRedirection
*/
async function redirectAsync(url, type, initiator, forceRedirection) {
- await init()
- return redirect(url, type, initiator, forceRedirection)
+ await init()
+ return redirect(url, type, initiator, forceRedirection)
}
/**
@@ -65,458 +65,481 @@ async function redirectAsync(url, type, initiator, forceRedirection) {
* @returns {undefined|string}
*/
function rewrite(url, frontend, randomInstance) {
- switch (frontend) {
- case "hyperpipe":
- return `${randomInstance}${url.pathname}${url.search}`.replace(/\/search\?q=.*/, searchQuery => searchQuery.replace("?q=", "/"))
- case "searx":
- case "searxng":
- return `${randomInstance}/${url.search}`
- case "whoogle":
- return `${randomInstance}/search${url.search}`
- case "4get": {
- const s = url.searchParams.get("q")
- if (s !== null) return `${randomInstance}/web?s=${encodeURIComponent(s)}`
- return randomInstance
- }
- case "librey":
- return `${randomInstance}/search.php${url.search}`
- case "yattee":
- url.searchParams.delete("si")
- return url.href.replace(/^https?:\/{2}/, "yattee://")
- case "freetube":
- url.searchParams.delete("si")
- return 'freetube://' + url.href
- case "freetubePwa":
- url.searchParams.delete("si")
- return 'freetube://' + url.href
- case "poketube": {
- url.searchParams.delete("si")
- if (url.pathname.startsWith('/channel')) {
- const reg = /\/channel\/(.*)\/?$/.exec(url.pathname)
- if (reg) {
- const id = reg[1]
- return `${randomInstance}/channel?id=${id}${url.search}`
- }
- }
- if (/\/@[a-z]+\//.exec(url.pathname)) return randomInstance
- return `${randomInstance}${url.pathname}${url.search}`
- }
- case "libMedium":
- case "scribe": {
- const regex = url.hostname.match(/^(link|cdn-images-\d+|.*)\.medium\.com/)
- if (regex && regex.length > 1) {
- const subdomain = regex[1]
- if (subdomain != "link" || !subdomain.startsWith("cdn-images")) {
- return `${randomInstance}/@${subdomain}${url.pathname}${url.search}`
- }
- }
- return `${randomInstance}${url.pathname}${url.search}`
- }
- case "simplyTranslate":
- return `${randomInstance}/${url.search}`
- case "send":
- case "mozhi":
- return randomInstance
- case "libreTranslate":
- return `${randomInstance}/${url.search.replace("sl", "source").replace("tl", "target").replace("text", "q")}`
- case "osm": {
- const placeRegex = /\/place\/(.*?)\//
- function convertMapCentre(url) {
- let [lat, lon, zoom] = [null, null, null]
- const reg = url.pathname.match(/@(-?\d[0-9.]*),(-?\d[0-9.]*),(\d{1,2})[.z]/)
- if (reg) {
- [, lon, lat, zoom] = reg
- } else if (url.searchParams.has("center")) {
- // Set map centre if present
- [lat, lon] = url.searchParams.get("center").split(",")
- zoom = url.searchParams.get("zoom") ?? "17"
- }
- return { zoom, lon, lat }
- }
- function addressToLatLng(address) {
- const http = new XMLHttpRequest()
- http.open("GET", `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(address)}&format=json&limit=1`, false)
- http.send()
- if (http.status == 200) {
- const json = JSON.parse(http.responseText)[0]
- if (json) {
- return {
- coordinate: `${json.lat},${json.lon}`,
- boundingbox: `${json.boundingbox[2]},${json.boundingbox[1]},${json.boundingbox[3]},${json.boundingbox[0]}`
- }
- }
- return {}
- }
- }
- function getQuery(url) {
- let query = ""
- if (url.searchParams.has("q")) query = url.searchParams.get("q")
- else if (url.searchParams.has("query")) query = url.searchParams.has("query")
- return query
- }
- function prefsEncoded(prefs) {
- return new URLSearchParams(prefs).toString()
- }
+ switch (frontend) {
+ case "hyperpipe":
+ return `${randomInstance}${url.pathname}${url.search}`.replace(/\/search\?q=.*/, searchQuery =>
+ searchQuery.replace("?q=", "/")
+ )
+ case "searx":
+ case "searxng":
+ return `${randomInstance}/${url.search}`
+ case "whoogle":
+ return `${randomInstance}/search${url.search}`
+ case "4get": {
+ const s = url.searchParams.get("q")
+ if (s !== null) return `${randomInstance}/web?s=${encodeURIComponent(s)}`
+ return randomInstance
+ }
+ case "librey":
+ return `${randomInstance}/search.php${url.search}`
+ case "yattee":
+ url.searchParams.delete("si")
+ return url.href.replace(/^https?:\/{2}/, "yattee://")
+ case "freetube":
+ url.searchParams.delete("si")
+ return "freetube://" + url.href
+ case "freetubePwa":
+ url.searchParams.delete("si")
+ return "freetube://" + url.href
+ case "poketube": {
+ url.searchParams.delete("si")
+ if (url.pathname.startsWith("/channel")) {
+ const reg = /\/channel\/(.*)\/?$/.exec(url.pathname)
+ if (reg) {
+ const id = reg[1]
+ return `${randomInstance}/channel?id=${id}${url.search}`
+ }
+ }
+ if (/\/@[a-z]+\//.exec(url.pathname)) return randomInstance
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ case "libMedium":
+ case "scribe": {
+ const regex = url.hostname.match(/^(link|cdn-images-\d+|.*)\.medium\.com/)
+ if (regex && regex.length > 1) {
+ const subdomain = regex[1]
+ if (subdomain != "link" || !subdomain.startsWith("cdn-images")) {
+ return `${randomInstance}/@${subdomain}${url.pathname}${url.search}`
+ }
+ }
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ case "simplyTranslate":
+ return `${randomInstance}/${url.search}`
+ case "send":
+ case "mozhi":
+ return randomInstance
+ case "libreTranslate":
+ return `${randomInstance}/${url.search.replace("sl", "source").replace("tl", "target").replace("text", "q")}`
+ case "osm": {
+ const placeRegex = /\/place\/(.*?)\//
+ function convertMapCentre(url) {
+ let [lat, lon, zoom] = [null, null, null]
+ const reg = url.pathname.match(/@(-?\d[0-9.]*),(-?\d[0-9.]*),(\d{1,2})[.z]/)
+ if (reg) {
+ ;[, lon, lat, zoom] = reg
+ } else if (url.searchParams.has("center")) {
+ // Set map centre if present
+ ;[lat, lon] = url.searchParams.get("center").split(",")
+ zoom = url.searchParams.get("zoom") ?? "17"
+ }
+ return { zoom, lon, lat }
+ }
+ function addressToLatLng(address) {
+ const http = new XMLHttpRequest()
+ http.open(
+ "GET",
+ `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(address)}&format=json&limit=1`,
+ false
+ )
+ http.send()
+ if (http.status == 200) {
+ const json = JSON.parse(http.responseText)[0]
+ if (json) {
+ return {
+ coordinate: `${json.lat},${json.lon}`,
+ boundingbox: `${json.boundingbox[2]},${json.boundingbox[1]},${json.boundingbox[3]},${json.boundingbox[0]}`,
+ }
+ }
+ return {}
+ }
+ }
+ function getQuery(url) {
+ let query = ""
+ if (url.searchParams.has("q")) query = url.searchParams.get("q")
+ else if (url.searchParams.has("query")) query = url.searchParams.has("query")
+ return query
+ }
+ function prefsEncoded(prefs) {
+ return new URLSearchParams(prefs).toString()
+ }
- if (initiator && initiator.host === "earth.google.com") return randomInstance
+ if (initiator && initiator.host === "earth.google.com") return randomInstance
- let mapCentre = "#"
- let prefs = { layers: "mapnik" }
+ let mapCentre = "#"
+ let prefs = { layers: "mapnik" }
- const mapCentreData = convertMapCentre(url)
- if (mapCentreData.zoom && mapCentreData.lon && mapCentreData.lat) mapCentre = `#map=${mapCentreData.zoom}/${mapCentreData.lon}/${mapCentreData.lat}`
+ const mapCentreData = convertMapCentre(url)
+ if (mapCentreData.zoom && mapCentreData.lon && mapCentreData.lat)
+ mapCentre = `#map=${mapCentreData.zoom}/${mapCentreData.lon}/${mapCentreData.lat}`
- if (url.pathname.includes("/embed")) { // https://www.google.com/maps/embed/v1/place?key=AIzaSyD4iE2xVSpkLLOXoyqT-RuPwURN3ddScAI&q=Eiffel+Tower,Paris+France
- const query = getQuery(url)
- let { coordinate, boundingbox } = addressToLatLng(query)
- prefs.bbox = boundingbox
- prefs.marker = coordinate
- return `${randomInstance}/export/embed.html?${prefsEncoded(prefs)}`
- } else if (url.pathname.includes("/dir")) {
- if (url.searchParams.has("travelmode")) {
- const travelModes = {
- driving: "fossgis_osrm_car",
- walking: "fossgis_osrm_foot",
- bicycling: "fossgis_osrm_bike",
- transit: "fossgis_osrm_car", // not implemented on OSM, default to car.
- }
- prefs.engine = travelModes[url.searchParams.get("travelmode")]
- }
- const regex1 = /\/dir\/([^@/]+)\/([^@/]+)\/@-?\d[0-9.]*,-?\d[0-9.]*,\d{1,2}[.z]/.exec(url.pathname)
- const regex2 = /\/dir\/([^@/]+)\//.exec(url.pathname)
- if (regex1) { // https://www.google.com/maps/dir/92+Rue+Moncey,+69003+Lyon,+France/M%C3%A9dip%C3%B4le+Lyon-Villeurbanne/@45.760254,4.8486298,13z?travelmode=bicycling
- const origin = addressToLatLng(decodeURIComponent(regex1[1])).coordinate ?? ''
- const destination = addressToLatLng(decodeURIComponent(regex1[2])).coordinate ?? ''
- prefs.route = `${origin};${destination}`
- } else if (regex2) { // https://www.google.com/maps/dir/92+Rue+Moncey,+69003+Lyon,+France/@45.760254,4.8486298,13z?travelmode=bicycling
- const origin = addressToLatLng(decodeURIComponent(regex2[1])).coordinate ?? ''
- prefs.route = `${origin};`
- } else { // https://www.google.com/maps/dir/?api=1&origin=Space+Needle+Seattle+WA&destination=Pike+Place+Market+Seattle+WA&travelmode=bicycling
- const origin = addressToLatLng(url.searchParams.get("origin")).coordinate ?? ''
- const destination = addressToLatLng(url.searchParams.get("destination")).coordinate ?? ''
- prefs.route = `${origin};${destination}`
- }
- return `${randomInstance}/directions?${prefsEncoded(prefs)}${mapCentre}`
- } else if (url.pathname.match(placeRegex)) { // https://www.google.com/maps/place/H%C3%B4tel+de+Londres+Eiffel/@40.9845265,28.7081268,14z
- const query = url.pathname.match(placeRegex)[1]
- return `${randomInstance}/search?query=${query}${mapCentre}`
- } else if (url.searchParams.has("ll")) { // https://maps.google.com/?ll=38.882147,-76.99017
- const [mlat, mlon] = url.searchParams.get("ll").split(",")
- return `${randomInstance}/search?query=${mlat}%2C${mlon}`
- } else if (url.searchParams.has("viewpoint")) { // https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=48.857832,2.295226&heading=-45&pitch=38&fov=80
- const [mlat, mlon] = url.searchParams.get("viewpoint").split(",")
- return `${randomInstance}/search?query=${mlat}%2C${mlon}`
- } else {
- const query = getQuery(url)
- if (query) return `${randomInstance}/search?query="${query}${mapCentre}&${prefsEncoded(prefs)}`
- }
- return `${randomInstance}/${mapCentre}&${prefsEncoded(prefs)}`
- }
- case "breezeWiki": {
- let wiki, urlpath = ""
- if (url.hostname.match(/^[a-zA-Z0-9-]+\.(?:fandom|wikia)\.com/)) {
- wiki = url.hostname.match(/^[a-zA-Z0-9-]+(?=\.(?:fandom|wikia)\.com)/)
- if (wiki == "www" || !wiki) wiki = ""
- else wiki = `/${wiki}`
- urlpath = url.pathname
- } else {
- wiki = url.pathname.match(/(?<=wiki\/w:c:)[a-zA-Z0-9-]+(?=:)/)
- if (!wiki) wiki = ""
- else {
- wiki = "/" + wiki + "/wiki/"
- urlpath = url.pathname.match(/(?<=wiki\/w:c:[a-zA-Z0-9-]+:).+/)
- }
- }
- if (url.href.search(/Special:Search\?query/) > -1) {
- return `${randomInstance}${wiki}${urlpath}${url.search}`.replace(/Special:Search\?query/, "search?q").replace(/\/wiki/, "")
- }
- return `${randomInstance}${wiki}${urlpath}${url.search}`
- }
- case "rimgo":
- if (url.href.search(/^https?:\/{2}(?:[im]\.)?stack\./) > -1) return `${randomInstance}/stack${url.pathname}${url.search}`
- return `${randomInstance}${url.pathname}${url.search}`
- case "redlib":
- case "libreddit": {
- const subdomain = url.hostname.match(/^(?:(?:external-)?preview|i)(?=\.redd\.it)/)
- if (!subdomain) return `${randomInstance}${url.pathname}${url.search}`
- switch (subdomain[0]) {
- case "preview":
- return `${randomInstance}/preview/pre${url.pathname}${url.search}`
- case "external-preview":
- return `${randomInstance}/preview/external-pre${url.pathname}${url.search}`
- case "i":
- return `${randomInstance}/img${url.pathname}`
- }
- return randomInstance
- }
- case "teddit":
- if (/^(?:(?:external-)?preview|i)\.redd\.it/.test(url.hostname)) {
- if (url.search == "") return `${randomInstance}${url.pathname}?teddit_proxy=${url.hostname}`
- else return `${randomInstance}${url.pathname}${url.search}&teddit_proxy=${url.hostname}`
- }
- return `${randomInstance}${url.pathname}${url.search}`
- case "neuters": {
- const p = url.pathname
- if (p.startsWith('/article/') || p.startsWith('/pf/') || p.startsWith('/arc/') || p.startsWith('/resizer/')) {
- return randomInstance
- }
- return `${randomInstance}${p}`
- }
- case "dumb":
- if (url.pathname.endsWith('-lyrics')) return `${randomInstance}${url.pathname}`
- return `${randomInstance}${url.pathname}${url.search}`
- case "intellectual":
- return `${randomInstance}${url.pathname}${url.search}`
- case "ruralDictionary":
- if (!url.pathname.includes('/define.php') && !url.pathname.includes('/random.php') && url.pathname != '/') return randomInstance
- return `${randomInstance}${url.pathname}${url.search}`
- case "anonymousOverflow": {
- if (url.hostname == "stackoverflow.com") {
- const threadID = /^\/a\/(\d+)\/?/.exec(url.pathname)
- if (threadID) return `${randomInstance}/questions/${threadID[1]}${url.search}`
- return `${randomInstance}${url.pathname}${url.search}`
- }
- if (url.pathname == "/" || url.pathname == "") {
- // https://stackexchange.com or https://superuser.com
- return `${randomInstance}${url.pathname}${url.search}`
- }
- const regex = url.href.match(/https?:\/{2}(?:([a-zA-Z0-9-]+)\.)?stackexchange\.com\//)
- if (regex && regex.length > 1) {
- const subdomain = regex[1]
- return `${randomInstance}/exchange/${subdomain}${url.pathname}${url.search}`
- }
- const notExchangeRegex = url.hostname.match(/(?:[a-zA-Z]+\.)?(?:askubuntu\.com|mathoverflow\.net|serverfault\.com|stackapps\.com|superuser\.com|stackoverflow\.com)/)
- if (notExchangeRegex) {
- return `${randomInstance}/exchange/${notExchangeRegex[0]}${url.pathname}${url.search}`
- }
- return `${randomInstance}${url.pathname}${url.search}`
- }
- case "biblioReads":
- return `${randomInstance}${url.pathname}${url.search}`
- case "wikiless": {
- let hostSplit = url.host.split(".")
- // wikiless doesn't have mobile view support yet
- if (hostSplit[0] != "wikipedia" && hostSplit[0] != "www") {
- if (hostSplit[0] == "m") url.searchParams.append("mobileaction", "toggle_view_mobile")
- else url.searchParams.append("lang", hostSplit[0])
- if (hostSplit[1] == "m") url.searchParams.append("mobileaction", "toggle_view_mobile")
- }
- return `${randomInstance}${url.pathname}${url.search}${url.hash}`
- }
- case "proxiTok":
- if (url.pathname.startsWith('/email')) return randomInstance
- return `${randomInstance}${url.pathname}${url.search}`
- case "waybackClassic": {
- const regex = /^\/\web\/(?:[0-9]+)?\*\/(.*)/.exec(url.pathname)
- if (regex) {
- const link = regex[1]
- return `${randomInstance}/cgi-bin/history.cgi?utf8=✓&q=${encodeURIComponent(link)}`
- }
- const regex2 = /(^\/\web\/([0-9]+)\/.*)/.exec(url.pathname)
- if (regex2) {
- let link = regex2[1]
- link = link.replace(regex2[2], regex2[2] + 'if_')
- return `https://web.archive.org${link}`
- }
- return
- }
- case "gothub":
- if (url.hostname == "gist.github.com") return `${randomInstance}/gist${url.pathname}${url.search}`
- if (url.hostname == "raw.githubusercontent.com") return `${randomInstance}/raw${url.pathname}${url.search}`
- return `${randomInstance}${url.pathname}${url.search}`
- case "mikuInvidious":
- if (url.hostname == "bilibili.com" || url.hostname == "www.bilibili.com" || url.hostname == 'b23.tv') return `${randomInstance}${url.pathname}${url.search}`
- if (url.hostname == "space.bilibili.com") return `${randomInstance}/space${url.pathname}${url.search}`
- case "tent": {
- if (url.hostname == 'bandcamp.com' && url.pathname == '/search') {
- const query = url.searchParams.get('q')
- return `${randomInstance}/search.php?query=${encodeURIComponent(query)}`
- }
- if (url.hostname.endsWith('bandcamp.com')) {
- const regex = /^(.*)\.bandcamp\.com/.exec(url.hostname)
- const artist = regex[1]
- if (url.pathname == '/' || url.pathname == '/music') {
- return `${randomInstance}/artist.php?name=${artist}`
- } else {
- const regex = /^\/(.*)\/(.*)/.exec(url.pathname)
- if (regex) {
- const type = regex[1]
- const name = regex[2]
- return `${randomInstance}/release.php?artist=${artist}&type=${type}&name=${name}`
- }
- }
- }
- if (url.hostname == 'f4.bcbits.com') {
- const regex = /\/img\/(.*)/.exec(url.pathname)
- const image = regex[1]
- return `${randomInstance}/image.php?file=${image}`
- }
- if (url.hostname == 't4.bcbits.com') {
- const regex = /\/stream\/(.*)\/(.*)\/(.*)/.exec(url.pathname)
- if (regex) {
- const directory = regex[1]
- const format = regex[2]
- const file = regex[3]
- const token = url.searchParams.get('token')
- return `${randomInstance}/audio.php/?directory=${directory}&format=${format}&file=${file}&token=${encodeURIComponent(token)}`
- }
- }
- }
- case "binternet":
- if (url.hostname == "i.pinimg.com") return `${randomInstance}/image_proxy.php?url=${url.href}`
- case "laboratory": {
- let path = url.pathname
- if (path == "/") path = ""
- return `${randomInstance}/${url.hostname}${path}${url.search}`
- }
- case "quetre": {
- const regex = /([a-z]+)\.quora\.com/.exec(url.hostname)
- if (regex) {
- const lang = regex[1]
- url.searchParams.append("lang", lang)
- return `${randomInstance}${url.pathname}${url.search}`
- }
- return `${randomInstance}${url.pathname}${url.search}`
- }
- case "pixivFe": {
- const regex = /\/[a-z]{1,3}\/(.*)/.exec(url.pathname)
- if (regex) {
- const path = regex[1]
- return `${randomInstance}/${path}${url.search}`
- }
- return `${randomInstance}${url.pathname}${url.search}`
- }
- case "invidious": {
- url.searchParams.delete("si")
- if (url.hostname == "youtu.be" || url.hostname.endsWith("youtube.com") && url.pathname.startsWith("/live")) {
- const watch = url.pathname.substring(url.pathname.lastIndexOf('/') + 1)
- return `${randomInstance}/watch?v=${watch}${url.search.replace("?", "&")}`
- }
- if (url.hostname.endsWith("youtube.com") && url.pathname.startsWith("/redirect?"))
- return url.href
- return `${randomInstance}${url.pathname}${url.search}`
- }
- case "freetubeMusic": {
- if (url.hostname == "youtu.be" || url.hostname.endsWith("youtube.com") && url.pathname.startsWith("/live")) {
- const watch = url.pathname.substring(url.pathname.lastIndexOf('/') + 1)
- return `freetube://youtube.com/watch?v=${watch}`
- }
- return 'freetube://' + url.href
- }
- case "invidiousMusic": {
- if (url.hostname == "youtu.be" || url.hostname.endsWith("youtube.com") && url.pathname.startsWith("/live")) {
- const watch = url.pathname.substring(url.pathname.lastIndexOf('/') + 1)
- return `${randomInstance}/watch?v=${watch}`
- }
- return `${randomInstance}${url.pathname}${url.search}`
- }
- case 'materialious': {
- url.searchParams.delete('si')
- if (url.hostname == 'youtu.be' || (url.hostname.endsWith('youtube.com') && url.pathname.startsWith('/live'))) {
- const watch = url.pathname.substring(url.pathname.lastIndexOf('/') + 1)
- return `${randomInstance}/watch/${watch}${url.search.replace('?', '&')}`
- }
- if (url.hostname.endsWith("youtube.com")) {
- if (url.pathname.startsWith('/watch')) {
- if (url.searchParams.has('v')) {
- const watch = url.searchParams.get('v')
- url.searchParams.delete('v')
- return `${randomInstance}/watch/${watch}${url.search.replace('?', '&')}`
- }
- return `${randomInstance}/watch/${url.search.replace('?', '&')}`
- }
- if (url.pathname.startsWith('/results')) {
- if (url.searchParams.has('search_query')) {
- const search = url.searchParams.get('search_query')
- url.searchParams.delete('search_query')
- return `${randomInstance}/search/${search}${url.search.replace('?', '&')}`
- }
- return `${randomInstance}/search/${url.search.replace('?', '&')}`
- }
- if (url.pathname.startsWith('/redirect?')) {
- return url.href
- }
- }
- return `${randomInstance}${url.pathname}${url.search}`
- }
- case "libremdb": {
- if (url.pathname.startsWith("/Name")) {
- for (const [key, value] of url.searchParams.entries()) {
- return `${randomInstance}/title/${encodeURIComponent(key)}`
- }
- }
- return `${randomInstance}${url.pathname}${url.search}`
- }
- case "tuboYoutube":
- url.searchParams.delete("si")
- if (url.pathname.startsWith("/channel")) return `${randomInstance}/channel?url=${encodeURIComponent(url.href)}`
- if (url.pathname.startsWith("/watch")) return `${randomInstance}/stream?url=${encodeURIComponent(url.href)}`
- return randomInstance
- case "tuboSoundcloud":
- if (url.pathname == '/') return `${randomInstance}?kiosk?serviceId=1`
- if (url.pathname.match(/^\/[^\/]+(\/$|$)/)) return `${randomInstance}/channel?url=${encodeURIComponent(url.href)}`
- if (url.pathname.match(/^\/[^\/]+\/[^\/]+/)) return `${randomInstance}/stream?url=${encodeURIComponent(url.href)}`
- return randomInstance
- case "twineo":
- case "safetwitch":
- if (url.hostname.startsWith("clips.")) return `${randomInstance}/clip${url.pathname}${url.search}`
- return `${randomInstance}${url.pathname}${url.search}`
+ if (url.pathname.includes("/embed")) {
+ // https://www.google.com/maps/embed/v1/place?key=AIzaSyD4iE2xVSpkLLOXoyqT-RuPwURN3ddScAI&q=Eiffel+Tower,Paris+France
+ const query = getQuery(url)
+ let { coordinate, boundingbox } = addressToLatLng(query)
+ prefs.bbox = boundingbox
+ prefs.marker = coordinate
+ return `${randomInstance}/export/embed.html?${prefsEncoded(prefs)}`
+ } else if (url.pathname.includes("/dir")) {
+ if (url.searchParams.has("travelmode")) {
+ const travelModes = {
+ driving: "fossgis_osrm_car",
+ walking: "fossgis_osrm_foot",
+ bicycling: "fossgis_osrm_bike",
+ transit: "fossgis_osrm_car", // not implemented on OSM, default to car.
+ }
+ prefs.engine = travelModes[url.searchParams.get("travelmode")]
+ }
+ const regex1 = /\/dir\/([^@/]+)\/([^@/]+)\/@-?\d[0-9.]*,-?\d[0-9.]*,\d{1,2}[.z]/.exec(url.pathname)
+ const regex2 = /\/dir\/([^@/]+)\//.exec(url.pathname)
+ if (regex1) {
+ // https://www.google.com/maps/dir/92+Rue+Moncey,+69003+Lyon,+France/M%C3%A9dip%C3%B4le+Lyon-Villeurbanne/@45.760254,4.8486298,13z?travelmode=bicycling
+ const origin = addressToLatLng(decodeURIComponent(regex1[1])).coordinate ?? ""
+ const destination = addressToLatLng(decodeURIComponent(regex1[2])).coordinate ?? ""
+ prefs.route = `${origin};${destination}`
+ } else if (regex2) {
+ // https://www.google.com/maps/dir/92+Rue+Moncey,+69003+Lyon,+France/@45.760254,4.8486298,13z?travelmode=bicycling
+ const origin = addressToLatLng(decodeURIComponent(regex2[1])).coordinate ?? ""
+ prefs.route = `${origin};`
+ } else {
+ // https://www.google.com/maps/dir/?api=1&origin=Space+Needle+Seattle+WA&destination=Pike+Place+Market+Seattle+WA&travelmode=bicycling
+ const origin = addressToLatLng(url.searchParams.get("origin")).coordinate ?? ""
+ const destination = addressToLatLng(url.searchParams.get("destination")).coordinate ?? ""
+ prefs.route = `${origin};${destination}`
+ }
+ return `${randomInstance}/directions?${prefsEncoded(prefs)}${mapCentre}`
+ } else if (url.pathname.match(placeRegex)) {
+ // https://www.google.com/maps/place/H%C3%B4tel+de+Londres+Eiffel/@40.9845265,28.7081268,14z
+ const query = url.pathname.match(placeRegex)[1]
+ return `${randomInstance}/search?query=${query}${mapCentre}`
+ } else if (url.searchParams.has("ll")) {
+ // https://maps.google.com/?ll=38.882147,-76.99017
+ const [mlat, mlon] = url.searchParams.get("ll").split(",")
+ return `${randomInstance}/search?query=${mlat}%2C${mlon}`
+ } else if (url.searchParams.has("viewpoint")) {
+ // https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=48.857832,2.295226&heading=-45&pitch=38&fov=80
+ const [mlat, mlon] = url.searchParams.get("viewpoint").split(",")
+ return `${randomInstance}/search?query=${mlat}%2C${mlon}`
+ } else {
+ const query = getQuery(url)
+ if (query) return `${randomInstance}/search?query="${query}${mapCentre}&${prefsEncoded(prefs)}`
+ }
+ return `${randomInstance}/${mapCentre}&${prefsEncoded(prefs)}`
+ }
+ case "breezeWiki": {
+ let wiki,
+ urlpath = ""
+ if (url.hostname.match(/^[a-zA-Z0-9-]+\.(?:fandom|wikia)\.com/)) {
+ wiki = url.hostname.match(/^[a-zA-Z0-9-]+(?=\.(?:fandom|wikia)\.com)/)
+ if (wiki == "www" || !wiki) wiki = ""
+ else wiki = `/${wiki}`
+ urlpath = url.pathname
+ } else {
+ wiki = url.pathname.match(/(?<=wiki\/w:c:)[a-zA-Z0-9-]+(?=:)/)
+ if (!wiki) wiki = ""
+ else {
+ wiki = "/" + wiki + "/wiki/"
+ urlpath = url.pathname.match(/(?<=wiki\/w:c:[a-zA-Z0-9-]+:).+/)
+ }
+ }
+ if (url.href.search(/Special:Search\?query/) > -1) {
+ return `${randomInstance}${wiki}${urlpath}${url.search}`
+ .replace(/Special:Search\?query/, "search?q")
+ .replace(/\/wiki/, "")
+ }
+ return `${randomInstance}${wiki}${urlpath}${url.search}`
+ }
+ case "rimgo":
+ if (url.href.search(/^https?:\/{2}(?:[im]\.)?stack\./) > -1)
+ return `${randomInstance}/stack${url.pathname}${url.search}`
+ return `${randomInstance}${url.pathname}${url.search}`
+ case "redlib":
+ case "libreddit": {
+ const subdomain = url.hostname.match(/^(?:(?:external-)?preview|i)(?=\.redd\.it)/)
+ if (!subdomain) return `${randomInstance}${url.pathname}${url.search}`
+ switch (subdomain[0]) {
+ case "preview":
+ return `${randomInstance}/preview/pre${url.pathname}${url.search}`
+ case "external-preview":
+ return `${randomInstance}/preview/external-pre${url.pathname}${url.search}`
+ case "i":
+ return `${randomInstance}/img${url.pathname}`
+ }
+ return randomInstance
+ }
+ case "teddit":
+ if (/^(?:(?:external-)?preview|i)\.redd\.it/.test(url.hostname)) {
+ if (url.search == "") return `${randomInstance}${url.pathname}?teddit_proxy=${url.hostname}`
+ else return `${randomInstance}${url.pathname}${url.search}&teddit_proxy=${url.hostname}`
+ }
+ return `${randomInstance}${url.pathname}${url.search}`
+ case "neuters": {
+ const p = url.pathname
+ if (p.startsWith("/article/") || p.startsWith("/pf/") || p.startsWith("/arc/") || p.startsWith("/resizer/")) {
+ return randomInstance
+ }
+ return `${randomInstance}${p}`
+ }
+ case "dumb":
+ if (url.pathname.endsWith("-lyrics")) return `${randomInstance}${url.pathname}`
+ return `${randomInstance}${url.pathname}${url.search}`
+ case "intellectual":
+ return `${randomInstance}${url.pathname}${url.search}`
+ case "ruralDictionary":
+ if (!url.pathname.includes("/define.php") && !url.pathname.includes("/random.php") && url.pathname != "/")
+ return randomInstance
+ return `${randomInstance}${url.pathname}${url.search}`
+ case "anonymousOverflow": {
+ if (url.hostname == "stackoverflow.com") {
+ const threadID = /^\/a\/(\d+)\/?/.exec(url.pathname)
+ if (threadID) return `${randomInstance}/questions/${threadID[1]}${url.search}`
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ if (url.pathname == "/" || url.pathname == "") {
+ // https://stackexchange.com or https://superuser.com
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ const regex = url.href.match(/https?:\/{2}(?:([a-zA-Z0-9-]+)\.)?stackexchange\.com\//)
+ if (regex && regex.length > 1) {
+ const subdomain = regex[1]
+ return `${randomInstance}/exchange/${subdomain}${url.pathname}${url.search}`
+ }
+ const notExchangeRegex = url.hostname.match(
+ /(?:[a-zA-Z]+\.)?(?:askubuntu\.com|mathoverflow\.net|serverfault\.com|stackapps\.com|superuser\.com|stackoverflow\.com)/
+ )
+ if (notExchangeRegex) {
+ return `${randomInstance}/exchange/${notExchangeRegex[0]}${url.pathname}${url.search}`
+ }
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ case "biblioReads":
+ return `${randomInstance}${url.pathname}${url.search}`
+ case "wikiless": {
+ let hostSplit = url.host.split(".")
+ // wikiless doesn't have mobile view support yet
+ if (hostSplit[0] != "wikipedia" && hostSplit[0] != "www") {
+ if (hostSplit[0] == "m") url.searchParams.append("mobileaction", "toggle_view_mobile")
+ else url.searchParams.append("lang", hostSplit[0])
+ if (hostSplit[1] == "m") url.searchParams.append("mobileaction", "toggle_view_mobile")
+ }
+ return `${randomInstance}${url.pathname}${url.search}${url.hash}`
+ }
+ case "proxiTok":
+ if (url.pathname.startsWith("/email")) return randomInstance
+ return `${randomInstance}${url.pathname}${url.search}`
+ case "waybackClassic": {
+ const regex = /^\/\web\/(?:[0-9]+)?\*\/(.*)/.exec(url.pathname)
+ if (regex) {
+ const link = regex[1]
+ return `${randomInstance}/cgi-bin/history.cgi?utf8=✓&q=${encodeURIComponent(link)}`
+ }
+ const regex2 = /(^\/\web\/([0-9]+)\/.*)/.exec(url.pathname)
+ if (regex2) {
+ let link = regex2[1]
+ link = link.replace(regex2[2], regex2[2] + "if_")
+ return `https://web.archive.org${link}`
+ }
+ return
+ }
+ case "gothub":
+ if (url.hostname == "gist.github.com") return `${randomInstance}/gist${url.pathname}${url.search}`
+ if (url.hostname == "raw.githubusercontent.com") return `${randomInstance}/raw${url.pathname}${url.search}`
+ return `${randomInstance}${url.pathname}${url.search}`
+ case "mikuInvidious":
+ if (url.hostname == "bilibili.com" || url.hostname == "www.bilibili.com" || url.hostname == "b23.tv")
+ return `${randomInstance}${url.pathname}${url.search}`
+ if (url.hostname == "space.bilibili.com") return `${randomInstance}/space${url.pathname}${url.search}`
+ case "tent": {
+ if (url.hostname == "bandcamp.com" && url.pathname == "/search") {
+ const query = url.searchParams.get("q")
+ return `${randomInstance}/search.php?query=${encodeURIComponent(query)}`
+ }
+ if (url.hostname.endsWith("bandcamp.com")) {
+ const regex = /^(.*)\.bandcamp\.com/.exec(url.hostname)
+ const artist = regex[1]
+ if (url.pathname == "/" || url.pathname == "/music") {
+ return `${randomInstance}/artist.php?name=${artist}`
+ } else {
+ const regex = /^\/(.*)\/(.*)/.exec(url.pathname)
+ if (regex) {
+ const type = regex[1]
+ const name = regex[2]
+ return `${randomInstance}/release.php?artist=${artist}&type=${type}&name=${name}`
+ }
+ }
+ }
+ if (url.hostname == "f4.bcbits.com") {
+ const regex = /\/img\/(.*)/.exec(url.pathname)
+ const image = regex[1]
+ return `${randomInstance}/image.php?file=${image}`
+ }
+ if (url.hostname == "t4.bcbits.com") {
+ const regex = /\/stream\/(.*)\/(.*)\/(.*)/.exec(url.pathname)
+ if (regex) {
+ const directory = regex[1]
+ const format = regex[2]
+ const file = regex[3]
+ const token = url.searchParams.get("token")
+ return `${randomInstance}/audio.php/?directory=${directory}&format=${format}&file=${file}&token=${encodeURIComponent(token)}`
+ }
+ }
+ }
+ case "binternet":
+ if (url.hostname == "i.pinimg.com") return `${randomInstance}/image_proxy.php?url=${url.href}`
+ case "laboratory": {
+ let path = url.pathname
+ if (path == "/") path = ""
+ return `${randomInstance}/${url.hostname}${path}${url.search}`
+ }
+ case "quetre": {
+ const regex = /([a-z]+)\.quora\.com/.exec(url.hostname)
+ if (regex) {
+ const lang = regex[1]
+ url.searchParams.append("lang", lang)
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ case "pixivFe": {
+ const regex = /\/[a-z]{1,3}\/(.*)/.exec(url.pathname)
+ if (regex) {
+ const path = regex[1]
+ return `${randomInstance}/${path}${url.search}`
+ }
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ case "invidious": {
+ url.searchParams.delete("si")
+ if (url.hostname == "youtu.be" || (url.hostname.endsWith("youtube.com") && url.pathname.startsWith("/live"))) {
+ const watch = url.pathname.substring(url.pathname.lastIndexOf("/") + 1)
+ return `${randomInstance}/watch?v=${watch}${url.search.replace("?", "&")}`
+ }
+ if (url.hostname.endsWith("youtube.com") && url.pathname.startsWith("/redirect?")) return url.href
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ case "freetubeMusic": {
+ if (url.hostname == "youtu.be" || (url.hostname.endsWith("youtube.com") && url.pathname.startsWith("/live"))) {
+ const watch = url.pathname.substring(url.pathname.lastIndexOf("/") + 1)
+ return `freetube://youtube.com/watch?v=${watch}`
+ }
+ return "freetube://" + url.href
+ }
+ case "invidiousMusic": {
+ if (url.hostname == "youtu.be" || (url.hostname.endsWith("youtube.com") && url.pathname.startsWith("/live"))) {
+ const watch = url.pathname.substring(url.pathname.lastIndexOf("/") + 1)
+ return `${randomInstance}/watch?v=${watch}`
+ }
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ case "materialious": {
+ url.searchParams.delete("si")
+ if (url.hostname == "youtu.be" || (url.hostname.endsWith("youtube.com") && url.pathname.startsWith("/live"))) {
+ const watch = url.pathname.substring(url.pathname.lastIndexOf("/") + 1)
+ return `${randomInstance}/watch/${watch}${url.search.replace("?", "&")}`
+ }
+ if (url.hostname.endsWith("youtube.com")) {
+ if (url.pathname.startsWith("/watch")) {
+ if (url.searchParams.has("v")) {
+ const watch = url.searchParams.get("v")
+ url.searchParams.delete("v")
+ return `${randomInstance}/watch/${watch}${url.search.replace("?", "&")}`
+ }
+ return `${randomInstance}/watch/${url.search.replace("?", "&")}`
+ }
+ if (url.pathname.startsWith("/results")) {
+ if (url.searchParams.has("search_query")) {
+ const search = url.searchParams.get("search_query")
+ url.searchParams.delete("search_query")
+ return `${randomInstance}/search/${search}${url.search.replace("?", "&")}`
+ }
+ return `${randomInstance}/search/${url.search.replace("?", "&")}`
+ }
+ if (url.pathname.startsWith("/redirect?")) {
+ return url.href
+ }
+ }
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ case "libremdb": {
+ if (url.pathname.startsWith("/Name")) {
+ for (const [key, value] of url.searchParams.entries()) {
+ return `${randomInstance}/title/${encodeURIComponent(key)}`
+ }
+ }
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ case "tuboYoutube":
+ url.searchParams.delete("si")
+ if (url.pathname.startsWith("/channel")) return `${randomInstance}/channel?url=${encodeURIComponent(url.href)}`
+ if (url.pathname.startsWith("/watch")) return `${randomInstance}/stream?url=${encodeURIComponent(url.href)}`
+ return randomInstance
+ case "tuboSoundcloud":
+ if (url.pathname == "/") return `${randomInstance}?kiosk?serviceId=1`
+ if (url.pathname.match(/^\/[^\/]+(\/$|$)/)) return `${randomInstance}/channel?url=${encodeURIComponent(url.href)}`
+ if (url.pathname.match(/^\/[^\/]+\/[^\/]+/)) return `${randomInstance}/stream?url=${encodeURIComponent(url.href)}`
+ return randomInstance
+ case "twineo":
+ case "safetwitch":
+ if (url.hostname.startsWith("clips.")) return `${randomInstance}/clip${url.pathname}${url.search}`
+ return `${randomInstance}${url.pathname}${url.search}`
- case "tekstoLibre":
- return `${randomInstance}/?${url.pathname.slice(1)}`;
- case "skyview":
- if (url.pathname == '/') return randomInstance
- return `${randomInstance}?url=${encodeURIComponent(url.href)}`
- case "nitter": {
- let search = new URLSearchParams(url.search)
+ case "tekstoLibre":
+ return `${randomInstance}/?${url.pathname.slice(1)}`
+ case "skyview":
+ if (url.pathname == "/") return randomInstance
+ return `${randomInstance}?url=${encodeURIComponent(url.href)}`
+ case "nitter": {
+ let search = new URLSearchParams(url.search)
- search.delete("ref_src")
- search.delete("ref_url")
- search.delete("s") // type of device that shared the link
- search.delete("t") // some sort of tracking ID
+ search.delete("ref_src")
+ search.delete("ref_url")
+ search.delete("s") // type of device that shared the link
+ search.delete("t") // some sort of tracking ID
- search = search.toString()
- if (search !== "") search = `?${search}`
+ search = search.toString()
+ if (search !== "") search = `?${search}`
- if (url.host.split(".")[0] === "pbs" || url.host.split(".")[0] === "video") {
- try {
- const [, id, format, extra] = search.match(/(.*)\?format=(.*)&(.*)/)
- const query = encodeURIComponent(`${id}.${format}?${extra}`)
- return `${randomInstance}/pic${url.pathname}${query}`
- } catch {
- return `${randomInstance}/pic${url.pathname}${search}`
- }
- }
- if (url.pathname.split("/").includes("tweets")) return `${randomInstance}${url.pathname.replace("/tweets", "")}${search}`
- if (url.host == "t.co") return `${randomInstance}/t.co${url.pathname}`
- return `${randomInstance}${url.pathname}${search}#m`
- }
- case "priviblur": {
- if (url.hostname == "www.tumblr.com") return `${randomInstance}${url.pathname}${url.search}`
- if (url.hostname.startsWith("assets")) return `${randomInstance}/tblr/assets${url.pathname}${url.search}`
- if (url.hostname.startsWith("static")) return `${randomInstance}/tblr/static${url.pathname}${url.search}`
+ if (url.host.split(".")[0] === "pbs" || url.host.split(".")[0] === "video") {
+ try {
+ const [, id, format, extra] = search.match(/(.*)\?format=(.*)&(.*)/)
+ const query = encodeURIComponent(`${id}.${format}?${extra}`)
+ return `${randomInstance}/pic${url.pathname}${query}`
+ } catch {
+ return `${randomInstance}/pic${url.pathname}${search}`
+ }
+ }
+ if (url.pathname.split("/").includes("tweets"))
+ return `${randomInstance}${url.pathname.replace("/tweets", "")}${search}`
+ if (url.host == "t.co") return `${randomInstance}/t.co${url.pathname}`
+ return `${randomInstance}${url.pathname}${search}#m`
+ }
+ case "priviblur": {
+ if (url.hostname == "www.tumblr.com") return `${randomInstance}${url.pathname}${url.search}`
+ if (url.hostname.startsWith("assets")) return `${randomInstance}/tblr/assets${url.pathname}${url.search}`
+ if (url.hostname.startsWith("static")) return `${randomInstance}/tblr/static${url.pathname}${url.search}`
- const reg = /^([0-9]+)\.media\.tumblr\.com/.exec(url.hostname) // *.media.tumblr.com
- if (reg) return `${randomInstance}/tblr/media/${reg[1]}${url.pathname}${url.search}`
+ const reg = /^([0-9]+)\.media\.tumblr\.com/.exec(url.hostname) // *.media.tumblr.com
+ if (reg) return `${randomInstance}/tblr/media/${reg[1]}${url.pathname}${url.search}`
- const blogregex = /^(?:www\.)?([a-z\d-]+)\.tumblr\.com/.exec(url.hostname) // <blog>.tumblr.com
- if (blogregex) {
- const blog_name = blogregex[1];
- // Under the <blog>.tumblr.com domain posts are under a /post path
- if (url.pathname.startsWith("/post")) return `${randomInstance}/${blog_name}${url.pathname.slice(5)}${url.search}`
- else return `${randomInstance}/${blog_name}${url.pathname}${url.search}`;
- }
- return `${randomInstance}${url.pathname}${url.search}`;
- }
- case "piped":
- case "pipedMaterial":
- case "cloudtube":
- case "lightTube":
- case "viewtube":
- url.searchParams.delete("si")
- default:
- return `${randomInstance}${url.pathname}${url.search}`
- }
+ const blogregex = /^(?:www\.)?([a-z\d-]+)\.tumblr\.com/.exec(url.hostname) // <blog>.tumblr.com
+ if (blogregex) {
+ const blog_name = blogregex[1]
+ // Under the <blog>.tumblr.com domain posts are under a /post path
+ if (url.pathname.startsWith("/post"))
+ return `${randomInstance}/${blog_name}${url.pathname.slice(5)}${url.search}`
+ else return `${randomInstance}/${blog_name}${url.pathname}${url.search}`
+ }
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
+ case "piped":
+ case "pipedMaterial":
+ case "cloudtube":
+ case "lightTube":
+ case "viewtube":
+ url.searchParams.delete("si")
+ default:
+ return `${randomInstance}${url.pathname}${url.search}`
+ }
}
/**
@@ -527,50 +550,54 @@ function rewrite(url, frontend, randomInstance) {
* @returns {string | undefined}
*/
function redirect(url, type, initiator, forceRedirection, incognito) {
- if (type != "main_frame" && type != "sub_frame" && type != "image") return
- let randomInstance
- let frontend
- if (!forceRedirection && options.redirectOnlyInIncognito == true && !incognito) return
- for (const service in config.services) {
- if (!forceRedirection && !options[service].enabled) continue
+ if (type != "main_frame" && type != "sub_frame" && type != "image") return
+ let randomInstance
+ let frontend
+ if (!forceRedirection && options.redirectOnlyInIncognito == true && !incognito) return
+ for (const service in config.services) {
+ if (!forceRedirection && !options[service].enabled) continue
- frontend = options[service].frontend
+ frontend = options[service].frontend
- if (config.services[service].frontends[frontend].desktopApp && type != "main_frame" && options[service].redirectType != "main_frame")
- frontend = options[service].embedFrontend
+ if (
+ config.services[service].frontends[frontend].desktopApp &&
+ type != "main_frame" &&
+ options[service].redirectType != "main_frame"
+ )
+ frontend = options[service].embedFrontend
- if (!regexArray(service, url, config, frontend)) {
- frontend = null
- continue
- }
+ if (!regexArray(service, url, config, frontend)) {
+ frontend = null
+ continue
+ }
- if (
- config.services[service].embeddable
- &&
- type != options[service].redirectType && options[service].redirectType != "both"
- ) {
- if (options[service].unsupportedUrls == 'block') return 'CANCEL'
- return
- }
+ if (
+ config.services[service].embeddable &&
+ type != options[service].redirectType &&
+ options[service].redirectType != "both"
+ ) {
+ if (options[service].unsupportedUrls == "block") return "CANCEL"
+ return
+ }
- let instanceList = options[frontend]
- if (instanceList === undefined) break
- if (instanceList.length === 0) return null
+ let instanceList = options[frontend]
+ if (instanceList === undefined) break
+ if (instanceList.length === 0) return null
- if (initiator && instanceList.includes(initiator.origin)) {
- if (type != "main_frame") return null
- else return "BYPASSTAB"
- }
+ if (initiator && instanceList.includes(initiator.origin)) {
+ if (type != "main_frame") return null
+ else return "BYPASSTAB"
+ }
- randomInstance = utils.getRandomInstance(instanceList)
- if (config.services[service].frontends[frontend].localhost && options[service].instance == "localhost") {
- randomInstance = `http://${frontend}.localhost:8080`
- }
- break
- }
- if (!frontend) return
+ randomInstance = utils.getRandomInstance(instanceList)
+ if (config.services[service].frontends[frontend].localhost && options[service].instance == "localhost") {
+ randomInstance = `http://${frontend}.localhost:8080`
+ }
+ break
+ }
+ if (!frontend) return
- return rewrite(url, frontend, randomInstance)
+ return rewrite(url, frontend, randomInstance)
}
/**
@@ -578,27 +605,25 @@ function redirect(url, type, initiator, forceRedirection, incognito) {
* @param {*} returnFrontend
*/
function computeService(url, returnFrontend) {
- return new Promise(async resolve => {
- const config = await utils.getConfig()
- const options = await utils.getOptions()
- for (const service in config.services) {
- if (regexArray(service, url, config)) {
- resolve(service)
- return
- } else {
- for (const frontend in config.services[service].frontends) {
- if (all(service, frontend, options, config).includes(utils.protocolHost(url))) {
- if (returnFrontend)
- resolve([service, frontend, utils.protocolHost(url)])
- else
- resolve(service)
- return
- }
- }
- }
- }
- resolve()
- })
+ return new Promise(async resolve => {
+ const config = await utils.getConfig()
+ const options = await utils.getOptions()
+ for (const service in config.services) {
+ if (regexArray(service, url, config)) {
+ resolve(service)
+ return
+ } else {
+ for (const frontend in config.services[service].frontends) {
+ if (all(service, frontend, options, config).includes(utils.protocolHost(url))) {
+ if (returnFrontend) resolve([service, frontend, utils.protocolHost(url)])
+ else resolve(service)
+ return
+ }
+ }
+ }
+ }
+ resolve()
+ })
}
/**
@@ -606,303 +631,301 @@ function computeService(url, returnFrontend) {
* @param {string} customService
*/
function switchInstance(url, customService) {
- return new Promise(async resolve => {
- let options = await utils.getOptions()
- let config = await utils.getConfig()
+ return new Promise(async resolve => {
+ let options = await utils.getOptions()
+ let config = await utils.getConfig()
- const protocolHost = utils.protocolHost(url)
- if (customService) {
- const instancesList = options[options[customService].frontend]
- if (instancesList !== undefined) {
- const newInstance = utils.getNextInstance(url.origin, instancesList)
- if (newInstance) {
- resolve(`${newInstance}${url.pathname}${url.search}`)
- return
- }
- }
- } else {
- for (const service in config.services) {
- let instancesList = options[options[service].frontend]
- if (instancesList === undefined) continue
- if (!instancesList.includes(protocolHost)) continue
+ const protocolHost = utils.protocolHost(url)
+ if (customService) {
+ const instancesList = options[options[customService].frontend]
+ if (instancesList !== undefined) {
+ const newInstance = utils.getNextInstance(url.origin, instancesList)
+ if (newInstance) {
+ resolve(`${newInstance}${url.pathname}${url.search}`)
+ return
+ }
+ }
+ } else {
+ for (const service in config.services) {
+ let instancesList = options[options[service].frontend]
+ if (instancesList === undefined) continue
+ if (!instancesList.includes(protocolHost)) continue
- instancesList.splice(instancesList.indexOf(protocolHost), 1)
- if (instancesList.length === 0) {
- resolve()
- return
- }
- const newInstance = utils.getNextInstance(url.origin, instancesList)
- if (newInstance) {
- resolve(`${newInstance}${url.pathname}${url.search}`)
- return
- }
- }
- }
- resolve()
- })
+ instancesList.splice(instancesList.indexOf(protocolHost), 1)
+ if (instancesList.length === 0) {
+ resolve()
+ return
+ }
+ const newInstance = utils.getNextInstance(url.origin, instancesList)
+ if (newInstance) {
+ resolve(`${newInstance}${url.pathname}${url.search}`)
+ return
+ }
+ }
+ }
+ resolve()
+ })
}
/**
* @param {URL} url
*/
async function reverse(url) {
- let options = await utils.getOptions()
- let config = await utils.getConfig()
- let protocolHost = utils.protocolHost(url)
- for (const service in config.services) {
- let frontend = options[service].frontend
- if (options[frontend] == undefined) continue
- if (!options[frontend].includes(protocolHost) && protocolHost != `http://${frontend}.localhost:8080`) continue
- switch (service) {
- case "youtube":
- case "imdb":
- case "imgur":
- case "tiktok":
- case "reddit":
- case "imdb":
- case "snopes":
- case "urbanDictionary":
- case "quora":
- case "medium":
- return `${config.services[service].url}${url.pathname}${url.search}`
- case "fandom": {
- let regex = url.pathname.match(/^\/([a-zA-Z0-9-]+)\/wiki\/(.*)/)
- if (regex) return `https://${regex[1]}.fandom.com/wiki/${regex[2]}`
- return
- }
- case "wikipedia": {
- const lang = url.searchParams.get("lang")
- if (lang != null) {
- return `https://${lang}.wikipedia.org${url.pathname}${url.search}${url.hash}`
- }
- return `https://wikipedia.org${url.pathname}${url.search}${url.hash}`
- }
- case "stackOverflow": {
- if (url.pathname.startsWith("/questions/")) {
- return `https://stackoverflow.com${url.pathname}${url.search}`
- }
- if (url.pathname.startsWith("/exchange/")) {
- const regex = /\/exchange\/(.*?)(\/.*)/.exec(url.pathname)
- if (regex) return `https://${regex[1]}.stackexchange.com${regex[2]}`
- }
- return
- }
- case "tekstowo":
- return `${config.services[service].url}/${url.search.slice(1)}`
- case "goodreads":
- return `https://goodreads.com${url.pathname}${url.search}`
- default:
- return
- }
- }
- return
+ let options = await utils.getOptions()
+ let config = await utils.getConfig()
+ let protocolHost = utils.protocolHost(url)
+ for (const service in config.services) {
+ let frontend = options[service].frontend
+ if (options[frontend] == undefined) continue
+ if (!options[frontend].includes(protocolHost) && protocolHost != `http://${frontend}.localhost:8080`) continue
+ switch (service) {
+ case "youtube":
+ case "imdb":
+ case "imgur":
+ case "tiktok":
+ case "reddit":
+ case "imdb":
+ case "snopes":
+ case "urbanDictionary":
+ case "quora":
+ case "medium":
+ return `${config.services[service].url}${url.pathname}${url.search}`
+ case "fandom": {
+ let regex = url.pathname.match(/^\/([a-zA-Z0-9-]+)\/wiki\/(.*)/)
+ if (regex) return `https://${regex[1]}.fandom.com/wiki/${regex[2]}`
+ return
+ }
+ case "wikipedia": {
+ const lang = url.searchParams.get("lang")
+ if (lang != null) {
+ return `https://${lang}.wikipedia.org${url.pathname}${url.search}${url.hash}`
+ }
+ return `https://wikipedia.org${url.pathname}${url.search}${url.hash}`
+ }
+ case "stackOverflow": {
+ if (url.pathname.startsWith("/questions/")) {
+ return `https://stackoverflow.com${url.pathname}${url.search}`
+ }
+ if (url.pathname.startsWith("/exchange/")) {
+ const regex = /\/exchange\/(.*?)(\/.*)/.exec(url.pathname)
+ if (regex) return `https://${regex[1]}.stackexchange.com${regex[2]}`
+ }
+ return
+ }
+ case "tekstowo":
+ return `${config.services[service].url}/${url.search.slice(1)}`
+ case "goodreads":
+ return `https://goodreads.com${url.pathname}${url.search}`
+ default:
+ return
+ }
+ }
+ return
}
const defaultInstances = {
- 'invidious': ['https://inv.vern.cc'],
- 'materialious': ['https://app.materialio.us'],
- 'viewtube': ['https://viewtube.io'],
- 'piped': ['https://pipedapi-libre.kavin.rocks'],
- 'pipedMaterial': ['https://piped-material.xn--17b.net'],
- 'cloudtube': ['https://tube.cadence.moe'],
- 'lightTube': ['https://tube.kuylar.dev'],
- 'poketube': ['https://poketube.fun'],
- 'proxiTok': ['https://proxitok.pabloferreiro.es'],
- 'redlib': ['https://safereddit.com'],
- 'libreddit': ['https://libreddit.spike.codes'],
- 'teddit': ['https://teddit.net'],
- 'scribe': ['https://scribe.rip'],
- 'libMedium': ['https://md.vern.cc'],
- 'quetre': ['https://quetre.iket.me'],
- 'libremdb': ['https://libremdb.iket.me'],
- 'simplyTranslate': ['https://simplytranslate.org'],
- 'mozhi': ['https://mozhi.aryak.me'],
- 'searxng': ['https://search.bus-hit.me'],
- '4get': ['https://4get.ca'],
- 'rimgo': ['https://rimgo.vern.cc'],
- 'hyperpipe': ['https://hyperpipe.surge.sh'],
- 'osm': ['https://www.openstreetmap.org'],
- 'breezeWiki': ['https://breezewiki.com'],
- 'neuters': ['https://neuters.de'],
- 'dumb': ['https://dm.vern.cc'],
- "intellectual": ['https://intellectual.insprill.net'],
- 'ruralDictionary': ['https://rd.vern.cc'],
- 'anonymousOverflow': ['https://code.whatever.social'],
- 'biblioReads': ['https://biblioreads.ml'],
- 'wikiless': ['https://wikiless.org'],
- 'suds': ['https://sd.vern.cc'],
- 'unfunny': ['https://uf.vern.cc'],
- 'soprano': ['https://sp.vern.cc'],
- 'meme': ['https://mm.vern.cc'],
- 'waybackClassic': ['https://wayback-classic.net'],
- 'gothub': ['https://gh.odyssey346.dev'],
- 'mikuInvidious': ['https://mikuinv.resrv.org'],
- "tent": ['https://tent.sny.sh'],
- "wolfreeAlpha": ['https://gqq.gitlab.io', 'https://uqq.gitlab.io'],
- "laboratory": ['https://lab.vern.cc'],
- 'binternet': ['https://bn.bloat.cat'],
- 'pixivFe': ['https://pixivfe.exozy.me'],
- 'indestructables': ['https://indestructables.private.coffee'],
- 'destructables': ['https://ds.vern.cc'],
- 'safetwitch': ['https://safetwitch.drgns.space'],
- 'twineo': ['https://twineo.exozy.me'],
- 'proxigram': ['https://ig.opnxng.com'],
- 'tuboYoutube': ['https://tubo.migalmoreno.com'],
- 'tuboSoundcloud': ['https://tubo.migalmoreno.com'],
- 'tekstoLibre': ['https://davilarek.github.io/TekstoLibre'],
- 'skyview': ['https://skyview.social'],
- 'priviblur': ['https://pb.bloat.cat'],
- 'nitter': ['https://nitter.privacydev.net'],
- 'pasted': ['https://pasted.drakeerv.com'],
+ invidious: ["https://inv.vern.cc"],
+ materialious: ["https://app.materialio.us"],
+ viewtube: ["https://viewtube.io"],
+ piped: ["https://pipedapi-libre.kavin.rocks"],
+ pipedMaterial: ["https://piped-material.xn--17b.net"],
+ cloudtube: ["https://tube.cadence.moe"],
+ lightTube: ["https://tube.kuylar.dev"],
+ poketube: ["https://poketube.fun"],
+ proxiTok: ["https://proxitok.pabloferreiro.es"],
+ redlib: ["https://safereddit.com"],
+ libreddit: ["https://libreddit.spike.codes"],
+ teddit: ["https://teddit.net"],
+ scribe: ["https://scribe.rip"],
+ libMedium: ["https://md.vern.cc"],
+ quetre: ["https://quetre.iket.me"],
+ libremdb: ["https://libremdb.iket.me"],
+ simplyTranslate: ["https://simplytranslate.org"],
+ mozhi: ["https://mozhi.aryak.me"],
+ searxng: ["https://search.bus-hit.me"],
+ "4get": ["https://4get.ca"],
+ rimgo: ["https://rimgo.vern.cc"],
+ hyperpipe: ["https://hyperpipe.surge.sh"],
+ osm: ["https://www.openstreetmap.org"],
+ breezeWiki: ["https://breezewiki.com"],
+ neuters: ["https://neuters.de"],
+ dumb: ["https://dm.vern.cc"],
+ intellectual: ["https://intellectual.insprill.net"],
+ ruralDictionary: ["https://rd.vern.cc"],
+ anonymousOverflow: ["https://code.whatever.social"],
+ biblioReads: ["https://biblioreads.ml"],
+ wikiless: ["https://wikiless.org"],
+ suds: ["https://sd.vern.cc"],
+ unfunny: ["https://uf.vern.cc"],
+ soprano: ["https://sp.vern.cc"],
+ meme: ["https://mm.vern.cc"],
+ waybackClassic: ["https://wayback-classic.net"],
+ gothub: ["https://gh.odyssey346.dev"],
+ mikuInvidious: ["https://mikuinv.resrv.org"],
+ tent: ["https://tent.sny.sh"],
+ wolfreeAlpha: ["https://gqq.gitlab.io", "https://uqq.gitlab.io"],
+ laboratory: ["https://lab.vern.cc"],
+ binternet: ["https://bn.bloat.cat"],
+ pixivFe: ["https://pixivfe.exozy.me"],
+ indestructables: ["https://indestructables.private.coffee"],
+ destructables: ["https://ds.vern.cc"],
+ safetwitch: ["https://safetwitch.drgns.space"],
+ twineo: ["https://twineo.exozy.me"],
+ proxigram: ["https://ig.opnxng.com"],
+ tuboYoutube: ["https://tubo.migalmoreno.com"],
+ tuboSoundcloud: ["https://tubo.migalmoreno.com"],
+ tekstoLibre: ["https://davilarek.github.io/TekstoLibre"],
+ skyview: ["https://skyview.social"],
+ priviblur: ["https://pb.bloat.cat"],
+ nitter: ["https://nitter.privacydev.net"],
+ pasted: ["https://pasted.drakeerv.com"],
}
function initDefaults() {
- return new Promise(resolve => {
- browser.storage.local.clear(async () => {
- let config = await utils.getConfig()
- let options = {}
- for (const service in config.services) {
- options[service] = {}
- for (const defaultOption in config.services[service].options) {
- options[service][defaultOption] = config.services[service].options[defaultOption]
- }
- for (const frontend in config.services[service].frontends) {
- if (config.services[service].frontends[frontend].instanceList) {
- options[frontend] = []
- }
- }
- }
- options.exceptions = {
- url: [],
- regex: [],
- }
- options.theme = "detect"
- options.popupServices = ["youtube", "tiktok", "imgur", "reddit", "quora", "translate", "maps"]
- options.fetchInstances = 'github'
- options.redirectOnlyInIncognito = false
+ return new Promise(resolve => {
+ browser.storage.local.clear(async () => {
+ let config = await utils.getConfig()
+ let options = {}
+ for (const service in config.services) {
+ options[service] = {}
+ for (const defaultOption in config.services[service].options) {
+ options[service][defaultOption] = config.services[service].options[defaultOption]
+ }
+ for (const frontend in config.services[service].frontends) {
+ if (config.services[service].frontends[frontend].instanceList) {
+ options[frontend] = []
+ }
+ }
+ }
+ options.exceptions = {
+ url: [],
+ regex: [],
+ }
+ options.theme = "detect"
+ options.popupServices = ["youtube", "tiktok", "imgur", "reddit", "quora", "translate", "maps"]
+ options.fetchInstances = "github"
+ options.redirectOnlyInIncognito = false
- options = { ...options, ...defaultInstances }
+ options = { ...options, ...defaultInstances }
- browser.storage.local.set({ options },
- () => resolve()
- )
- })
- })
+ browser.storage.local.set({ options }, () => resolve())
+ })
+ })
}
function upgradeOptions() {
- return new Promise(async resolve => {
- let options = await utils.getOptions()
+ return new Promise(async resolve => {
+ let options = await utils.getOptions()
- browser.storage.local.clear(() => {
- browser.storage.local.set({ options }, () => {
- resolve()
- })
- })
- })
+ browser.storage.local.clear(() => {
+ browser.storage.local.set({ options }, () => {
+ resolve()
+ })
+ })
+ })
}
function processUpdate() {
- return new Promise(async resolve => {
- let frontends = []
- const config = await utils.getConfig()
- let options = await utils.getOptions()
- for (const service in config.services) {
- if (!options[service]) options[service] = {}
+ return new Promise(async resolve => {
+ let frontends = []
+ const config = await utils.getConfig()
+ let options = await utils.getOptions()
+ for (const service in config.services) {
+ if (!options[service]) options[service] = {}
- if (!(options[service].frontend in config.services[service].frontends)) {
- options[service] = config.services[service].options // Reset settings for service
- delete options[options[service].frontend] // Remove deprecated frontend
- }
+ if (!(options[service].frontend in config.services[service].frontends)) {
+ options[service] = config.services[service].options // Reset settings for service
+ delete options[options[service].frontend] // Remove deprecated frontend
+ }
- for (const defaultOption in config.services[service].options) {
- if (!(defaultOption in options[service])) {
- options[service][defaultOption] = config.services[service].options[defaultOption]
- }
- }
+ for (const defaultOption in config.services[service].options) {
+ if (!(defaultOption in options[service])) {
+ options[service][defaultOption] = config.services[service].options[defaultOption]
+ }
+ }
- for (const frontend in config.services[service].frontends) {
- frontends.push(frontend)
- if (!(frontend in options) && config.services[service].frontends[frontend].instanceList) {
- options[frontend] = defaultInstances[frontend] || []
- }
- }
+ for (const frontend in config.services[service].frontends) {
+ frontends.push(frontend)
+ if (!(frontend in options) && config.services[service].frontends[frontend].instanceList) {
+ options[frontend] = defaultInstances[frontend] || []
+ }
+ }
- for (const frontend of options.popupServices) {
- if (!Object.keys(config.services).includes(frontend)) {
- const i = options.popupServices.indexOf(frontend);
- if (i > -1) options.popupServices.splice(i, 1);
- }
- }
- }
- const general = ['theme', 'popupServices', 'fetchInstances', 'redirectOnlyInIncognito']
- const combined = [
- ...Object.keys(config.services),
- ...frontends,
- ...general,
- 'exceptions',
- 'popupServices',
- 'version',
- ]
- for (const key in options) {
- if (combined.indexOf(key) < 0) {
- delete options[key] // Remove any unknown settings in options
- }
- }
- browser.storage.local.set({ options }, () => {
- resolve()
- })
- })
+ for (const frontend of options.popupServices) {
+ if (!Object.keys(config.services).includes(frontend)) {
+ const i = options.popupServices.indexOf(frontend)
+ if (i > -1) options.popupServices.splice(i, 1)
+ }
+ }
+ }
+ const general = ["theme", "popupServices", "fetchInstances", "redirectOnlyInIncognito"]
+ const combined = [
+ ...Object.keys(config.services),
+ ...frontends,
+ ...general,
+ "exceptions",
+ "popupServices",
+ "version",
+ ]
+ for (const key in options) {
+ if (combined.indexOf(key) < 0) {
+ delete options[key] // Remove any unknown settings in options
+ }
+ }
+ browser.storage.local.set({ options }, () => {
+ resolve()
+ })
+ })
}
/**
* @param {URL} url
*/
async function copyRaw(url) {
- const newUrl = await reverse(url)
- if (newUrl) {
- if (!isChrome) {
- navigator.clipboard.writeText(newUrl)
- } else {
- var copyFrom = document.createElement("textarea");
- copyFrom.textContent = newUrl;
- document.body.appendChild(copyFrom);
- copyFrom.select()
- document.execCommand('copy')
- copyFrom.blur();
- document.body.removeChild(copyFrom);
- }
- }
+ const newUrl = await reverse(url)
+ if (newUrl) {
+ if (!isChrome) {
+ navigator.clipboard.writeText(newUrl)
+ } else {
+ var copyFrom = document.createElement("textarea")
+ copyFrom.textContent = newUrl
+ document.body.appendChild(copyFrom)
+ copyFrom.select()
+ document.execCommand("copy")
+ copyFrom.blur()
+ document.body.removeChild(copyFrom)
+ }
+ }
}
/**
* @param {URL} url
*/
function isException(url) {
- if (!options.exceptions) return false
- let exceptions = options.exceptions
- if (exceptions && url) {
- if (exceptions.url) {
- for (let item of exceptions.url) {
- item = new URL(item)
- item = item.href.replace(/^http:\/\//, 'https://')
- if (item == url.href) return true
- }
- }
- if (exceptions.regex) for (const item of exceptions.regex) if (new RegExp(item).test(url.href)) return true
- }
- return false
+ if (!options.exceptions) return false
+ let exceptions = options.exceptions
+ if (exceptions && url) {
+ if (exceptions.url) {
+ for (let item of exceptions.url) {
+ item = new URL(item)
+ item = item.href.replace(/^http:\/\//, "https://")
+ if (item == url.href) return true
+ }
+ }
+ if (exceptions.regex) for (const item of exceptions.regex) if (new RegExp(item).test(url.href)) return true
+ }
+ return false
}
export default {
- redirect,
- redirectAsync,
- computeService,
- reverse,
- initDefaults,
- upgradeOptions,
- processUpdate,
- copyRaw,
- switchInstance,
- isException
+ redirect,
+ redirectAsync,
+ computeService,
+ reverse,
+ initDefaults,
+ upgradeOptions,
+ processUpdate,
+ copyRaw,
+ switchInstance,
+ isException,
}
diff --git a/src/assets/javascripts/utils.js b/src/assets/javascripts/utils.js
index d28f9701..439826dd 100644
--- a/src/assets/javascripts/utils.js
+++ b/src/assets/javascripts/utils.js
@@ -1,11 +1,11 @@
window.browser = window.browser || window.chrome
/**
- * @param {Array.<T>} instances
+ * @param {Array.<T>} instances
* @returns {T}
*/
function getRandomInstance(instances) {
- return instances[~~(instances.length * Math.random())]
+ return instances[~~(instances.length * Math.random())]
}
/**
@@ -14,27 +14,28 @@ function getRandomInstance(instances) {
* @returns {T}
*/
function getNextInstance(currentInstanceUrl, instances) {
- const currentInstanceIndex = instances.indexOf(currentInstanceUrl);
- if (currentInstanceIndex === -1) return getRandomInstance(instances);
- const nextInstanceIndex = (currentInstanceIndex + 1) % instances.length;
- return instances[nextInstanceIndex];
+ const currentInstanceIndex = instances.indexOf(currentInstanceUrl)
+ if (currentInstanceIndex === -1) return getRandomInstance(instances)
+ const nextInstanceIndex = (currentInstanceIndex + 1) % instances.length
+ return instances[nextInstanceIndex]
}
/**
* @param {string} str
*/
function camelCase(str) {
- return str.charAt(0).toUpperCase() + str.slice(1)
+ return str.charAt(0).toUpperCase() + str.slice(1)
}
/**
* @param {URL} url
*/
function protocolHost(url) {
- if (url.username && url.password) return `${url.protocol}//${url.username}:${url.password}@${url.host}`
- if (url.pathname == "/TekstoLibre/" && url.host.endsWith("github.io")) // workaround
- return `${url.protocol}//${url.host}${url.pathname.slice(0, -1)}`
- return `${url.protocol}//${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
+ 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>}
*/
function getConfig() {
- return new Promise(resolve => {
- fetch("/config.json")
- .then(response => response.text())
- .then(json => {
- resolve(JSON.parse(json))
- return
- })
- })
+ return new Promise(resolve => {
+ fetch("/config.json")
+ .then(response => response.text())
+ .then(json => {
+ resolve(JSON.parse(json))
+ return
+ })
+ })
}
/**
@@ -78,106 +79,108 @@ function getConfig() {
* @returns {Promise<Object.<string, Option | string[]>>}
*/
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() {
- 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) {
- return new Promise(resolve => {
- let url
- if (options.fetchInstances == 'github') url = 'https://raw.githubusercontent.com/libredirect/instances/main/blacklist.json'
- else if (options.fetchInstances == 'codeberg') url = 'https://codeberg.org/LibRedirect/instances/raw/branch/main/blacklist.json'
- else return resolve('disabled')
- const http = new XMLHttpRequest()
- http.open("GET", url, true)
- http.onreadystatechange = () => {
- if (http.status === 200 && http.readyState == XMLHttpRequest.DONE)
- resolve(JSON.parse(http.responseText))
- }
- http.onerror = () => resolve()
- http.ontimeout = () => resolve()
- http.send(null)
- })
+ return new Promise(resolve => {
+ let url
+ if (options.fetchInstances == "github")
+ url = "https://raw.githubusercontent.com/libredirect/instances/main/blacklist.json"
+ else if (options.fetchInstances == "codeberg")
+ url = "https://codeberg.org/LibRedirect/instances/raw/branch/main/blacklist.json"
+ else return resolve("disabled")
+ const http = new XMLHttpRequest()
+ http.open("GET", url, true)
+ http.onreadystatechange = () => {
+ if (http.status === 200 && http.readyState == XMLHttpRequest.DONE) resolve(JSON.parse(http.responseText))
+ }
+ http.onerror = () => resolve()
+ http.ontimeout = () => resolve()
+ http.send(null)
+ })
}
function getList(options) {
- return new Promise(resolve => {
- let url
- if (options.fetchInstances == 'github') url = 'https://raw.githubusercontent.com/libredirect/instances/main/data.json'
- else if (options.fetchInstances == 'codeberg') url = 'https://codeberg.org/LibRedirect/instances/raw/branch/main/data.json'
- else return resolve('disabled')
- const http = new XMLHttpRequest()
- http.open("GET", url, true)
- http.onreadystatechange = () => {
- if (http.status === 200 && http.readyState == XMLHttpRequest.DONE)
- return resolve(JSON.parse(http.responseText))
- }
- http.onerror = () => resolve()
- http.ontimeout = () => resolve()
- http.send(null)
- })
+ return new Promise(resolve => {
+ let url
+ if (options.fetchInstances == "github")
+ url = "https://raw.githubusercontent.com/libredirect/instances/main/data.json"
+ else if (options.fetchInstances == "codeberg")
+ url = "https://codeberg.org/LibRedirect/instances/raw/branch/main/data.json"
+ else return resolve("disabled")
+ const http = new XMLHttpRequest()
+ http.open("GET", url, true)
+ http.onreadystatechange = () => {
+ if (http.status === 200 && http.readyState == XMLHttpRequest.DONE) return resolve(JSON.parse(http.responseText))
+ }
+ http.onerror = () => resolve()
+ http.ontimeout = () => resolve()
+ http.send(null)
+ })
}
/**
* @param {string} href
*/
function pingOnce(href) {
- return new Promise(async resolve => {
- let started
- let http = new XMLHttpRequest()
- http.timeout = 5000
- http.ontimeout = () => resolve(5000)
- http.onerror = () => resolve()
- http.onreadystatechange = () => {
- if (http.readyState == 2) {
- if (http.status == 200) {
- let ended = new Date().getTime()
- http.abort()
- resolve(ended - started)
- } else {
- resolve(5000 + http.status)
- }
- }
- }
- http.open("GET", `${href}?_=${new Date().getTime()}`, true)
- started = new Date().getTime()
- http.send(null)
- })
+ return new Promise(async resolve => {
+ let started
+ let http = new XMLHttpRequest()
+ http.timeout = 5000
+ http.ontimeout = () => resolve(5000)
+ http.onerror = () => resolve()
+ http.onreadystatechange = () => {
+ if (http.readyState == 2) {
+ if (http.status == 200) {
+ let ended = new Date().getTime()
+ http.abort()
+ resolve(ended - started)
+ } else {
+ resolve(5000 + http.status)
+ }
+ }
+ }
+ http.open("GET", `${href}?_=${new Date().getTime()}`, true)
+ started = new Date().getTime()
+ http.send(null)
+ })
}
/**
* @param {string} href
*/
function ping(href) {
- return new Promise(async resolve => {
- let average = 0
- let time
- for (let i = 0; i < 3; i++) {
- time = await pingOnce(href)
- if (i == 0) continue
- if (time >= 5000) {
- resolve(time)
- return
- }
- average += time
- }
- average = parseInt(average / 3)
- resolve(average)
- })
+ return new Promise(async resolve => {
+ let average = 0
+ let time
+ for (let i = 0; i < 3; i++) {
+ time = await pingOnce(href)
+ if (i == 0) continue
+ if (time >= 5000) {
+ resolve(time)
+ return
+ }
+ average += time
+ }
+ average = parseInt(average / 3)
+ resolve(average)
+ })
}
export default {
- getRandomInstance,
- getNextInstance,
- protocolHost,
- getList,
- getBlacklist,
- camelCase,
- getConfig,
- getOptions,
- getPingCache,
- ping,
+ getRandomInstance,
+ getNextInstance,
+ protocolHost,
+ getList,
+ getBlacklist,
+ camelCase,
+ getConfig,
+ getOptions,
+ getPingCache,
+ ping,
}
diff --git a/src/config.json b/src/config.json
index 76e4b022..6375357f 100644
--- a/src/config.json
+++ b/src/config.json
@@ -1,1124 +1,1036 @@
{
- "networks": {
- "clearnet": {
- "tld": "org",
- "name": "Clearnet"
- },
- "tor": {
- "tld": "onion",
- "name": "Tor"
- },
- "i2p": {
- "tld": "i2p",
- "name": "I2P"
- },
- "loki": {
- "tld": "loki",
- "name": "Lokinet"
- }
- },
- "services": {
- "youtube": {
- "frontends": {
- "invidious": {
- "name": "Invidious",
- "embeddable": true,
- "instanceList": true,
- "url": "https://invidious.io/"
- },
- "materialious": {
- "name": "Materialious",
- "embeddable": true,
- "instanceList": true,
- "url": "https://materialio.us/"
- },
- "piped": {
- "excludeTargets": [
- 2,
- 3
- ],
- "name": "Piped",
- "embeddable": true,
- "instanceList": true,
- "url": "https://github.com/TeamPiped/Piped"
- },
- "pipedMaterial": {
- "excludeTargets": [
- 2,
- 3
- ],
- "name": "Piped-Material",
- "embeddable": false,
- "instanceList": true,
- "url": "https://github.com/mmjee/Piped-Material"
- },
- "poketube": {
- "excludeTargets": [
- 2,
- 3
- ],
- "name": "PokeTube",
- "embeddable": true,
- "instanceList": true,
- "url": "https://codeberg.org/Ashley/poketube"
- },
- "cloudtube": {
- "name": "CloudTube",
- "embeddable": false,
- "instanceList": true,
- "url": "https://sr.ht/~cadence/tube",
- "excludeTargets": [
- 2,
- 3
- ]
- },
- "lightTube": {
- "name": "LightTube",
- "embeddable": false,
- "instanceList": true,
- "url": "https://github.com/lighttube-org/LightTube"
- },
- "tuboYoutube": {
- "name": "Tubo",
- "embeddable": false,
- "instanceList": true,
- "url": "https://git.migalmoreno.com/tubo/about/",
- "excludeTargets": [
- 2,
- 3
- ]
- },
- "freetube": {
- "excludeTargets": [
- 2,
- 3
- ],
- "name": "FreeTube",
- "embeddable": false,
- "desktopApp": true,
- "instanceList": false,
- "url": "https://github.com/FreeTubeApp/FreeTube"
- },
- "yattee": {
- "excludeTargets": [
- 2,
- 3
- ],
- "name": "Yattee",
- "embeddable": false,
- "desktopApp": true,
- "instanceList": false,
- "url": "https://github.com/yattee/yattee"
- },
- "freetubePwa": {
- "excludeTargets": [
- 2,
- 3
- ],
- "name": "FreeTube PWA",
- "embeddable": false,
- "instanceList": false,
- "url": "https://github.com/MarmadileManteater/FreeTubeCordova"
- },
- "viewtube": {
- "name": "ViewTube",
- "embeddable": false,
- "instanceList": true,
- "url": "https://github.com/ViewTube/viewtube"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.|m\\.)?youtube.com(\\/|$)(?!iframe_api\\/|redirect\\/)",
- "^https?:\\/{2}img\\.youtube.com\\/vi\\/.*\\/..*",
- "^https?:\\/{2}(i|s)\\.ytimg.com\\/vi\\/.*\\/..*",
- "^https?:\\/{2}(www\\.)?youtube.com\\/watch?v=..*",
- "^https?:\\/{2}(www\\.)?youtu\\.be\\/..*",
- "^https?:\\/{2}(www\\.)?(youtube|youtube-nocookie)\\.com\\/embed\\/..*"
- ],
- "name": "YouTube",
- "options": {
- "enabled": false,
- "redirectType": "main_frame",
- "frontend": "invidious",
- "embedFrontend": "invidious",
- "unsupportedUrls": "bypass"
- },
- "imageType": "png",
- "embeddable": true,
- "url": "https://youtube.com"
- },
- "youtubeMusic": {
- "frontends": {
- "hyperpipe": {
- "name": "Hyperpipe",
- "instanceList": true,
- "url": "https://codeberg.org/Hyperpipe/Hyperpipe"
- },
- "invidiousMusic": {
- "name": "Invidious",
- "embeddable": true,
- "instanceList": true,
- "url": "https://invidious.io/"
- },
- "freetubeMusic": {
- "name": "FreeTube",
- "embeddable": false,
- "desktopApp": true,
- "instanceList": false,
- "url": "https://github.com/FreeTubeApp/FreeTube"
- }
- },
- "targets": [
- "^https?:\\/{2}music\\.youtube\\.com\\/"
- ],
- "name": "YT Music",
- "options": {
- "enabled": false,
- "frontend": "hyperpipe",
- "unsupportedUrls": "bypass"
- },
- "imageType": "png",
- "url": "https://music.youtube.com"
- },
- "twitter": {
- "frontends": {
- "nitter": {
- "name": "Nitter",
- "embeddable": true,
- "instanceList": true,
- "url": "https://github.com/zedeus/nitter",
- "localhost": true
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.|mobile\\.)?twitter\\.com\\/",
- "^https?:\\/{2}(www\\.|mobile\\.)?x\\.com\\/",
- "^https?:\\/{2}(pbs\\.|video\\.)twimg\\.com\\/",
- "^https?:\\/{2}platform\\.x\\.com/embed\\/",
- "^https?:\\/{2}platform\\.twitter\\.com/embed\\/",
- "^https?:\\/{2}t\\.co\\/"
- ],
- "name": "Twitter",
- "options": {
- "enabled": false,
- "redirectType": "main_frame",
- "unsupportedUrls": "bypass",
- "frontend": "nitter",
- "instance": "public"
- },
- "imageType": "png",
- "embeddable": true,
- "url": "https://twitter.com"
- },
- "bluesky": {
- "frontends": {
- "skyview": {
- "name": "Skyview",
- "instanceList": true,
- "url": "https://github.com/badlogic/skyview"
- }
- },
- "targets": [
- "^https?:\\/{2}bsky\\.app\\/"
- ],
- "name": "Bluesky",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "skyview"
- },
- "imageType": "svg",
- "url": "https://bsky.app/"
- },
- "reddit": {
- "frontends": {
- "libreddit": {
- "name": "Libreddit",
- "instanceList": true,
- "url": "https://github.com/spikecodes/libreddit",
- "localhost": true
- },
- "redlib": {
- "name": "Redlib",
- "instanceList": true,
- "url": "https://github.com/redlib-org/redlib",
- "localhost": true
- },
- "teddit": {
- "name": "Teddit",
- "instanceList": true,
- "url": "https://codeberg.org/teddit/teddit",
- "localhost": true
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.|old\\.|np\\.|new\\.|amp\\.)?(reddit|reddittorjg6rue252oqsxryoxengawnmo46qy4kyii5wtqnwfj4ooad)\\.(com|onion)(?=\\/u(ser)?\\/|\\/r\\/|\\/search|\\/new|\\/?$)",
- "^https?:\\/{2}(i|(external-)?preview)\\.redd\\.it"
- ],
- "name": "Reddit",
- "options": {
- "enabled": false,
- "frontend": "libreddit",
- "unsupportedUrls": "bypass",
- "instance": "public"
- },
- "imageType": "png",
- "url": "https://reddit.com"
- },
- "tumblr": {
- "frontends": {
- "priviblur": {
- "name": "Priviblur",
- "embeddable": true,
- "instanceList": true,
- "url": "https://github.com/syeopite/priviblur",
- "localhost": true
- }
- },
- "targets": [
- "^https?:\\/{2}(media\\.|assets\\.|static\\.)?tumblr\\.com\\/",
- "^https?:\\/{2}[0-9]+\\.media\\.tumblr\\.com\\/",
- "^https?:\\/{2}(www\\.)?(.*)\\.tumblr.com\\/"
- ],
- "name": "Tumblr",
- "options": {
- "enabled": false,
- "redirectType": "main_frame",
- "unsupportedUrls": "bypass",
- "frontend": "priviblur",
- "instance": "public"
- },
- "imageType": "svg",
- "embeddable": true,
- "url": "https://tumblr.com"
- },
- "twitch": {
- "frontends": {
- "safetwitch": {
- "name": "SafeTwitch",
- "embeddable": true,
- "instanceList": true,
- "url": "https://codeberg.org/dragongoose/safetwitch",
- "localhost": false
- },
- "twineo": {
- "name": "Twineo",
- "embeddable": true,
- "instanceList": true,
- "url": "https://codeberg.org/CloudyyUw/twineo",
- "localhost": false
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.|clips\\.)?twitch\\.(tv|com)\\/"
- ],
- "name": "Twitch",
- "options": {
- "enabled": false,
- "redirectType": "main_frame",
- "unsupportedUrls": "bypass",
- "frontend": "safetwitch",
- "instance": "public"
- },
- "imageType": "svg",
- "embeddable": true,
- "url": "https://twitch.tv"
- },
- "tiktok": {
- "frontends": {
- "proxiTok": {
- "name": "ProxiTok",
- "instanceList": true,
- "url": "https://github.com/pablouser1/ProxiTok",
- "localhost": true
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?tiktok\\.com\\/"
- ],
- "name": "TikTok",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "proxiTok",
- "instance": "public"
- },
- "imageType": "png",
- "url": "https://tiktok.com"
- },
- "instagram": {
- "frontends": {
- "proxigram": {
- "name": "Proxigram",
- "instanceList": true,
- "url": "https://codeberg.org/ThePenguinDev/Proxigram",
- "localhost": false
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?instagram\\.com"
- ],
- "name": "Instagram",
- "options": {
- "enabled": false,
- "frontend": "proxigram",
- "unsupportedUrls": "bypass",
- "instance": "public"
- },
- "imageType": "png",
- "url": "https://www.instagram.com"
- },
- "imdb": {
- "frontends": {
- "libremdb": {
- "name": "libremdb",
- "instanceList": true,
- "url": "https://github.com/zyachel/libremdb",
- "localhost": true
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.|m\\.)?imdb\\.com"
- ],
- "name": "IMDb",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "libremdb",
- "instance": "public"
- },
- "imageType": "svg",
- "url": "https://imdb.com"
- },
- "bilibili": {
- "frontends": {
- "mikuInvidious": {
- "name": "MikuInvidious",
- "instanceList": true,
- "url": "https://0xacab.org/johnxina/mikuinvidious"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.|space\\.)?bilibili\\.com\\/",
- "^https?:\\/{2}b23\\.tv\\/"
- ],
- "name": "Bilibili",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "mikuInvidious"
- },
- "imageType": "svgMono",
- "url": "https://bilibili.com/"
- },
- "pixiv": {
- "name": "Pixiv",
- "frontends": {
- "pixivFe": {
- "name": "PixivFE",
- "instanceList": true,
- "url": "https://codeberg.org/VnPower/pixivfe"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?pixiv\\.net\\/"
- ],
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "pixivFe"
- },
- "imageType": "svg",
- "url": "https://www.pixiv.net"
- },
- "fandom": {
- "frontends": {
- "breezeWiki": {
- "name": "BreezeWiki",
- "instanceList": true,
- "url": "https://breezewiki.com"
- }
- },
- "targets": [
- "^https?:\\/{2}([a-zA-Z0-9-]+\\.)?(fandom|wikia)\\.com(?=\\/wiki|\\/?$)"
- ],
- "name": "Fandom",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "instance": "public",
- "frontend": "breezeWiki"
- },
- "imageType": "svg",
- "url": "https://fandom.com"
- },
- "imgur": {
- "frontends": {
- "rimgo": {
- "name": "rimgo",
- "instanceList": true,
- "url": "https://codeberg.org/video-prize-ranch/rimgo",
- "localhost": true,
- "embeddable": true
- }
- },
- "targets": [
- "^https?:\\/{2}([im]\\.)?(stack\\.)?imgur\\.(com|io)\\/"
- ],
- "name": "Imgur",
- "options": {
- "enabled": false,
- "redirectType": "main_frame",
- "unsupportedUrls": "bypass",
- "frontend": "rimgo",
- "instance": "public"
- },
- "imageType": "png",
- "embeddable": true,
- "url": "https://imgur.com"
- },
- "pinterest": {
- "name": "Pinterest",
- "frontends": {
- "binternet": {
- "name": "Binternet",
- "instanceList": true,
- "url": "https://github.com/Ahwxorg/Binternet",
- "embeddable": true
- }
- },
- "targets": [
- "^https?:\\/{2}i\\.pinimg\\.com",
- "^https?:\\/{2}(www\\.)?pinterest\\.com"
- ],
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "redirectType": "main_frame",
- "frontend": "binternet"
- },
- "imageType": "svg",
- "embeddable": true,
- "url": "https://pinterest.com"
- },
- "soundcloud": {
- "frontends": {
- "tuboSoundcloud": {
- "name": "Tubo",
- "embeddable": false,
- "instanceList": true,
- "url": "https://git.migalmoreno.com/tubo/about/"
- }
- },
- "targets": [
- "^https?:\\/{2}soundcloud\\.com"
- ],
- "name": "SoundCloud",
- "options": {
- "enabled": false,
- "redirectType": "main_frame",
- "frontend": "tuboSoundcloud",
- "unsupportedUrls": "bypass"
- },
- "imageType": "svg",
- "embeddable": false,
- "url": "https://soundcloud.com"
- },
- "bandcamp": {
- "frontends": {
- "tent": {
- "name": "Tent",
- "instanceList": true,
- "url": "https://forgejo.sny.sh/sun/Tent"
- }
- },
- "targets": [
- "^https?:\\/{2}(.*\\.)?bandcamp\\.com\\/",
- "^https?:\\/{2}(f4|t4)\\.bcbits\\.com\\/"
- ],
- "name": "Bandcamp",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "tent"
- },
- "imageType": "svg",
- "url": "https://bandcamp.com"
- },
- "tekstowo": {
- "frontends": {
- "tekstoLibre": {
- "name": "TekstoLibre",
- "instanceList": true,
- "url": "https://github.com/Davilarek/TekstoLibre"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?tekstowo\\.pl\\/"
- ],
- "name": "Tekstowo.pl",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "tekstoLibre"
- },
- "imageType": "svg",
- "url": "https://www.tekstowo.pl"
- },
- "genius": {
- "frontends": {
- "dumb": {
- "name": "Dumb",
- "instanceList": true,
- "url": "https://github.com/rramiachraf/dumb",
- "localhost": true
- },
- "intellectual": {
- "name": "Intellectual",
- "instanceList": true,
- "url": "https://github.com/Insprill/intellectual",
- "localhost": false
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?genius\\.com\\/"
- ],
- "name": "Genius",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "dumb",
- "instance": "public"
- },
- "imageType": "svg",
- "url": "https://genius.com"
- },
- "medium": {
- "frontends": {
- "scribe": {
- "name": "Scribe",
- "instanceList": true,
- "url": "https://sr.ht/~edwardloveall/Scribe"
- },
- "libMedium": {
- "name": "LibMedium",
- "instanceList": true,
- "url": "https://github.com/realaravinth/libmedium"
- }
- },
- "targets": [
- "^https:\\/{2}([a-zA-Z0-9-]+\\.)?medium\\.com",
- "^https?:\\/{2}towardsdatascience\\.com\\/",
- "^https?:\\/{2}uxdesign\\.cc\\/",
- "^https?:\\/{2}uxplanet\\.org\\/",
- "^https?:\\/{2}betterprogramming\\.pub\\/",
- "^https?:\\/{2}aninjusticemag\\.com\\/",
- "^https?:\\/{2}betterhumans\\.pub\\/",
- "^https?:\\/{2}psiloveyou\\.xyz\\/",
- "^https?:\\/{2}entrepreneurshandbook\\.co\\/",
- "^https?:\\/{2}blog\\.coinbase\\.com\\/",
- "^https?:\\/{2}levelup\\.gitconnected\\.com\\/",
- "^https?:\\/{2}javascript\\.plainenglish\\.io\\/",
- "^https?:\\/{2}blog\\.bitsrc\\.io\\/",
- "^https?:\\/{2}itnext\\.io\\/",
- "^https?:\\/{2}codeburst\\.io\\/",
- "^https?:\\/{2}infosecwriteups\\.com\\/",
- "^https?:\\/{2}blog\\.devgenius\\.io\\/",
- "^https?:\\/{2}writingcooperative\\.com\\/",
- "^https?:\\/{2}proandroiddev\\.com\\/"
- ],
- "name": "Medium",
- "options": {
- "frontend": "scribe",
- "enabled": false,
- "unsupportedUrls": "bypass"
- },
- "imageType": "svg",
- "url": "https://medium.com"
- },
- "quora": {
- "frontends": {
- "quetre": {
- "name": "Quetre",
- "instanceList": true,
- "url": "https://github.com/zyachel/quetre",
- "localhost": true
- }
- },
- "targets": [
- "^https?:\\/{2}([a-zA-Z0-9-]+\\.)*quora\\.com\\/"
- ],
- "name": "Quora",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "quetre",
- "instance": "public"
- },
- "imageType": "png",
- "url": "https://quora.com"
- },
- "github": {
- "frontends": {
- "gothub": {
- "name": "Gothub",
- "instanceList": true,
- "url": "https://codeberg.org/gothub/gothub"
- }
- },
- "targets": [
- "^https?:\\/{2}github\\.com\\/",
- "^https?:\\/{2}gist\\.github\\.com\\/[^\\/]+\\/[^\\/]+\\/?",
- "^https?:\\/{2}raw\\.githubusercontent\\.com\\/[^\\/]+\\/[^\\/]+\\/?"
- ],
- "name": "GitHub",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "gothub"
- },
- "imageType": "svgMono",
- "url": "https://github.com"
- },
- "gitlab": {
- "frontends": {
- "laboratory": {
- "name": "Laboratory",
- "instanceList": true,
- "url": "https://git.vitali64.duckdns.org/utils/laboratory.git/about/"
- }
- },
- "targets": [
- "^https?:\\/{2}gitlab\\.com\\/",
- "^https?:\\/{2}gitlab\\.freedesktop\\.com\\/",
- "^https?:\\/{2}gitlab\\.archlinux\\.com\\/"
- ],
- "name": "Gitlab",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "laboratory"
- },
- "imageType": "svg",
- "url": "https://gitlab.com/"
- },
- "stackOverflow": {
- "frontends": {
- "anonymousOverflow": {
- "name": "AnonymousOverflow",
- "instanceList": true,
- "url": "https://github.com/httpjamesm/AnonymousOverflow",
- "localhost": true
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?([a-zA-Z]+\\.)?stackoverflow\\.com\\/",
- "(?!^https?:\\/{2}(api|data|blog)\\.)^https?:\\/{2}([a-zA-Z0-9-]+\\.)stackexchange\\.com\\/",
- "^https?:\\/{2}(www\\.)?([a-zA-Z]+\\.)?(askubuntu\\.com|mathoverflow\\.net|serverfault\\.com|stackapps\\.com|superuser\\.com)\\/"
- ],
- "name": "Stack Overflow",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "anonymousOverflow",
- "instance": "public"
- },
- "imageType": "svgMono",
- "url": "https://stackoverflow.com/"
- },
- "reuters": {
- "frontends": {
- "neuters": {
- "name": "Neuters",
- "instanceList": true,
- "url": "https://github.com/HookedBehemoth/neuters"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?reuters\\.com\\/"
- ],
- "name": "Reuters",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "neuters"
- },
- "imageType": "svg",
- "url": "https://reuters.com"
- },
- "snopes": {
- "frontends": {
- "suds": {
- "name": "Suds",
- "instanceList": true,
- "url": "https://git.vern.cc/cobra/Suds"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?snopes\\.com\\/"
- ],
- "name": "Snopes",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "suds"
- },
- "imageType": "svg",
- "url": "https://www.snopes.com"
- },
- "ifunny": {
- "frontends": {
- "unfunny": {
- "name": "UNfunny",
- "instanceList": true,
- "url": "https://git.vern.cc/cobra/UNfunny"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?ifunny\\.co\\/"
- ],
- "name": "iFunny",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "unfunny"
- },
- "imageType": "svg",
- "url": "https://ifunny.co"
- },
- "tenor": {
- "frontends": {
- "soprano": {
- "name": "Soprano",
- "instanceList": true,
- "url": "https://git.vern.cc/cobra/Soprano"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?tenor\\.com\\/"
- ],
- "name": "Tenor",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "soprano"
- },
- "imageType": "svg",
- "url": "https://tenor.com"
- },
- "knowyourmeme": {
- "frontends": {
- "meme": {
- "name": "MeMe",
- "instanceList": true,
- "url": "https://git.vern.cc/cobra/MeMe"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?knowyourmeme\\.com\\/"
- ],
- "name": "KnowYourMeme",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "meme"
- },
- "imageType": "svg",
- "url": "https://knowyourmeme.com"
- },
- "urbanDictionary": {
- "frontends": {
- "ruralDictionary": {
- "name": "Rural Dictionary",
- "instanceList": true,
- "url": "https://codeberg.org/zortazert/rural-dictionary"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?urbandictionary\\.com\\/"
- ],
- "name": "Urban Dictionary",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "ruralDictionary"
- },
- "imageType": "svg",
- "url": "https://urbandictionary.com"
- },
- "goodreads": {
- "frontends": {
- "biblioReads": {
- "name": "BiblioReads",
- "instanceList": true,
- "url": "https://github.com/nesaku/BiblioReads",
- "localhost": true
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?goodreads\\.com\\/"
- ],
- "name": "Goodreads",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "biblioReads",
- "instance": "public"
- },
- "imageType": "svgMono",
- "url": "https://goodreads.com/"
- },
- "wolframAlpha": {
- "frontends": {
- "wolfreeAlpha": {
- "name": "WolfreeAlpha",
- "instanceList": true,
- "url": "https://git.disroot.org/wolfree"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?wolframalpha\\.com\\/"
- ],
- "name": "Wolfram Alpha",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "wolfreeAlpha"
- },
- "imageType": "svg",
- "url": "https://www.wolframalpha.com"
- },
- "instructables": {
- "name": "Instructables",
- "frontends": {
- "indestructables": {
- "name": "Indestructables",
- "instanceList": true,
- "url": "https://indestructables.codeberg.page"
- },
- "destructables": {
- "name": "Destructables",
- "instanceList": true,
- "url": "https://git.vern.cc/cobra/Destructables"
- }
- },
- "targets": [
- "^https?:\\/{2}(www\\.)?instructables\\.com\\/"
- ],
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "indestructables"
- },
- "imageType": "svg",
- "url": "https://www.instructables.com"
- },
- "wikipedia": {
- "frontends": {
- "wikiless": {
- "name": "Wikiless",
- "instanceList": true,
- "url": "https://wikiless.org"
- }
- },
- "targets": [
- "^https?:\\/{2}([a-z]+\\.)*wikipedia\\.org\\/?"
- ],
- "name": "Wikipedia",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "wikiless"
- },
- "imageType": "svg",
- "url": "https://wikipedia.org"
- },
- "waybackMachine": {
- "frontends": {
- "waybackClassic": {
- "name": "Wayback Classic",
- "instanceList": true,
- "url": "https://github.com/ticky/wayback-classic"
- }
- },
- "targets": [
- "^https?:\\/{2}web\\.archive\\.org\\/"
- ],
- "name": "Wayback Machine",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "waybackClassic"
- },
- "imageType": "svgMono",
- "url": "https://web.archive.org"
- },
- "search": {
- "frontends": {
- "searxng": {
- "name": "SearXNG",
- "instanceList": true,
- "url": "https://github.com/searxng/searxng",
- "localhost": true
- },
- "searx": {
- "name": "SearX",
- "instanceList": true,
- "url": "https://searx.github.io/searx/"
- },
- "whoogle": {
- "name": "Whoogle",
- "instanceList": true,
- "url": "https://benbusby.com/projects/whoogle-search/"
- },
- "librey": {
- "name": "LibreY",
- "instanceList": true,
- "url": "https://github.com/Ahwxorg/librey/"
- },
- "4get": {
- "name": "4get",
- "instanceList": true,
- "url": "https://git.lolcat.ca/lolcat/4get"
- }
- },
- "targets": [
- "^https?:\\/{2}search\\.libredirect\\.invalid",
- "^https?:\\/{2}libredirect\\.github\\.io\\/\\?q"
- ],
- "name": "Search",
- "options": {
- "enabled": false,
- "frontend": "searxng",
- "unsupportedUrls": "bypass",
- "instance": "public"
- },
- "imageType": "svgMono",
- "url": "https://search.libredirect.invalid"
- },
- "translate": {
- "frontends": {
- "simplyTranslate": {
- "name": "SimplyTranslate",
- "instanceList": true,
- "url": "https://git.sr.ht/~metalune/simplytranslate_web",
- "localhost": true
- },
- "mozhi": {
- "name": "Mozhi",
- "instanceList": true,
- "url": "https://codeberg.org/aryak/mozhi",
- "localhost": false
- },
- "libreTranslate": {
- "name": "LibreTranslate",
- "instanceList": true,
- "url": "https://github.com/LibreTranslate/LibreTranslate"
- }
- },
- "targets": [
- "^https?:\\/{2}translate\\.google(\\.[a-z]{2,3}){1,2}\\/",
- "^https?:\\/{2}translate\\.libredirect\\.invalid"
- ],
- "name": "Translate",
- "options": {
- "enabled": false,
- "frontend": "simplyTranslate",
- "unsupportedUrls": "bypass",
- "instance": "public"
- },
- "imageType": "svgMono",
- "url": "https://translate.libredirect.invalid"
- },
- "maps": {
- "frontends": {
- "osm": {
- "name": "OpenStreetMap",
- "instanceList": true,
- "embeddable": true,
- "url": "https://www.openstreetmap.org/"
- }
- },
- "targets": [
- "^https?:\\/{2}maps\\.libredirect\\.invalid",
- "^https?:\\/{2}(((www|maps)\\.)?(google\\.).*(\\/maps)|maps\\.(google\\.).*)"
- ],
- "name": "Maps",
- "options": {
- "redirectType": "main_frame",
- "enabled": false,
- "frontend": "osm",
- "unsupportedUrls": "bypass"
- },
- "imageType": "svgMono",
- "url": "https://maps.libredirect.invalid"
- },
- "meet": {
- "name": "Meet",
- "frontends": {
- "jitsi": {
- "name": "Jitsi",
- "instanceList": true,
- "url": "https://jitsi.org"
- }
- },
- "targets": [
- "^https?:\\/{2}meet\\.libredirect\\.invalid\\/"
- ],
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "jitsi"
- },
- "imageType": "svgMono",
- "url": "https://meet.libredirect.invalid"
- },
- "sendFiles": {
- "frontends": {
- "send": {
- "name": "Send",
- "instanceList": true,
- "url": "https://gitlab.com/timvisee/send"
- }
- },
- "targets": [
- "^https?:\\/{2}send\\.libredirect\\.invalid",
- "^https?:\\/{2}send\\.firefox\\.com\\/?$",
- "^https?:\\/{2}sendfiles\\.online\\/?$"
- ],
- "name": "Send Files",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "send"
- },
- "imageType": "svgMono",
- "url": "https://send.libredirect.invalid"
- },
- "textStorage": {
- "frontends": {
- "privateBin": {
- "name": "PrivateBin",
- "instanceList": true,
- "url": "https://privatebin.info"
- },
- "pasted": {
- "name": "Pasted",
- "instanceList": true,
- "url": "https://github.com/Dragynfruit/pasted"
- }
- },
- "targets": [
- "^https?:\\/{2}paste\\.libredirect\\.invalid"
- ],
- "name": "Paste Text",
- "options": {
- "enabled": false,
- "unsupportedUrls": "bypass",
- "frontend": "privateBin"
- },
- "imageType": "svgMono",
- "url": "https://paste.libredirect.invalid"
- }
- }
-} \ No newline at end of file
+ "networks": {
+ "clearnet": {
+ "tld": "org",
+ "name": "Clearnet"
+ },
+ "tor": {
+ "tld": "onion",
+ "name": "Tor"
+ },
+ "i2p": {
+ "tld": "i2p",
+ "name": "I2P"
+ },
+ "loki": {
+ "tld": "loki",
+ "name": "Lokinet"
+ }
+ },
+ "services": {
+ "youtube": {
+ "frontends": {
+ "invidious": {
+ "name": "Invidious",
+ "embeddable": true,
+ "instanceList": true,
+ "url": "https://invidious.io/"
+ },
+ "materialious": {
+ "name": "Materialious",
+ "embeddable": true,
+ "instanceList": true,
+ "url": "https://materialio.us/"
+ },
+ "piped": {
+ "excludeTargets": [2, 3],
+ "name": "Piped",
+ "embeddable": true,
+ "instanceList": true,
+ "url": "https://github.com/TeamPiped/Piped"
+ },
+ "pipedMaterial": {
+ "excludeTargets": [2, 3],
+ "name": "Piped-Material",
+ "embeddable": false,
+ "instanceList": true,
+ "url": "https://github.com/mmjee/Piped-Material"
+ },
+ "poketube": {
+ "excludeTargets": [2, 3],
+ "name": "PokeTube",
+ "embeddable": true,
+ "instanceList": true,
+ "url": "https://codeberg.org/Ashley/poketube"
+ },
+ "cloudtube": {
+ "name": "CloudTube",
+ "embeddable": false,
+ "instanceList": true,
+ "url": "https://sr.ht/~cadence/tube",
+ "excludeTargets": [2, 3]
+ },
+ "lightTube": {
+ "name": "LightTube",
+ "embeddable": false,
+ "instanceList": true,
+ "url": "https://github.com/lighttube-org/LightTube"
+ },
+ "tuboYoutube": {
+ "name": "Tubo",
+ "embeddable": false,
+ "instanceList": true,
+ "url": "https://git.migalmoreno.com/tubo/about/",
+ "excludeTargets": [2, 3]
+ },
+ "freetube": {
+ "excludeTargets": [2, 3],
+ "name": "FreeTube",
+ "embeddable": false,
+ "desktopApp": true,
+ "instanceList": false,
+ "url": "https://github.com/FreeTubeApp/FreeTube"
+ },
+ "yattee": {
+ "excludeTargets": [2, 3],
+ "name": "Yattee",
+ "embeddable": false,
+ "desktopApp": true,
+ "instanceList": false,
+ "url": "https://github.com/yattee/yattee"
+ },
+ "freetubePwa": {
+ "excludeTargets": [2, 3],
+ "name": "FreeTube PWA",
+ "embeddable": false,
+ "instanceList": false,
+ "url": "https://github.com/MarmadileManteater/FreeTubeCordova"
+ },
+ "viewtube": {
+ "name": "ViewTube",
+ "embeddable": false,
+ "instanceList": true,
+ "url": "https://github.com/ViewTube/viewtube"
+ }
+ },
+ "targets": [
+ "^https?:\\/{2}(www\\.|m\\.)?youtube.com(\\/|$)(?!iframe_api\\/|redirect\\/)",
+ "^https?:\\/{2}img\\.youtube.com\\/vi\\/.*\\/..*",
+ "^https?:\\/{2}(i|s)\\.ytimg.com\\/vi\\/.*\\/..*",
+ "^https?:\\/{2}(www\\.)?youtube.com\\/watch?v=..*",
+ "^https?:\\/{2}(www\\.)?youtu\\.be\\/..*",
+ "^https?:\\/{2}(www\\.)?(youtube|youtube-nocookie)\\.com\\/embed\\/..*"
+ ],
+ "name": "YouTube",
+ "options": {
+ "enabled": false,
+ "redirectType": "main_frame",
+ "frontend": "invidious",
+ "embedFrontend": "invidious",
+ "unsupportedUrls": "bypass"
+ },
+ "imageType": "png",
+ "embeddable": true,
+ "url": "https://youtube.com"
+ },
+ "youtubeMusic": {
+ "frontends": {
+ "hyperpipe": {
+ "name": "Hyperpipe",
+ "instanceList": true,
+ "url": "https://codeberg.org/Hyperpipe/Hyperpipe"
+ },
+ "invidiousMusic": {
+ "name": "Invidious",
+ "embeddable": true,
+ "instanceList": true,
+ "url": "https://invidious.io/"
+ },
+ "freetubeMusic": {
+ "name": "FreeTube",
+ "embeddable": false,
+ "desktopApp": true,
+ "instanceList": false,
+ "url": "https://github.com/FreeTubeApp/FreeTube"
+ }
+ },
+ "targets": ["^https?:\\/{2}music\\.youtube\\.com\\/"],
+ "name": "YT Music",
+ "options": {
+ "enabled": false,
+ "frontend": "hyperpipe",
+ "unsupportedUrls": "bypass"
+ },
+ "imageType": "png",
+ "url": "https://music.youtube.com"
+ },
+ "twitter": {
+ "frontends": {
+ "nitter": {
+ "name": "Nitter",
+ "embeddable": true,
+ "instanceList": true,
+ "url": "https://github.com/zedeus/nitter",
+ "localhost": true
+ }
+ },
+ "targets": [
+ "^https?:\\/{2}(www\\.|mobile\\.)?twitter\\.com\\/",
+ "^https?:\\/{2}(www\\.|mobile\\.)?x\\.com\\/",
+ "^https?:\\/{2}(pbs\\.|video\\.)twimg\\.com\\/",
+ "^https?:\\/{2}platform\\.x\\.com/embed\\/",
+ "^https?:\\/{2}platform\\.twitter\\.com/embed\\/",
+ "^https?:\\/{2}t\\.co\\/"
+ ],
+ "name": "Twitter",
+ "options": {
+ "enabled": false,
+ "redirectType": "main_frame",
+ "unsupportedUrls": "bypass",
+ "frontend": "nitter",
+ "instance": "public"
+ },
+ "imageType": "png",
+ "embeddable": true,
+ "url": "https://twitter.com"
+ },
+ "bluesky": {
+ "frontends": {
+ "skyview": {
+ "name": "Skyview",
+ "instanceList": true,
+ "url": "https://github.com/badlogic/skyview"
+ }
+ },
+ "targets": ["^https?:\\/{2}bsky\\.app\\/"],
+ "name": "Bluesky",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "skyview"
+ },
+ "imageType": "svg",
+ "url": "https://bsky.app/"
+ },
+ "reddit": {
+ "frontends": {
+ "libreddit": {
+ "name": "Libreddit",
+ "instanceList": true,
+ "url": "https://github.com/spikecodes/libreddit",
+ "localhost": true
+ },
+ "redlib": {
+ "name": "Redlib",
+ "instanceList": true,
+ "url": "https://github.com/redlib-org/redlib",
+ "localhost": true
+ },
+ "teddit": {
+ "name": "Teddit",
+ "instanceList": true,
+ "url": "https://codeberg.org/teddit/teddit",
+ "localhost": true
+ }
+ },
+ "targets": [
+ "^https?:\\/{2}(www\\.|old\\.|np\\.|new\\.|amp\\.)?(reddit|reddittorjg6rue252oqsxryoxengawnmo46qy4kyii5wtqnwfj4ooad)\\.(com|onion)(?=\\/u(ser)?\\/|\\/r\\/|\\/search|\\/new|\\/?$)",
+ "^https?:\\/{2}(i|(external-)?preview)\\.redd\\.it"
+ ],
+ "name": "Reddit",
+ "options": {
+ "enabled": false,
+ "frontend": "libreddit",
+ "unsupportedUrls": "bypass",
+ "instance": "public"
+ },
+ "imageType": "png",
+ "url": "https://reddit.com"
+ },
+ "tumblr": {
+ "frontends": {
+ "priviblur": {
+ "name": "Priviblur",
+ "embeddable": true,
+ "instanceList": true,
+ "url": "https://github.com/syeopite/priviblur",
+ "localhost": true
+ }
+ },
+ "targets": [
+ "^https?:\\/{2}(media\\.|assets\\.|static\\.)?tumblr\\.com\\/",
+ "^https?:\\/{2}[0-9]+\\.media\\.tumblr\\.com\\/",
+ "^https?:\\/{2}(www\\.)?(.*)\\.tumblr.com\\/"
+ ],
+ "name": "Tumblr",
+ "options": {
+ "enabled": false,
+ "redirectType": "main_frame",
+ "unsupportedUrls": "bypass",
+ "frontend": "priviblur",
+ "instance": "public"
+ },
+ "imageType": "svg",
+ "embeddable": true,
+ "url": "https://tumblr.com"
+ },
+ "twitch": {
+ "frontends": {
+ "safetwitch": {
+ "name": "SafeTwitch",
+ "embeddable": true,
+ "instanceList": true,
+ "url": "https://codeberg.org/dragongoose/safetwitch",
+ "localhost": false
+ },
+ "twineo": {
+ "name": "Twineo",
+ "embeddable": true,
+ "instanceList": true,
+ "url": "https://codeberg.org/CloudyyUw/twineo",
+ "localhost": false
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.|clips\\.)?twitch\\.(tv|com)\\/"],
+ "name": "Twitch",
+ "options": {
+ "enabled": false,
+ "redirectType": "main_frame",
+ "unsupportedUrls": "bypass",
+ "frontend": "safetwitch",
+ "instance": "public"
+ },
+ "imageType": "svg",
+ "embeddable": true,
+ "url": "https://twitch.tv"
+ },
+ "tiktok": {
+ "frontends": {
+ "proxiTok": {
+ "name": "ProxiTok",
+ "instanceList": true,
+ "url": "https://github.com/pablouser1/ProxiTok",
+ "localhost": true
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?tiktok\\.com\\/"],
+ "name": "TikTok",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "proxiTok",
+ "instance": "public"
+ },
+ "imageType": "png",
+ "url": "https://tiktok.com"
+ },
+ "instagram": {
+ "frontends": {
+ "proxigram": {
+ "name": "Proxigram",
+ "instanceList": true,
+ "url": "https://codeberg.org/ThePenguinDev/Proxigram",
+ "localhost": false
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?instagram\\.com"],
+ "name": "Instagram",
+ "options": {
+ "enabled": false,
+ "frontend": "proxigram",
+ "unsupportedUrls": "bypass",
+ "instance": "public"
+ },
+ "imageType": "png",
+ "url": "https://www.instagram.com"
+ },
+ "imdb": {
+ "frontends": {
+ "libremdb": {
+ "name": "libremdb",
+ "instanceList": true,
+ "url": "https://github.com/zyachel/libremdb",
+ "localhost": true
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.|m\\.)?imdb\\.com"],
+ "name": "IMDb",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "libremdb",
+ "instance": "public"
+ },
+ "imageType": "svg",
+ "url": "https://imdb.com"
+ },
+ "bilibili": {
+ "frontends": {
+ "mikuInvidious": {
+ "name": "MikuInvidious",
+ "instanceList": true,
+ "url": "https://0xacab.org/johnxina/mikuinvidious"
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.|space\\.)?bilibili\\.com\\/", "^https?:\\/{2}b23\\.tv\\/"],
+ "name": "Bilibili",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "mikuInvidious"
+ },
+ "imageType": "svgMono",
+ "url": "https://bilibili.com/"
+ },
+ "pixiv": {
+ "name": "Pixiv",
+ "frontends": {
+ "pixivFe": {
+ "name": "PixivFE",
+ "instanceList": true,
+ "url": "https://codeberg.org/VnPower/pixivfe"
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?pixiv\\.net\\/"],
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "pixivFe"
+ },
+ "imageType": "svg",
+ "url": "https://www.pixiv.net"
+ },
+ "fandom": {
+ "frontends": {
+ "breezeWiki": {
+ "name": "BreezeWiki",
+ "instanceList": true,
+ "url": "https://breezewiki.com"
+ }
+ },
+ "targets": ["^https?:\\/{2}([a-zA-Z0-9-]+\\.)?(fandom|wikia)\\.com(?=\\/wiki|\\/?$)"],
+ "name": "Fandom",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "instance": "public",
+ "frontend": "breezeWiki"
+ },
+ "imageType": "svg",
+ "url": "https://fandom.com"
+ },
+ "imgur": {
+ "frontends": {
+ "rimgo": {
+ "name": "rimgo",
+ "instanceList": true,
+ "url": "https://codeberg.org/video-prize-ranch/rimgo",
+ "localhost": true,
+ "embeddable": true
+ }
+ },
+ "targets": ["^https?:\\/{2}([im]\\.)?(stack\\.)?imgur\\.(com|io)\\/"],
+ "name": "Imgur",
+ "options": {
+ "enabled": false,
+ "redirectType": "main_frame",
+ "unsupportedUrls": "bypass",
+ "frontend": "rimgo",
+ "instance": "public"
+ },
+ "imageType": "png",
+ "embeddable": true,
+ "url": "https://imgur.com"
+ },
+ "pinterest": {
+ "name": "Pinterest",
+ "frontends": {
+ "binternet": {
+ "name": "Binternet",
+ "instanceList": true,
+ "url": "https://github.com/Ahwxorg/Binternet",
+ "embeddable": true
+ }
+ },
+ "targets": ["^https?:\\/{2}i\\.pinimg\\.com", "^https?:\\/{2}(www\\.)?pinterest\\.com"],
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "redirectType": "main_frame",
+ "frontend": "binternet"
+ },
+ "imageType": "svg",
+ "embeddable": true,
+ "url": "https://pinterest.com"
+ },
+ "soundcloud": {
+ "frontends": {
+ "tuboSoundcloud": {
+ "name": "Tubo",
+ "embeddable": false,
+ "instanceList": true,
+ "url": "https://git.migalmoreno.com/tubo/about/"
+ }
+ },
+ "targets": ["^https?:\\/{2}soundcloud\\.com"],
+ "name": "SoundCloud",
+ "options": {
+ "enabled": false,
+ "redirectType": "main_frame",
+ "frontend": "tuboSoundcloud",
+ "unsupportedUrls": "bypass"
+ },
+ "imageType": "svg",
+ "embeddable": false,
+ "url": "https://soundcloud.com"
+ },
+ "bandcamp": {
+ "frontends": {
+ "tent": {
+ "name": "Tent",
+ "instanceList": true,
+ "url": "https://forgejo.sny.sh/sun/Tent"
+ }
+ },
+ "targets": ["^https?:\\/{2}(.*\\.)?bandcamp\\.com\\/", "^https?:\\/{2}(f4|t4)\\.bcbits\\.com\\/"],
+ "name": "Bandcamp",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "tent"
+ },
+ "imageType": "svg",
+ "url": "https://bandcamp.com"
+ },
+ "tekstowo": {
+ "frontends": {
+ "tekstoLibre": {
+ "name": "TekstoLibre",
+ "instanceList": true,
+ "url": "https://github.com/Davilarek/TekstoLibre"
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?tekstowo\\.pl\\/"],
+ "name": "Tekstowo.pl",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "tekstoLibre"
+ },
+ "imageType": "svg",
+ "url": "https://www.tekstowo.pl"
+ },
+ "genius": {
+ "frontends": {
+ "dumb": {
+ "name": "Dumb",
+ "instanceList": true,
+ "url": "https://github.com/rramiachraf/dumb",
+ "localhost": true
+ },
+ "intellectual": {
+ "name": "Intellectual",
+ "instanceList": true,
+ "url": "https://github.com/Insprill/intellectual",
+ "localhost": false
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?genius\\.com\\/"],
+ "name": "Genius",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "dumb",
+ "instance": "public"
+ },
+ "imageType": "svg",
+ "url": "https://genius.com"
+ },
+ "medium": {
+ "frontends": {
+ "scribe": {
+ "name": "Scribe",
+ "instanceList": true,
+ "url": "https://sr.ht/~edwardloveall/Scribe"
+ },
+ "libMedium": {
+ "name": "LibMedium",
+ "instanceList": true,
+ "url": "https://github.com/realaravinth/libmedium"
+ }
+ },
+ "targets": [
+ "^https:\\/{2}([a-zA-Z0-9-]+\\.)?medium\\.com",
+ "^https?:\\/{2}towardsdatascience\\.com\\/",
+ "^https?:\\/{2}uxdesign\\.cc\\/",
+ "^https?:\\/{2}uxplanet\\.org\\/",
+ "^https?:\\/{2}betterprogramming\\.pub\\/",
+ "^https?:\\/{2}aninjusticemag\\.com\\/",
+ "^https?:\\/{2}betterhumans\\.pub\\/",
+ "^https?:\\/{2}psiloveyou\\.xyz\\/",
+ "^https?:\\/{2}entrepreneurshandbook\\.co\\/",
+ "^https?:\\/{2}blog\\.coinbase\\.com\\/",
+ "^https?:\\/{2}levelup\\.gitconnected\\.com\\/",
+ "^https?:\\/{2}javascript\\.plainenglish\\.io\\/",
+ "^https?:\\/{2}blog\\.bitsrc\\.io\\/",
+ "^https?:\\/{2}itnext\\.io\\/",
+ "^https?:\\/{2}codeburst\\.io\\/",
+ "^https?:\\/{2}infosecwriteups\\.com\\/",
+ "^https?:\\/{2}blog\\.devgenius\\.io\\/",
+ "^https?:\\/{2}writingcooperative\\.com\\/",
+ "^https?:\\/{2}proandroiddev\\.com\\/"
+ ],
+ "name": "Medium",
+ "options": {
+ "frontend": "scribe",
+ "enabled": false,
+ "unsupportedUrls": "bypass"
+ },
+ "imageType": "svg",
+ "url": "https://medium.com"
+ },
+ "quora": {
+ "frontends": {
+ "quetre": {
+ "name": "Quetre",
+ "instanceList": true,
+ "url": "https://github.com/zyachel/quetre",
+ "localhost": true
+ }
+ },
+ "targets": ["^https?:\\/{2}([a-zA-Z0-9-]+\\.)*quora\\.com\\/"],
+ "name": "Quora",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "quetre",
+ "instance": "public"
+ },
+ "imageType": "png",
+ "url": "https://quora.com"
+ },
+ "github": {
+ "frontends": {
+ "gothub": {
+ "name": "Gothub",
+ "instanceList": true,
+ "url": "https://codeberg.org/gothub/gothub"
+ }
+ },
+ "targets": [
+ "^https?:\\/{2}github\\.com\\/",
+ "^https?:\\/{2}gist\\.github\\.com\\/[^\\/]+\\/[^\\/]+\\/?",
+ "^https?:\\/{2}raw\\.githubusercontent\\.com\\/[^\\/]+\\/[^\\/]+\\/?"
+ ],
+ "name": "GitHub",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "gothub"
+ },
+ "imageType": "svgMono",
+ "url": "https://github.com"
+ },
+ "gitlab": {
+ "frontends": {
+ "laboratory": {
+ "name": "Laboratory",
+ "instanceList": true,
+ "url": "https://git.vitali64.duckdns.org/utils/laboratory.git/about/"
+ }
+ },
+ "targets": [
+ "^https?:\\/{2}gitlab\\.com\\/",
+ "^https?:\\/{2}gitlab\\.freedesktop\\.com\\/",
+ "^https?:\\/{2}gitlab\\.archlinux\\.com\\/"
+ ],
+ "name": "Gitlab",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "laboratory"
+ },
+ "imageType": "svg",
+ "url": "https://gitlab.com/"
+ },
+ "stackOverflow": {
+ "frontends": {
+ "anonymousOverflow": {
+ "name": "AnonymousOverflow",
+ "instanceList": true,
+ "url": "https://github.com/httpjamesm/AnonymousOverflow",
+ "localhost": true
+ }
+ },
+ "targets": [
+ "^https?:\\/{2}(www\\.)?([a-zA-Z]+\\.)?stackoverflow\\.com\\/",
+ "(?!^https?:\\/{2}(api|data|blog)\\.)^https?:\\/{2}([a-zA-Z0-9-]+\\.)stackexchange\\.com\\/",
+ "^https?:\\/{2}(www\\.)?([a-zA-Z]+\\.)?(askubuntu\\.com|mathoverflow\\.net|serverfault\\.com|stackapps\\.com|superuser\\.com)\\/"
+ ],
+ "name": "Stack Overflow",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "anonymousOverflow",
+ "instance": "public"
+ },
+ "imageType": "svgMono",
+ "url": "https://stackoverflow.com/"
+ },
+ "reuters": {
+ "frontends": {
+ "neuters": {
+ "name": "Neuters",
+ "instanceList": true,
+ "url": "https://github.com/HookedBehemoth/neuters"
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?reuters\\.com\\/"],
+ "name": "Reuters",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "neuters"
+ },
+ "imageType": "svg",
+ "url": "https://reuters.com"
+ },
+ "snopes": {
+ "frontends": {
+ "suds": {
+ "name": "Suds",
+ "instanceList": true,
+ "url": "https://git.vern.cc/cobra/Suds"
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?snopes\\.com\\/"],
+ "name": "Snopes",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "suds"
+ },
+ "imageType": "svg",
+ "url": "https://www.snopes.com"
+ },
+ "ifunny": {
+ "frontends": {
+ "unfunny": {
+ "name": "UNfunny",
+ "instanceList": true,
+ "url": "https://git.vern.cc/cobra/UNfunny"
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?ifunny\\.co\\/"],
+ "name": "iFunny",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "unfunny"
+ },
+ "imageType": "svg",
+ "url": "https://ifunny.co"
+ },
+ "tenor": {
+ "frontends": {
+ "soprano": {
+ "name": "Soprano",
+ "instanceList": true,
+ "url": "https://git.vern.cc/cobra/Soprano"
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?tenor\\.com\\/"],
+ "name": "Tenor",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "soprano"
+ },
+ "imageType": "svg",
+ "url": "https://tenor.com"
+ },
+ "knowyourmeme": {
+ "frontends": {
+ "meme": {
+ "name": "MeMe",
+ "instanceList": true,
+ "url": "https://git.vern.cc/cobra/MeMe"
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?knowyourmeme\\.com\\/"],
+ "name": "KnowYourMeme",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "meme"
+ },
+ "imageType": "svg",
+ "url": "https://knowyourmeme.com"
+ },
+ "urbanDictionary": {
+ "frontends": {
+ "ruralDictionary": {
+ "name": "Rural Dictionary",
+ "instanceList": true,
+ "url": "https://codeberg.org/zortazert/rural-dictionary"
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?urbandictionary\\.com\\/"],
+ "name": "Urban Dictionary",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "ruralDictionary"
+ },
+ "imageType": "svg",
+ "url": "https://urbandictionary.com"
+ },
+ "goodreads": {
+ "frontends": {
+ "biblioReads": {
+ "name": "BiblioReads",
+ "instanceList": true,
+ "url": "https://github.com/nesaku/BiblioReads",
+ "localhost": true
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?goodreads\\.com\\/"],
+ "name": "Goodreads",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "biblioReads",
+ "instance": "public"
+ },
+ "imageType": "svgMono",
+ "url": "https://goodreads.com/"
+ },
+ "wolframAlpha": {
+ "frontends": {
+ "wolfreeAlpha": {
+ "name": "WolfreeAlpha",
+ "instanceList": true,
+ "url": "https://git.disroot.org/wolfree"
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?wolframalpha\\.com\\/"],
+ "name": "Wolfram Alpha",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "wolfreeAlpha"
+ },
+ "imageType": "svg",
+ "url": "https://www.wolframalpha.com"
+ },
+ "instructables": {
+ "name": "Instructables",
+ "frontends": {
+ "indestructables": {
+ "name": "Indestructables",
+ "instanceList": true,
+ "url": "https://indestructables.codeberg.page"
+ },
+ "destructables": {
+ "name": "Destructables",
+ "instanceList": true,
+ "url": "https://git.vern.cc/cobra/Destructables"
+ }
+ },
+ "targets": ["^https?:\\/{2}(www\\.)?instructables\\.com\\/"],
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "indestructables"
+ },
+ "imageType": "svg",
+ "url": "https://www.instructables.com"
+ },
+ "wikipedia": {
+ "frontends": {
+ "wikiless": {
+ "name": "Wikiless",
+ "instanceList": true,
+ "url": "https://wikiless.org"
+ }
+ },
+ "targets": ["^https?:\\/{2}([a-z]+\\.)*wikipedia\\.org\\/?"],
+ "name": "Wikipedia",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "wikiless"
+ },
+ "imageType": "svg",
+ "url": "https://wikipedia.org"
+ },
+ "waybackMachine": {
+ "frontends": {
+ "waybackClassic": {
+ "name": "Wayback Classic",
+ "instanceList": true,
+ "url": "https://github.com/ticky/wayback-classic"
+ }
+ },
+ "targets": ["^https?:\\/{2}web\\.archive\\.org\\/"],
+ "name": "Wayback Machine",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "waybackClassic"
+ },
+ "imageType": "svgMono",
+ "url": "https://web.archive.org"
+ },
+ "search": {
+ "frontends": {
+ "searxng": {
+ "name": "SearXNG",
+ "instanceList": true,
+ "url": "https://github.com/searxng/searxng",
+ "localhost": true
+ },
+ "searx": {
+ "name": "SearX",
+ "instanceList": true,
+ "url": "https://searx.github.io/searx/"
+ },
+ "whoogle": {
+ "name": "Whoogle",
+ "instanceList": true,
+ "url": "https://benbusby.com/projects/whoogle-search/"
+ },
+ "librey": {
+ "name": "LibreY",
+ "instanceList": true,
+ "url": "https://github.com/Ahwxorg/librey/"
+ },
+ "4get": {
+ "name": "4get",
+ "instanceList": true,
+ "url": "https://git.lolcat.ca/lolcat/4get"
+ }
+ },
+ "targets": ["^https?:\\/{2}search\\.libredirect\\.invalid", "^https?:\\/{2}libredirect\\.github\\.io\\/\\?q"],
+ "name": "Search",
+ "options": {
+ "enabled": false,
+ "frontend": "searxng",
+ "unsupportedUrls": "bypass",
+ "instance": "public"
+ },
+ "imageType": "svgMono",
+ "url": "https://search.libredirect.invalid"
+ },
+ "translate": {
+ "frontends": {
+ "simplyTranslate": {
+ "name": "SimplyTranslate",
+ "instanceList": true,
+ "url": "https://git.sr.ht/~metalune/simplytranslate_web",
+ "localhost": true
+ },
+ "mozhi": {
+ "name": "Mozhi",
+ "instanceList": true,
+ "url": "https://codeberg.org/aryak/mozhi",
+ "localhost": false
+ },
+ "libreTranslate": {
+ "name": "LibreTranslate",
+ "instanceList": true,
+ "url": "https://github.com/LibreTranslate/LibreTranslate"
+ }
+ },
+ "targets": [
+ "^https?:\\/{2}translate\\.google(\\.[a-z]{2,3}){1,2}\\/",
+ "^https?:\\/{2}translate\\.libredirect\\.invalid"
+ ],
+ "name": "Translate",
+ "options": {
+ "enabled": false,
+ "frontend": "simplyTranslate",
+ "unsupportedUrls": "bypass",
+ "instance": "public"
+ },
+ "imageType": "svgMono",
+ "url": "https://translate.libredirect.invalid"
+ },
+ "maps": {
+ "frontends": {
+ "osm": {
+ "name": "OpenStreetMap",
+ "instanceList": true,
+ "embeddable": true,
+ "url": "https://www.openstreetmap.org/"
+ }
+ },
+ "targets": [
+ "^https?:\\/{2}maps\\.libredirect\\.invalid",
+ "^https?:\\/{2}(((www|maps)\\.)?(google\\.).*(\\/maps)|maps\\.(google\\.).*)"
+ ],
+ "name": "Maps",
+ "options": {
+ "redirectType": "main_frame",
+ "enabled": false,
+ "frontend": "osm",
+ "unsupportedUrls": "bypass"
+ },
+ "imageType": "svgMono",
+ "url": "https://maps.libredirect.invalid"
+ },
+ "meet": {
+ "name": "Meet",
+ "frontends": {
+ "jitsi": {
+ "name": "Jitsi",
+ "instanceList": true,
+ "url": "https://jitsi.org"
+ }
+ },
+ "targets": ["^https?:\\/{2}meet\\.libredirect\\.invalid\\/"],
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "jitsi"
+ },
+ "imageType": "svgMono",
+ "url": "https://meet.libredirect.invalid"
+ },
+ "sendFiles": {
+ "frontends": {
+ "send": {
+ "name": "Send",
+ "instanceList": true,
+ "url": "https://gitlab.com/timvisee/send"
+ }
+ },
+ "targets": [
+ "^https?:\\/{2}send\\.libredirect\\.invalid",
+ "^https?:\\/{2}send\\.firefox\\.com\\/?$",
+ "^https?:\\/{2}sendfiles\\.online\\/?$"
+ ],
+ "name": "Send Files",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "send"
+ },
+ "imageType": "svgMono",
+ "url": "https://send.libredirect.invalid"
+ },
+ "textStorage": {
+ "frontends": {
+ "privateBin": {
+ "name": "PrivateBin",
+ "instanceList": true,
+ "url": "https://privatebin.info"
+ },
+ "pasted": {
+ "name": "Pasted",
+ "instanceList": true,
+ "url": "https://github.com/Dragynfruit/pasted"
+ }
+ },
+ "targets": ["^https?:\\/{2}paste\\.libredirect\\.invalid"],
+ "name": "Paste Text",
+ "options": {
+ "enabled": false,
+ "unsupportedUrls": "bypass",
+ "frontend": "privateBin"
+ },
+ "imageType": "svgMono",
+ "url": "https://paste.libredirect.invalid"
+ }
+ }
+}
diff --git a/src/manifest.json b/src/manifest.json
index cab3181b..cf763247 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -1,91 +1,82 @@
{
- "name": "__MSG_extensionName__",
- "description": "__MSG_extensionDescription__",
- "version": "2.8.5",
- "manifest_version": 2,
- "browser_specific_settings": {
- "gecko": {
- "id": "7esoorv3@alefvanoon.anonaddy.me",
- "strict_min_version": "89.0"
- },
- "gecko_android": {
- "strict_min_version": "113.0"
- }
- },
- "background": {
- "page": "pages/background/background.html",
- "persistent": true
- },
- "icons": {
- "16": "assets/images/libredirect-16.png",
- "32": "assets/images/libredirect-32.png",
- "48": "assets/images/libredirect-48.png",
- "128": "assets/images/libredirect-128.png"
- },
- "permissions": [
- "webRequest",
- "webRequestBlocking",
- "storage",
- "clipboardWrite",
- "contextMenus",
- "<all_urls>"
- ],
- "optional_permissions": [
- "bookmarks"
- ],
- "browser_action": {
- "default_title": "__MSG_extensionName__",
- "browser_style": false,
- "default_popup": "pages/popup/popup.html",
- "default_icon": {
- "16": "assets/images/libredirect-16.png",
- "32": "assets/images/libredirect-32.png",
- "48": "assets/images/libredirect-48.png",
- "128": "assets/images/libredirect-128.png"
- }
- },
- "options_ui": {
- "page": "pages/options/index.html",
- "browser_style": false,
- "open_in_tab": true
- },
- "chrome_settings_overrides": {
- "search_provider": {
- "name": "__MSG_extensionName__",
- "keyword": "@libredirect",
- "favicon_url": "https://raw.githubusercontent.com/libredirect/libredirect/master/src/assets/images/libredirect-16.png",
- "search_url": "https://search.libredirect.invalid/?q={searchTerms}",
- "encoding": "UTF-8",
- "is_default": false
- }
- },
- "commands": {
- "switchInstance": {
- "suggested_key": {
- "default": "Alt+Shift+L"
- },
- "description": "__MSG_switchInstance__"
- },
- "copyRaw": {
- "suggested_key": {
- "default": "Alt+Shift+C"
- },
- "description": "Copies the original link. Ex: Copies the original twitter link while in the nitter website"
- },
- "reverse": {
- "suggested_key": {
- "default": "Alt+Shift+O"
- },
- "description": "Redirect to the original link. Ex: Redirects to the original twitter link while in the nitter website"
- },
- "redirect": {
- "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"
-} \ No newline at end of file
+ "name": "__MSG_extensionName__",
+ "description": "__MSG_extensionDescription__",
+ "version": "2.8.5",
+ "manifest_version": 2,
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "7esoorv3@alefvanoon.anonaddy.me",
+ "strict_min_version": "89.0"
+ },
+ "gecko_android": {
+ "strict_min_version": "113.0"
+ }
+ },
+ "background": {
+ "page": "pages/background/background.html",
+ "persistent": true
+ },
+ "icons": {
+ "16": "assets/images/libredirect-16.png",
+ "32": "assets/images/libredirect-32.png",
+ "48": "assets/images/libredirect-48.png",
+ "128": "assets/images/libredirect-128.png"
+ },
+ "permissions": ["webRequest", "webRequestBlocking", "storage", "clipboardWrite", "contextMenus", "<all_urls>"],
+ "optional_permissions": ["bookmarks"],
+ "browser_action": {
+ "default_title": "__MSG_extensionName__",
+ "browser_style": false,
+ "default_popup": "pages/popup/popup.html",
+ "default_icon": {
+ "16": "assets/images/libredirect-16.png",
+ "32": "assets/images/libredirect-32.png",
+ "48": "assets/images/libredirect-48.png",
+ "128": "assets/images/libredirect-128.png"
+ }
+ },
+ "options_ui": {
+ "page": "pages/options/index.html",
+ "browser_style": false,
+ "open_in_tab": true
+ },
+ "chrome_settings_overrides": {
+ "search_provider": {
+ "name": "__MSG_extensionName__",
+ "keyword": "@libredirect",
+ "favicon_url": "https://raw.githubusercontent.com/libredirect/libredirect/master/src/assets/images/libredirect-16.png",
+ "search_url": "https://search.libredirect.invalid/?q={searchTerms}",
+ "encoding": "UTF-8",
+ "is_default": false
+ }
+ },
+ "commands": {
+ "switchInstance": {
+ "suggested_key": {
+ "default": "Alt+Shift+L"
+ },
+ "description": "__MSG_switchInstance__"
+ },
+ "copyRaw": {
+ "suggested_key": {
+ "default": "Alt+Shift+C"
+ },
+ "description": "Copies the original link. Ex: Copies the original twitter link while in the nitter website"
+ },
+ "reverse": {
+ "suggested_key": {
+ "default": "Alt+Shift+O"
+ },
+ "description": "Redirect to the original link. Ex: Redirects to the original twitter link while in the nitter website"
+ },
+ "redirect": {
+ "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"
+}
diff --git a/src/pages/background/background.html b/src/pages/background/background.html
index 542df18b..787d419e 100644
--- a/src/pages/background/background.html
+++ b/src/pages/background/background.html
@@ -1,7 +1,7 @@
-<!DOCTYPE html>
+<!doctype html>
<html>
- <head>
- <meta charset="utf-8" />
- <script type="module" src="background.js"></script>
- </head>
+ <head>
+ <meta charset="utf-8" />
+ <script type="module" src="background.js"></script>
+ </head>
</html>
diff --git a/src/pages/background/background.js b/src/pages/background/background.js
index 0c4d0923..e3d9fb31 100644
--- a/src/pages/background/background.js
+++ b/src/pages/background/background.js
@@ -7,310 +7,324 @@ const isChrome = browser.runtime.getBrowserInfo === undefined
window.browser = window.browser || window.chrome
browser.runtime.onInstalled.addListener(async details => {
- if (details.previousVersion != browser.runtime.getManifest().version) {
- // ^Used to prevent this running when debugging with auto-reload
- if (details.reason == "install") {
- if (!(await utils.getOptions())) {
- await servicesHelper.initDefaults()
- }
- browser.runtime.openOptionsPage()
- }
- else if (details.reason == "update") {
- if (details.previousVersion == '2.5.2') {
- await servicesHelper.upgradeOptions()
- await servicesHelper.processUpdate()
- } else {
- await servicesHelper.processUpdate()
- }
- }
- }
+ if (details.previousVersion != browser.runtime.getManifest().version) {
+ // ^Used to prevent this running when debugging with auto-reload
+ if (details.reason == "install") {
+ if (!(await utils.getOptions())) {
+ await servicesHelper.initDefaults()
+ }
+ browser.runtime.openOptionsPage()
+ } else if (details.reason == "update") {
+ if (details.previousVersion == "2.5.2") {
+ await servicesHelper.upgradeOptions()
+ await servicesHelper.processUpdate()
+ } else {
+ await servicesHelper.processUpdate()
+ }
+ }
+ }
})
let tabIdRedirects = {}
// true == Always redirect, false == Never redirect, null/undefined == follow options for services
browser.webRequest.onBeforeRequest.addListener(
- details => {
- const url = new URL(details.url)
- const old_href = url.href
- if (new RegExp(/^chrome-extension:\/{2}.*\/instances\/.*.json$/).test(url.href) && details.type == "xmlhttprequest") return
- let initiator
- try {
- if (details.originUrl) initiator = new URL(details.originUrl)
- else if (details.initiator && details.initiator !== "null") initiator = new URL(details.initiator)
- } catch {
- return null
- }
- if (tabIdRedirects[details.tabId] == false) return null
- let newUrl = servicesHelper.redirect(url, details.type, initiator, tabIdRedirects[details.tabId], details.incognito)
+ details => {
+ const url = new URL(details.url)
+ const old_href = url.href
+ if (new RegExp(/^chrome-extension:\/{2}.*\/instances\/.*.json$/).test(url.href) && details.type == "xmlhttprequest")
+ return
+ let initiator
+ try {
+ if (details.originUrl) initiator = new URL(details.originUrl)
+ else if (details.initiator && details.initiator !== "null") initiator = new URL(details.initiator)
+ } catch {
+ return null
+ }
+ 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 (details.type == "main_frame")
- newUrl = "BYPASSTAB"
- else
- return null
- }
+ if (servicesHelper.isException(url)) {
+ if (details.type == "main_frame") newUrl = "BYPASSTAB"
+ else return null
+ }
- if (!newUrl) {
- const match = url.href.match(/^https?:\/{2}.*\.libredirect\.invalid.*/)
- if (match) {
- browser.tabs.update({
- url: browser.runtime.getURL(`/pages/messages/no_instance.html`)
- });
- }
- }
+ if (!newUrl) {
+ const match = url.href.match(/^https?:\/{2}.*\.libredirect\.invalid.*/)
+ if (match) {
+ browser.tabs.update({
+ url: browser.runtime.getURL(`/pages/messages/no_instance.html`),
+ })
+ }
+ }
- if (newUrl) {
- if (newUrl === "CANCEL") {
- console.log(`Canceled ${url}`)
- return { cancel: true }
- }
- if (newUrl === "BYPASSTAB") {
- console.log(`Bypassed ${details.tabId} ${url}`)
- if (tabIdRedirects[details.tabId] != false) tabIdRedirects[details.tabId] = false
- return null
- }
- console.log("Redirecting", old_href, "=>", newUrl)
- return { redirectUrl: newUrl }
- }
- return null
- },
- { urls: ["<all_urls>"] },
- ["blocking"]
+ if (newUrl) {
+ if (newUrl === "CANCEL") {
+ console.log(`Canceled ${url}`)
+ return { cancel: true }
+ }
+ if (newUrl === "BYPASSTAB") {
+ console.log(`Bypassed ${details.tabId} ${url}`)
+ if (tabIdRedirects[details.tabId] != false) tabIdRedirects[details.tabId] = false
+ return null
+ }
+ console.log("Redirecting", old_href, "=>", newUrl)
+ return { redirectUrl: newUrl }
+ }
+ return null
+ },
+ { urls: ["<all_urls>"] },
+ ["blocking"]
)
browser.tabs.onRemoved.addListener(tabId => {
- if (tabIdRedirects[tabId] != undefined) {
- delete tabIdRedirects[tabId]
- console.log(`Removed tab ${tabId} from tabIdRedirects`)
- }
+ if (tabIdRedirects[tabId] != undefined) {
+ delete tabIdRedirects[tabId]
+ console.log(`Removed tab ${tabId} from tabIdRedirects`)
+ }
})
browser.commands.onCommand.addListener(async command => {
- browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
- const url = new URL(tabs[0].url)
- switch (command) {
- case "switchInstance": {
- const newUrl = await servicesHelper.switchInstance(url)
- if (newUrl) browser.tabs.update({ url: newUrl })
- break
- }
- case "copyRaw":
- servicesHelper.copyRaw(url)
- break
- case "redirect":
- browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
- if (tabs[0].url) {
- const url = new URL(tabs[0].url)
- const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
- if (newUrl) {
- browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
- tabIdRedirects[tabs[0].id] = true
- })
- }
- }
- })
- break
- case "reverse":
- browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
- if (tabs[0].url) {
- const url = new URL(tabs[0].url)
- const newUrl = await servicesHelper.reverse(url)
- if (newUrl) {
- browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
- tabIdRedirects[tabs[0].id] = false
- })
- }
- }
- })
- break
- }
- })
+ browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+ const url = new URL(tabs[0].url)
+ switch (command) {
+ case "switchInstance": {
+ const newUrl = await servicesHelper.switchInstance(url)
+ if (newUrl) browser.tabs.update({ url: newUrl })
+ break
+ }
+ case "copyRaw":
+ servicesHelper.copyRaw(url)
+ break
+ case "redirect":
+ browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+ if (tabs[0].url) {
+ const url = new URL(tabs[0].url)
+ const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
+ if (newUrl) {
+ browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
+ tabIdRedirects[tabs[0].id] = true
+ })
+ }
+ }
+ })
+ break
+ case "reverse":
+ browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+ if (tabs[0].url) {
+ const url = new URL(tabs[0].url)
+ const newUrl = await servicesHelper.reverse(url)
+ if (newUrl) {
+ browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
+ tabIdRedirects[tabs[0].id] = false
+ })
+ }
+ }
+ })
+ break
+ }
+ })
})
-browser.contextMenus.create({ id: "settingsTab", title: browser.i18n.getMessage("settings"), 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: "settingsTab",
+ title: browser.i18n.getMessage("settings"),
+ 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: "redirectLinkInNewTab", title: 'Redirect In New Tab', 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: "copyReverseLink", title: 'Copy Original', 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: "redirectLink", title: "Redirect", 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: "reverseLinkInNewTab", title: "Redirect To Original In New Tab", 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: "bypassLinkInNewTab", title: "Bypass In New Tab", contexts: ["link"] })
if (!isChrome) {
- 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: "reverseBookmark", title: 'Redirect To Original', contexts: ["bookmark"] })
- browser.contextMenus.create({ id: "reverseBookmarkInNewTab", title: 'Redirect To Original In New Tab', 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.create({ id: "redirectBookmark", title: "Redirect", 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: "reverseBookmarkInNewTab",
+ title: "Redirect To Original In New Tab",
+ 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) => {
- switch (info.menuItemId) {
- case 'switchInstanceTab': {
- const url = new URL(info.pageUrl)
- const newUrl = await servicesHelper.switchInstance(url)
- if (newUrl) browser.tabs.update({ url: newUrl })
- return
- }
- case 'settingsTab':
- browser.runtime.openOptionsPage()
- return
- case 'copyReverseTab':
- browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
- if (tabs[0].url) {
- const url = new URL(tabs[0].url)
- servicesHelper.copyRaw(url)
- }
- })
- return
- case 'reverseTab':
- browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
- if (tabs[0].url) {
- const url = new URL(tabs[0].url)
- const newUrl = await servicesHelper.reverse(url)
- if (newUrl) {
- browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
- tabIdRedirects[tabs[0].id] = false
- })
- }
- }
- })
- return
- case 'redirectTab':
- browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
- if (tabs[0].url) {
- const url = new URL(tabs[0].url)
- const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
- if (newUrl) {
- browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
- tabIdRedirects[tabs[0].id] = true
- })
- }
- }
- })
- return
- case 'copyReverseLink': {
- const url = new URL(info.linkUrl)
- await servicesHelper.copyRaw(url)
- return
- }
- case 'redirectLink':
- case 'redirectLinkInNewTab': {
- const url = new URL(info.linkUrl)
- const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
- if (newUrl) {
- if (info.menuItemId == "redirectLink") browser.tabs.update({ url: newUrl })
- else browser.tabs.create({ url: newUrl })
- }
- return
- }
- case 'reverseLink':
- case 'reverseLinkInNewTab': {
- const url = new URL(info.linkUrl)
- const newUrl = await servicesHelper.reverse(url)
- if (newUrl) {
- if (info.menuItemId == "reverseLink") {
- browser.tabs.update({ url: newUrl }, tab => {
- tabIdRedirects[tab.id] = false
- })
- } else {
- browser.tabs.create({ url: newUrl }, tab => {
- tabIdRedirects[tab.id] = false
- })
- }
- }
- return
- }
- case 'bypassLink':
- case 'bypassLinkInNewTab': {
- const url = new URL(info.linkUrl)
- if (info.menuItemId == "bypassLink") {
- browser.tabs.update({ url: url.href }, tab => {
- tabIdRedirects[tab.id] = false
- })
- } else {
- browser.tabs.create({ url: url.href }, tab => {
- tabIdRedirects[tab.id] = false
- })
- }
- return
- }
- case 'copyReverseBookmark':
- browser.bookmarks.get(info.bookmarkId, bookmarks => {
- const url = new URL(bookmarks[0].url)
- servicesHelper.copyRaw(url)
- });
- return
- case 'redirectBookmark':
- case 'redirectBookmarkInNewTab':
- browser.bookmarks.get(info.bookmarkId, bookmarks => {
- const url = new URL(bookmarks[0].url)
- const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
- if (newUrl) {
- if (info.menuItemId == 'redirectBookmark') browser.tabs.update({ url: newUrl })
- else browser.tabs.create({ url: newUrl })
- }
- })
- return
- case 'reverseBookmark':
- case 'reverseBookmarkInNewTab':
- browser.bookmarks.get(info.bookmarkId, async bookmarks => {
- const url = new URL(bookmarks[0].url)
- const newUrl = await servicesHelper.reverse(url)
- if (newUrl) {
- if (info.menuItemId == "reverseBookmark") {
- browser.tabs.update({ url: newUrl }, tab => {
- tabIdRedirects[tab.id] = false
- })
- } else {
- browser.tabs.create({ url: newUrl }, tab => {
- tabIdRedirects[tab.id] = false
- })
- }
- }
- })
- return
- case 'bypassBookmark':
- case 'bypassBookmarkInNewTab':
- browser.bookmarks.get(info.bookmarkId, async bookmarks => {
- const url = new URL(bookmarks[0].url)
- if (info.menuItemId == "bypassBookmark") {
- browser.tabs.update({ url: url.href }, tab => tabIdRedirects[tab.id] = false)
- } else {
- browser.tabs.create({ url: url.href }, tab => tabIdRedirects[tab.id] = false)
- }
- return
- })
- }
+browser.contextMenus.onClicked.addListener(async info => {
+ switch (info.menuItemId) {
+ case "switchInstanceTab": {
+ const url = new URL(info.pageUrl)
+ const newUrl = await servicesHelper.switchInstance(url)
+ if (newUrl) browser.tabs.update({ url: newUrl })
+ return
+ }
+ case "settingsTab":
+ browser.runtime.openOptionsPage()
+ return
+ case "copyReverseTab":
+ browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+ if (tabs[0].url) {
+ const url = new URL(tabs[0].url)
+ servicesHelper.copyRaw(url)
+ }
+ })
+ return
+ case "reverseTab":
+ browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+ if (tabs[0].url) {
+ const url = new URL(tabs[0].url)
+ const newUrl = await servicesHelper.reverse(url)
+ if (newUrl) {
+ browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
+ tabIdRedirects[tabs[0].id] = false
+ })
+ }
+ }
+ })
+ return
+ case "redirectTab":
+ browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+ if (tabs[0].url) {
+ const url = new URL(tabs[0].url)
+ const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
+ if (newUrl) {
+ browser.tabs.update(tabs[0].id, { url: newUrl }, () => {
+ tabIdRedirects[tabs[0].id] = true
+ })
+ }
+ }
+ })
+ return
+ case "copyReverseLink": {
+ const url = new URL(info.linkUrl)
+ await servicesHelper.copyRaw(url)
+ return
+ }
+ case "redirectLink":
+ case "redirectLinkInNewTab": {
+ const url = new URL(info.linkUrl)
+ const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
+ if (newUrl) {
+ if (info.menuItemId == "redirectLink") browser.tabs.update({ url: newUrl })
+ else browser.tabs.create({ url: newUrl })
+ }
+ return
+ }
+ case "reverseLink":
+ case "reverseLinkInNewTab": {
+ const url = new URL(info.linkUrl)
+ const newUrl = await servicesHelper.reverse(url)
+ if (newUrl) {
+ if (info.menuItemId == "reverseLink") {
+ browser.tabs.update({ url: newUrl }, tab => {
+ tabIdRedirects[tab.id] = false
+ })
+ } else {
+ browser.tabs.create({ url: newUrl }, tab => {
+ tabIdRedirects[tab.id] = false
+ })
+ }
+ }
+ return
+ }
+ case "bypassLink":
+ case "bypassLinkInNewTab": {
+ const url = new URL(info.linkUrl)
+ if (info.menuItemId == "bypassLink") {
+ browser.tabs.update({ url: url.href }, tab => {
+ tabIdRedirects[tab.id] = false
+ })
+ } else {
+ browser.tabs.create({ url: url.href }, tab => {
+ tabIdRedirects[tab.id] = false
+ })
+ }
+ return
+ }
+ case "copyReverseBookmark":
+ browser.bookmarks.get(info.bookmarkId, bookmarks => {
+ const url = new URL(bookmarks[0].url)
+ servicesHelper.copyRaw(url)
+ })
+ return
+ case "redirectBookmark":
+ case "redirectBookmarkInNewTab":
+ browser.bookmarks.get(info.bookmarkId, bookmarks => {
+ const url = new URL(bookmarks[0].url)
+ const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
+ if (newUrl) {
+ if (info.menuItemId == "redirectBookmark") browser.tabs.update({ url: newUrl })
+ else browser.tabs.create({ url: newUrl })
+ }
+ })
+ return
+ case "reverseBookmark":
+ case "reverseBookmarkInNewTab":
+ browser.bookmarks.get(info.bookmarkId, async bookmarks => {
+ const url = new URL(bookmarks[0].url)
+ const newUrl = await servicesHelper.reverse(url)
+ if (newUrl) {
+ if (info.menuItemId == "reverseBookmark") {
+ browser.tabs.update({ url: newUrl }, tab => {
+ tabIdRedirects[tab.id] = false
+ })
+ } else {
+ browser.tabs.create({ url: newUrl }, tab => {
+ tabIdRedirects[tab.id] = false
+ })
+ }
+ }
+ })
+ return
+ case "bypassBookmark":
+ case "bypassBookmarkInNewTab":
+ browser.bookmarks.get(info.bookmarkId, async bookmarks => {
+ const url = new URL(bookmarks[0].url)
+ if (info.menuItemId == "bypassBookmark") {
+ browser.tabs.update({ url: url.href }, tab => (tabIdRedirects[tab.id] = false))
+ } else {
+ browser.tabs.create({ url: url.href }, tab => (tabIdRedirects[tab.id] = false))
+ }
+ return
+ })
+ }
})
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
- if (request == "reverseTab") {
- browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
- if (tabs[0].url) {
- const url = new URL(tabs[0].url)
- const newUrl = await servicesHelper.reverse(url)
- if (newUrl) browser.tabs.update(tabs[0].id, { url: newUrl }, () => tabIdRedirects[tabs[0].id] = false)
- }
- })
- }
- else if (request == "redirectTab") {
- browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
- if (tabs[0].url) {
- const url = new URL(tabs[0].url)
- const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
- if (newUrl) browser.tabs.update(tabs[0].id, { url: newUrl }, () => tabIdRedirects[tabs[0].id] = true)
- }
- })
- }
-}) \ No newline at end of file
+ if (request == "reverseTab") {
+ browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+ if (tabs[0].url) {
+ const url = new URL(tabs[0].url)
+ const newUrl = await servicesHelper.reverse(url)
+ if (newUrl) browser.tabs.update(tabs[0].id, { url: newUrl }, () => (tabIdRedirects[tabs[0].id] = false))
+ }
+ })
+ } else if (request == "redirectTab") {
+ browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+ if (tabs[0].url) {
+ const url = new URL(tabs[0].url)
+ const newUrl = servicesHelper.redirect(url, "main_frame", null, true)
+ if (newUrl) browser.tabs.update(tabs[0].id, { url: newUrl }, () => (tabIdRedirects[tabs[0].id] = true))
+ }
+ })
+ }
+})
diff --git a/src/pages/messages/no_instance.html b/src/pages/messages/no_instance.html
index 76ec19cf..358ff506 100644
--- a/src/pages/messages/no_instance.html
+++ b/src/pages/messages/no_instance.html
@@ -1,26 +1,24 @@
-<!DOCTYPE html>
+<!doctype html>
<html lang="en">
-
-<head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link href="../stylesheets/styles.css" rel="stylesheet">
+ <head>
+ <meta charset="UTF-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <link href="../stylesheets/styles.css" rel="stylesheet" />
<title>No instances found</title>
<style>
- #body {
- display: flex;
- justify-content: center;
- align-items: center;
- height: 100vh;
- }
+ #body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ }
</style>
-</head>
+ </head>
-<body>
+ <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>
-</body>
-
-</html> \ No newline at end of file
+ </body>
+</html>
diff --git a/src/pages/options/index.js b/src/pages/options/index.js
index 62708f7b..8d5676e0 100644
--- a/src/pages/options/index.js
+++ b/src/pages/options/index.js
@@ -1,19 +1,19 @@
import utils from "../../assets/javascripts/utils.js"
let config,
- options,
- blacklist,
- redirects,
- divs = {}
+ options,
+ blacklist,
+ redirects,
+ divs = {}
for (const a of document.getElementById("links").getElementsByTagName("a")) {
- if (!a.href.includes("https://")) {
- a.addEventListener("click", e => {
- const path = a.getAttribute("href").replace("#", "")
- loadPage(path)
- e.preventDefault()
- })
- }
+ if (!a.href.includes("https://")) {
+ a.addEventListener("click", e => {
+ const path = a.getAttribute("href").replace("#", "")
+ loadPage(path)
+ e.preventDefault()
+ })
+ }
}
config = await utils.getConfig()
@@ -23,180 +23,180 @@ options = await utils.getOptions()
* @param {string} service
*/
async function changeFrontendsSettings(service) {
- options = await utils.getOptions()
- const opacityDiv = document.getElementById(`${service}-opacity`)
- if (document.getElementById(`${service}-enabled`).checked) {
- opacityDiv.style.pointerEvents = 'auto'
- opacityDiv.style.opacity = 1
- opacityDiv.style.userSelect = 'auto'
- } else {
- opacityDiv.style.pointerEvents = 'none'
- opacityDiv.style.opacity = 0.4
- opacityDiv.style.userSelect = 'none'
- }
- for (const frontend in config.services[service].frontends) {
- if (config.services[service].frontends[frontend].instanceList) {
- const frontendDiv = document.getElementById(frontend)
- if (typeof divs[service].frontend !== "undefined") {
- if (
- frontend == divs[service].frontend.value
- ||
- (config.services[service].frontends[divs[service].frontend.value].desktopApp && divs[service].embedFrontend && frontend == divs[service].embedFrontend.value)
- ) {
- frontendDiv.style.display = ""
- if (config.services[service].frontends[frontend].localhost === true) {
- document.getElementById(`${service}-instance-div`).style.display = ""
+ options = await utils.getOptions()
+ const opacityDiv = document.getElementById(`${service}-opacity`)
+ if (document.getElementById(`${service}-enabled`).checked) {
+ opacityDiv.style.pointerEvents = "auto"
+ opacityDiv.style.opacity = 1
+ opacityDiv.style.userSelect = "auto"
+ } else {
+ opacityDiv.style.pointerEvents = "none"
+ opacityDiv.style.opacity = 0.4
+ opacityDiv.style.userSelect = "none"
+ }
+ for (const frontend in config.services[service].frontends) {
+ if (config.services[service].frontends[frontend].instanceList) {
+ const frontendDiv = document.getElementById(frontend)
+ if (typeof divs[service].frontend !== "undefined") {
+ if (
+ frontend == divs[service].frontend.value ||
+ (config.services[service].frontends[divs[service].frontend.value].desktopApp &&
+ divs[service].embedFrontend &&
+ frontend == divs[service].embedFrontend.value)
+ ) {
+ frontendDiv.style.display = ""
+ if (config.services[service].frontends[frontend].localhost === true) {
+ document.getElementById(`${service}-instance-div`).style.display = ""
- if (options[service].instance == "localhost") {
- frontendDiv.style.display = "none"
- }
- } else {
- document.getElementById(`${service}-instance-div`).style.display = "none"
- }
- } else {
- frontendDiv.style.display = "none"
- }
- }
- }
- }
- if (document.getElementById(`${service}-redirectType`)) {
- const frontend = options[service].frontend
- if (config.services[service].frontends[frontend].embeddable) {
- document.getElementById(`${service}-redirectType`).innerHTML = `
+ if (options[service].instance == "localhost") {
+ frontendDiv.style.display = "none"
+ }
+ } else {
+ document.getElementById(`${service}-instance-div`).style.display = "none"
+ }
+ } else {
+ frontendDiv.style.display = "none"
+ }
+ }
+ }
+ }
+ if (document.getElementById(`${service}-redirectType`)) {
+ const frontend = options[service].frontend
+ if (config.services[service].frontends[frontend].embeddable) {
+ document.getElementById(`${service}-redirectType`).innerHTML = `
<option value="both" data-localise="__MSG_both__">both</options>
<option value="sub_frame" data-localise="__MSG_onlyEmbedded__">Only Embedded</option>
<option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option>
`
- }
- else if (config.services[service].frontends[frontend].desktopApp && Object.values(config.services[service].frontends).some(frontend => frontend.embeddable)) {
- document.getElementById(`${service}-redirectType`).innerHTML = `
+ } else if (
+ config.services[service].frontends[frontend].desktopApp &&
+ 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="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option>
`
- if (options[service].redirectType == "sub_frame") {
- options[service].redirectType = "main_frame"
- browser.storage.local.set({ options })
- }
- } else {
- document.getElementById(`${service}-redirectType`).innerHTML =
- '<option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option>'
- options[service].redirectType = "main_frame"
+ if (options[service].redirectType == "sub_frame") {
+ options[service].redirectType = "main_frame"
+ browser.storage.local.set({ options })
+ }
+ } else {
+ document.getElementById(`${service}-redirectType`).innerHTML =
+ '<option value="main_frame" data-localise="__MSG_onlyNotEmbedded__">Only Not Embedded</option>'
+ options[service].redirectType = "main_frame"
- browser.storage.local.set({ options })
- }
- document.getElementById(`${service}-redirectType`).value = options[service].redirectType
- if (config.services[service].frontends[frontend].desktopApp && options[service].redirectType != "main_frame") {
- document.getElementById(`${service}-embedFrontend-div`).style.display = ''
- document.getElementById(divs[service].embedFrontend.value).style.display = ''
- }
- else if (config.services[service].frontends[frontend].desktopApp && options[service].redirectType == "main_frame") {
- 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
+ browser.storage.local.set({ options })
+ }
+ document.getElementById(`${service}-redirectType`).value = options[service].redirectType
+ if (config.services[service].frontends[frontend].desktopApp && options[service].redirectType != "main_frame") {
+ document.getElementById(`${service}-embedFrontend-div`).style.display = ""
+ document.getElementById(divs[service].embedFrontend.value).style.display = ""
+ } else if (
+ config.services[service].frontends[frontend].desktopApp &&
+ options[service].redirectType == "main_frame"
+ ) {
+ 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
}
/**
* @param {string} path
*/
async function loadPage(path) {
- options = await utils.getOptions()
- for (const section of document.getElementById("pages").getElementsByTagName("section")) section.style.display = "none"
- document.getElementById(`${path}_page`).style.display = "block"
+ options = await utils.getOptions()
+ for (const section of document.getElementById("pages").getElementsByTagName("section")) section.style.display = "none"
+ document.getElementById(`${path}_page`).style.display = "block"
- for (const element of document.getElementsByClassName("title")) {
- const a = element.getElementsByTagName('a')[0]
- if (a.getAttribute("href") == `#${path}`) {
- element.classList.add("selected")
- } else {
- element.classList.remove("selected")
- }
- }
+ for (const element of document.getElementsByClassName("title")) {
+ const a = element.getElementsByTagName("a")[0]
+ if (a.getAttribute("href") == `#${path}`) {
+ element.classList.add("selected")
+ } else {
+ element.classList.remove("selected")
+ }
+ }
- for (const service in config.services) {
- if (options[service].enabled) {
- document.getElementById(`${service}-link`).style.opacity = 1
- } else {
- document.getElementById(`${service}-link`).style.opacity = 0.4
- }
- }
+ for (const service in config.services) {
+ if (options[service].enabled) {
+ document.getElementById(`${service}-link`).style.opacity = 1
+ } else {
+ 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') {
- const service = path;
- divs[service] = {}
- for (const option in config.services[service].options) {
- divs[service][option] = document.getElementById(`${service}-${option}`)
- if (typeof config.services[service].options[option] == "boolean") divs[service][option].checked = options[service][option]
- else divs[service][option].value = options[service][option]
- divs[service][option].addEventListener("change", async () => {
- let options = await utils.getOptions()
- if (typeof config.services[service].options[option] == "boolean")
- options[service][option] = divs[service][option].checked
- else
- options[service][option] = divs[service][option].value
- browser.storage.local.set({ options })
- changeFrontendsSettings(service)
- })
- }
- changeFrontendsSettings(service)
+ if (path != "general") {
+ const service = path
+ divs[service] = {}
+ for (const option in config.services[service].options) {
+ divs[service][option] = document.getElementById(`${service}-${option}`)
+ if (typeof config.services[service].options[option] == "boolean")
+ divs[service][option].checked = options[service][option]
+ else divs[service][option].value = options[service][option]
+ divs[service][option].addEventListener("change", async () => {
+ let options = await utils.getOptions()
+ if (typeof config.services[service].options[option] == "boolean")
+ options[service][option] = divs[service][option].checked
+ else options[service][option] = divs[service][option].value
+ browser.storage.local.set({ options })
+ changeFrontendsSettings(service)
+ })
+ }
+ changeFrontendsSettings(service)
- blacklist = await utils.getBlacklist(options)
- redirects = await utils.getList(options)
- for (const frontend in config.services[service].frontends) {
- if (config.services[service].frontends[frontend].instanceList) {
- if (redirects == 'disabled' || blacklist == 'disabled') {
- document.getElementById(frontend).getElementsByClassName('clearnet')[0].style.display = 'none'
- document.getElementById(frontend).getElementsByClassName('ping')[0].style.display = 'none'
- }
- else if (!redirects || !blacklist) {
- document.getElementById(frontend)
- .getElementsByClassName('clearnet')[0]
- .getElementsByClassName("checklist")[0]
- .getElementsByClassName('loading')[0]
- .innerHTML = 'Could not fetch instances.'
- }
- else {
- createList(frontend)
- }
- }
- }
+ blacklist = await utils.getBlacklist(options)
+ redirects = await utils.getList(options)
+ for (const frontend in config.services[service].frontends) {
+ if (config.services[service].frontends[frontend].instanceList) {
+ if (redirects == "disabled" || blacklist == "disabled") {
+ document.getElementById(frontend).getElementsByClassName("clearnet")[0].style.display = "none"
+ document.getElementById(frontend).getElementsByClassName("ping")[0].style.display = "none"
+ } else if (!redirects || !blacklist) {
+ document
+ .getElementById(frontend)
+ .getElementsByClassName("clearnet")[0]
+ .getElementsByClassName("checklist")[0]
+ .getElementsByClassName("loading")[0].innerHTML = "Could not fetch instances."
+ } else {
+ createList(frontend)
+ }
+ }
+ }
- for (const frontend in config.services[service].frontends) {
- if (config.services[service].frontends[frontend].instanceList) {
- processCustomInstances(frontend)
- document.getElementById(`ping-${frontend}`).addEventListener("click", async () => {
- document.getElementById(`ping-${frontend}`).getElementsByTagName('x')[0].innerHTML = "Pinging..."
- await ping(frontend)
- document.getElementById(`ping-${frontend}`).getElementsByTagName('x')[0].innerHTML = "Ping instances"
- })
- }
- }
- }
+ for (const frontend in config.services[service].frontends) {
+ if (config.services[service].frontends[frontend].instanceList) {
+ processCustomInstances(frontend)
+ document.getElementById(`ping-${frontend}`).addEventListener("click", async () => {
+ document.getElementById(`ping-${frontend}`).getElementsByTagName("x")[0].innerHTML = "Pinging..."
+ await ping(frontend)
+ document.getElementById(`ping-${frontend}`).getElementsByTagName("x")[0].innerHTML = "Ping instances"
+ })
+ }
+ }
+ }
}
async function calcCustomInstances(frontend) {
- let options = await utils.getOptions()
- let customInstances = options[frontend]
- const pingCache = await utils.getPingCache()
+ let options = await utils.getOptions()
+ let customInstances = options[frontend]
+ const pingCache = await utils.getPingCache()
- document.getElementById(frontend).getElementsByClassName("custom-checklist")[0].innerHTML = customInstances
- .map(
- x => {
- const time = pingCache[x];
- if (time) {
- var { color, text } = processTime(time);
- }
- const timeText = time
- ? `<span class="ping" style="color:${color};">${text}</span>`
- : "";
- const custom = isCustomInstance(frontend, x) ? "" : `<span>custom</span>`
- return `<div>
+ document.getElementById(frontend).getElementsByClassName("custom-checklist")[0].innerHTML = customInstances
+ .map(x => {
+ const time = pingCache[x]
+ if (time) {
+ var { color, text } = processTime(time)
+ }
+ const timeText = time ? `<span class="ping" style="color:${color};">${text}</span>` : ""
+ const custom = isCustomInstance(frontend, x) ? "" : `<span>custom</span>`
+ return `<div>
<x>
<a href="${x}" target="_blank">${x}</a>
${timeText}
@@ -209,87 +209,97 @@ async function calcCustomInstances(frontend) {
</button>
</div>
<hr>`
- })
- .join("\n")
- for (const item of customInstances) {
- document.getElementById(frontend).getElementsByClassName(`clear-${item}`)[0].addEventListener("click", async () => {
- const index = customInstances.indexOf(item)
- if (index > -1) customInstances.splice(index, 1)
- options = await utils.getOptions()
- options[frontend] = customInstances
- browser.storage.local.set({ options }, async () => {
- calcCustomInstances(frontend)
- createList(frontend)
- })
- })
- }
+ })
+ .join("\n")
+ for (const item of customInstances) {
+ document
+ .getElementById(frontend)
+ .getElementsByClassName(`clear-${item}`)[0]
+ .addEventListener("click", async () => {
+ const index = customInstances.indexOf(item)
+ if (index > -1) customInstances.splice(index, 1)
+ options = await utils.getOptions()
+ options[frontend] = customInstances
+ browser.storage.local.set({ options }, async () => {
+ calcCustomInstances(frontend)
+ createList(frontend)
+ })
+ })
+ }
}
async function processCustomInstances(frontend) {
- calcCustomInstances(frontend)
- document.getElementById(frontend).getElementsByClassName("custom-instance-form")[0].addEventListener("submit", async event => {
- event.preventDefault()
- let options = await utils.getOptions()
- let customInstances = options[frontend]
- let frontendCustomInstanceInput = document.getElementById(frontend).getElementsByClassName("custom-instance")[0]
- try {
- var url = new URL(frontendCustomInstanceInput.value)
- } catch (error) {
- return
- }
- let protocolHostVar = utils.protocolHost(url)
- if (frontendCustomInstanceInput.validity.valid) {
- if (!customInstances.includes(protocolHostVar)) {
- customInstances.push(protocolHostVar)
- options = await utils.getOptions()
- options[frontend] = customInstances
- browser.storage.local.set({ options }, () => {
- frontendCustomInstanceInput.value = ""
- calcCustomInstances(frontend)
- })
- }
- }
- })
+ calcCustomInstances(frontend)
+ document
+ .getElementById(frontend)
+ .getElementsByClassName("custom-instance-form")[0]
+ .addEventListener("submit", async event => {
+ event.preventDefault()
+ let options = await utils.getOptions()
+ let customInstances = options[frontend]
+ let frontendCustomInstanceInput = document.getElementById(frontend).getElementsByClassName("custom-instance")[0]
+ try {
+ var url = new URL(frontendCustomInstanceInput.value)
+ } catch (error) {
+ return
+ }
+ let protocolHostVar = utils.protocolHost(url)
+ if (frontendCustomInstanceInput.validity.valid) {
+ if (!customInstances.includes(protocolHostVar)) {
+ customInstances.push(protocolHostVar)
+ options = await utils.getOptions()
+ options[frontend] = customInstances
+ browser.storage.local.set({ options }, () => {
+ frontendCustomInstanceInput.value = ""
+ calcCustomInstances(frontend)
+ })
+ }
+ }
+ })
}
/**
* @param {string} frontend
*/
async function createList(frontend) {
- const pingCache = await utils.getPingCache()
- const options = await utils.getOptions()
- for (const network in config.networks) {
- const checklist = document.getElementById(frontend).getElementsByClassName(network)[0].getElementsByClassName("checklist")[0]
+ const pingCache = await utils.getPingCache()
+ const options = await utils.getOptions()
+ for (const network in config.networks) {
+ const checklist = document
+ .getElementById(frontend)
+ .getElementsByClassName(network)[0]
+ .getElementsByClassName("checklist")[0]
- if (!redirects[frontend]) {
- checklist.innerHTML = '<div class="block block-option">No instances found.</div>'
- break
- }
+ if (!redirects[frontend]) {
+ checklist.innerHTML = '<div class="block block-option">No instances found.</div>'
+ break
+ }
- const instances = redirects[frontend][network]
- if (!instances || instances.length === 0) continue
+ const instances = redirects[frontend][network]
+ 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))
- const content = instances
- .map(x => {
- const cloudflare = blacklist.cloudflare.includes(x) ?
- `<a target="_blank" href="https://libredirect.github.io/docs.html#instances">
+ instances.sort((a, b) => blacklist.cloudflare.includes(a) && !blacklist.cloudflare.includes(b))
+ const content = instances.map(x => {
+ const cloudflare = blacklist.cloudflare.includes(x)
+ ? `<a target="_blank" href="https://libredirect.github.io/docs.html#instances">
<span style="color:red;">cloudflare</span>
- </a>` : ""
+ </a>`
+ : ""
- let time = pingCache[x]
- let timeText = ""
- if (time) {
- const { color, text } = processTime(time)
- timeText = `<span class="ping" style="color:${color};">${text}</span>`
- }
+ let time = pingCache[x]
+ let timeText = ""
+ if (time) {
+ const { color, text } = processTime(time)
+ 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(" ")
- return `<div class="frontend">
+ const warnings = [cloudflare, timeText, chosen].join(" ")
+ return `<div class="frontend">
<x>
<a href="${x}" target="_blank">${x}</a>
${warnings}
@@ -300,30 +310,29 @@ async function createList(frontend) {
</svg>
</button>
</div>`
- })
+ })
- checklist.innerHTML = [
- `<div class="block block-option">
+ checklist.innerHTML = [
+ `<div class="block block-option">
<label>${utils.camelCase(network)}</label>
</div>`,
- ...content,
- "<br>"
- ].join("\n<hr>\n")
+ ...content,
+ "<br>",
+ ].join("\n<hr>\n")
- for (const instance of instances) {
- checklist.getElementsByClassName(`add-${instance}`)[0]
- .addEventListener("click", async () => {
- let options = await utils.getOptions()
- if (!options[frontend].includes(instance)) {
- options[frontend].push(instance)
- browser.storage.local.set({ options }, () => {
- calcCustomInstances(frontend)
- createList(frontend)
- })
- }
- })
- }
- }
+ for (const instance of instances) {
+ checklist.getElementsByClassName(`add-${instance}`)[0].addEventListener("click", async () => {
+ let options = await utils.getOptions()
+ if (!options[frontend].includes(instance)) {
+ options[frontend].push(instance)
+ browser.storage.local.set({ options }, () => {
+ calcCustomInstances(frontend)
+ createList(frontend)
+ })
+ }
+ })
+ }
+ }
}
const r = window.location.href.match(/#(.*)/)
@@ -334,59 +343,57 @@ else loadPage("general")
* @param {string} frontend
*/
async function ping(frontend) {
- const instanceElements = [
- ...document.getElementById(frontend).getElementsByClassName("custom-checklist")[0].getElementsByTagName('x'),
- ...document.getElementById(frontend).getElementsByClassName('clearnet')[0].getElementsByTagName('x')
- ]
+ const instanceElements = [
+ ...document.getElementById(frontend).getElementsByClassName("custom-checklist")[0].getElementsByTagName("x"),
+ ...document.getElementById(frontend).getElementsByClassName("clearnet")[0].getElementsByTagName("x"),
+ ]
- let pingCache = await utils.getPingCache()
- let redundancyList = {}
- for (const element of instanceElements) {
- let span = element.getElementsByClassName('ping')[0]
- if (!span) span = document.createElement('span')
- span.classList = ['ping']
- span.innerHTML = '<span style="color:lightblue">pinging...</span>'
- element.appendChild(span)
- const href = element.getElementsByTagName('a')[0].href
- const innerHTML = element.getElementsByTagName('a')[0].innerHTML
- const time = redundancyList[innerHTML] ?? await utils.ping(href)
- const { color, text } = processTime(time)
- span.innerHTML = `<span style="color:${color};">${text}</span>`
- pingCache[innerHTML] = time
- redundancyList[innerHTML] = time
+ let pingCache = await utils.getPingCache()
+ let redundancyList = {}
+ for (const element of instanceElements) {
+ let span = element.getElementsByClassName("ping")[0]
+ if (!span) span = document.createElement("span")
+ span.classList = ["ping"]
+ span.innerHTML = '<span style="color:lightblue">pinging...</span>'
+ element.appendChild(span)
+ const href = element.getElementsByTagName("a")[0].href
+ const innerHTML = element.getElementsByTagName("a")[0].innerHTML
+ const time = redundancyList[innerHTML] ?? (await utils.ping(href))
+ const { color, text } = processTime(time)
+ span.innerHTML = `<span style="color:${color};">${text}</span>`
+ pingCache[innerHTML] = time
+ redundancyList[innerHTML] = time
- browser.storage.local.set({ pingCache })
- }
+ browser.storage.local.set({ pingCache })
+ }
}
/**
* @param {number} time
*/
function processTime(time) {
- let text
- let color
- if (time < 5000) {
- text = `${time}ms`
- if (time <= 1000) color = "green"
- else if (time <= 2000) color = "orange"
- }
- else if (time >= 5000) {
- color = "red"
- if (time == 5000) text = "5000ms+"
- if (time > 5000) text = `Error: ${time - 5000}`
- }
- else {
- color = "red"
- text = 'Server not found'
- }
- return { color, text }
+ let text
+ let color
+ if (time < 5000) {
+ text = `${time}ms`
+ if (time <= 1000) color = "green"
+ else if (time <= 2000) color = "orange"
+ } else if (time >= 5000) {
+ color = "red"
+ if (time == 5000) text = "5000ms+"
+ if (time > 5000) text = `Error: ${time - 5000}`
+ } else {
+ color = "red"
+ text = "Server not found"
+ }
+ return { color, text }
}
function isCustomInstance(frontend, instance) {
- for (const network in config.networks) {
- if (!redirects[frontend]) return false;
- const instances = redirects[frontend][network]
- if (instances.includes(instance)) return true
- }
- return false
-} \ No newline at end of file
+ for (const network in config.networks) {
+ if (!redirects[frontend]) return false
+ const instances = redirects[frontend][network]
+ if (instances.includes(instance)) return true
+ }
+ return false
+}
diff --git a/src/pages/options/init.js b/src/pages/options/init.js
index baf3eebe..07da1859 100644
--- a/src/pages/options/init.js
+++ b/src/pages/options/init.js
@@ -5,46 +5,46 @@ import utils from "../../assets/javascripts/utils.js"
import servicesHelper from "../../assets/javascripts/services.js"
if (!(await utils.getOptions())) {
- await servicesHelper.initDefaults()
+ await servicesHelper.initDefaults()
}
async function changeTheme() {
- switch ((await utils.getOptions()).theme) {
- case "dark":
- document.body.classList.add("dark-theme")
- document.body.classList.remove("light-theme")
- for (const element of document.body.getElementsByClassName('dark')) {
- element.style.display = 'none';
- }
- break
- case "light":
- document.body.classList.add("light-theme")
- document.body.classList.remove("dark-theme")
- for (const element of document.body.getElementsByClassName('light')) {
- element.style.display = 'none';
- }
- break
- default:
- if (matchMedia("(prefers-color-scheme: light)").matches) {
- document.body.classList.add("light-theme")
- document.body.classList.remove("dark-theme")
- for (const element of document.body.getElementsByClassName('light')) {
- element.style.display = 'none';
- }
- } else {
- document.body.classList.add("dark-theme")
- document.body.classList.remove("light-theme")
- for (const element of document.body.getElementsByClassName('dark')) {
- element.style.display = 'none';
- }
- }
- }
+ switch ((await utils.getOptions()).theme) {
+ case "dark":
+ document.body.classList.add("dark-theme")
+ document.body.classList.remove("light-theme")
+ for (const element of document.body.getElementsByClassName("dark")) {
+ element.style.display = "none"
+ }
+ break
+ case "light":
+ document.body.classList.add("light-theme")
+ document.body.classList.remove("dark-theme")
+ for (const element of document.body.getElementsByClassName("light")) {
+ element.style.display = "none"
+ }
+ break
+ default:
+ if (matchMedia("(prefers-color-scheme: light)").matches) {
+ document.body.classList.add("light-theme")
+ document.body.classList.remove("dark-theme")
+ for (const element of document.body.getElementsByClassName("light")) {
+ element.style.display = "none"
+ }
+ } else {
+ document.body.classList.add("dark-theme")
+ document.body.classList.remove("light-theme")
+ for (const element of document.body.getElementsByClassName("dark")) {
+ element.style.display = "none"
+ }
+ }
+ }
}
changeTheme()
if (["ar", "iw", "ku", "fa", "ur"].includes(browser.i18n.getUILanguage())) {
- document.getElementsByTagName("body")[0].classList.add("rtl")
- document.getElementsByTagName("body")[0].dir = "rtl"
+ document.getElementsByTagName("body")[0].classList.add("rtl")
+ document.getElementsByTagName("body")[0].dir = "rtl"
}
localise.localisePage()
diff --git a/src/pages/options/widgets/general.js b/src/pages/options/widgets/general.js
index 30a8a0c8..b9ddfd08 100644
--- a/src/pages/options/widgets/general.js
+++ b/src/pages/options/widgets/general.js
@@ -7,29 +7,29 @@ import servicesHelper from "../../../assets/javascripts/services.js"
const isChrome = browser.runtime.getBrowserInfo === undefined
async function setOption(option, type, event) {
- let options = await utils.getOptions()
- switch (type) {
- case "select":
- options[option] = event.target.options[event.target.options.selectedIndex].value
- break;
- case "checkbox":
- options[option] = event.target.checked
- break;
- case "range":
- options[option] = event.target.value
- break;
- }
- browser.storage.local.set({ options })
+ let options = await utils.getOptions()
+ switch (type) {
+ case "select":
+ options[option] = event.target.options[event.target.options.selectedIndex].value
+ break
+ case "checkbox":
+ options[option] = event.target.checked
+ break
+ case "range":
+ options[option] = event.target.value
+ break
+ }
+ browser.storage.local.set({ options })
}
const exportSettingsElement = document.getElementById("export-settings")
async function exportSettings() {
- const options = await utils.getOptions()
- options.version = browser.runtime.getManifest().version
- let resultString = JSON.stringify(options, null, " ")
- exportSettingsElement.href = "data:application/json;base64," + btoa(resultString)
- exportSettingsElement.download = `libredirect-settings-v${options.version}.json`
- return
+ const options = await utils.getOptions()
+ options.version = browser.runtime.getManifest().version
+ let resultString = JSON.stringify(options, null, " ")
+ exportSettingsElement.href = "data:application/json;base64," + btoa(resultString)
+ exportSettingsElement.download = `libredirect-settings-v${options.version}.json`
+ return
}
exportSettings()
document.getElementById("general_page").onclick = exportSettings
@@ -37,35 +37,32 @@ document.getElementById("general_page").onclick = exportSettings
const importSettingsElement = document.getElementById("import-settings")
const importSettingsElementText = document.getElementById("import_settings_text")
importSettingsElement.addEventListener("change", () => {
- function importError() {
- const oldHTML = importSettingsElementText.innerHTML
- importSettingsElementText.innerHTML = '<span style="color:red;">Error!</span>'
- setTimeout(() => (importSettingsElementText.innerHTML = oldHTML), 1000)
- }
- importSettingsElementText.innerHTML = "..."
- let file = importSettingsElement.files[0]
- const reader = new FileReader()
- reader.readAsText(file)
- reader.onload = async () => {
- const data = JSON.parse(reader.result)
- if (
- "theme" in data
- && data.version == browser.runtime.getManifest().version
- ) {
- browser.storage.local.clear(async () => {
- browser.storage.local.set({ options: data }, () => {
- location.reload()
- })
- })
- } else {
- console.log("incompatible settings")
- importError()
- }
- }
- reader.onerror = error => {
- console.log("error", error)
- importError()
- }
+ function importError() {
+ const oldHTML = importSettingsElementText.innerHTML
+ importSettingsElementText.innerHTML = '<span style="color:red;">Error!</span>'
+ setTimeout(() => (importSettingsElementText.innerHTML = oldHTML), 1000)
+ }
+ importSettingsElementText.innerHTML = "..."
+ let file = importSettingsElement.files[0]
+ const reader = new FileReader()
+ reader.readAsText(file)
+ reader.onload = async () => {
+ const data = JSON.parse(reader.result)
+ if ("theme" in data && data.version == browser.runtime.getManifest().version) {
+ browser.storage.local.clear(async () => {
+ browser.storage.local.set({ options: data }, () => {
+ location.reload()
+ })
+ })
+ } else {
+ console.log("incompatible settings")
+ importError()
+ }
+ }
+ reader.onerror = error => {
+ console.log("error", error)
+ importError()
+ }
})
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")
exportSettingsSync.addEventListener("click", async () => {
- let options = await utils.getOptions()
- options.version = browser.runtime.getManifest().version
- browser.storage.sync.set({ options }, () => location.reload())
+ let options = await utils.getOptions()
+ options.version = browser.runtime.getManifest().version
+ browser.storage.sync.set({ options }, () => location.reload())
})
importSettingsSync.addEventListener("click", () => {
- function importError() {
- importSettingsSyncText.innerHTML = '<span style="color:red;">Error!</span>'
- setTimeout(() => (importSettingsSyncText.innerHTML = oldHTML), 1000)
- }
- const oldHTML = importSettingsSyncText.innerHTML
- importSettingsSyncText.innerHTML = "..."
- browser.storage.sync.get({ options }, r => {
- const options = r.options
- if (options.version == browser.runtime.getManifest().version) {
- browser.storage.local.set({ options }, () => location.reload())
- } else {
- importError()
- }
- })
+ function importError() {
+ importSettingsSyncText.innerHTML = '<span style="color:red;">Error!</span>'
+ setTimeout(() => (importSettingsSyncText.innerHTML = oldHTML), 1000)
+ }
+ const oldHTML = importSettingsSyncText.innerHTML
+ importSettingsSyncText.innerHTML = "..."
+ browser.storage.sync.get({ options }, r => {
+ const options = r.options
+ if (options.version == browser.runtime.getManifest().version) {
+ browser.storage.local.set({ options }, () => location.reload())
+ } else {
+ importError()
+ }
+ })
})
const resetSettings = document.getElementById("reset-settings")
resetSettings.addEventListener("click", async () => {
- resetSettings.innerHTML = "..."
- await servicesHelper.initDefaults()
- location.reload()
+ resetSettings.innerHTML = "..."
+ await servicesHelper.initDefaults()
+ location.reload()
})
-const fetchInstancesElement = document.getElementById('fetch-instances')
-fetchInstancesElement.addEventListener('change', event => {
- setOption('fetchInstances', 'select', event)
- location.reload()
+const fetchInstancesElement = document.getElementById("fetch-instances")
+fetchInstancesElement.addEventListener("change", event => {
+ setOption("fetchInstances", "select", event)
+ location.reload()
})
-const redirectOnlyInIncognitoElement = document.getElementById('redirectOnlyInIncognito')
-redirectOnlyInIncognitoElement.addEventListener('change', event => {
- setOption('redirectOnlyInIncognito', 'checkbox', event)
+const redirectOnlyInIncognitoElement = document.getElementById("redirectOnlyInIncognito")
+redirectOnlyInIncognitoElement.addEventListener("change", event => {
+ setOption("redirectOnlyInIncognito", "checkbox", event)
})
-const bookmarksMenuElement = document.getElementById('bookmarksMenu')
-bookmarksMenuElement.addEventListener('change', async event => {
- if (event.target.checked)
- browser.permissions.request({ permissions: ["bookmarks"] }, r => bookmarksMenuElement.checked = r)
- else
- browser.permissions.remove({ permissions: ["bookmarks"] }, r => bookmarksMenuElement.checked = !r)
+const bookmarksMenuElement = document.getElementById("bookmarksMenu")
+bookmarksMenuElement.addEventListener("change", async event => {
+ if (event.target.checked)
+ browser.permissions.request({ permissions: ["bookmarks"] }, r => (bookmarksMenuElement.checked = r))
+ else browser.permissions.remove({ permissions: ["bookmarks"] }, r => (bookmarksMenuElement.checked = !r))
})
let themeElement = document.getElementById("theme")
themeElement.addEventListener("change", event => {
- setOption("theme", "select", event)
- location.reload()
+ setOption("theme", "select", event)
+ location.reload()
})
let nameCustomInstanceInput = document.getElementById("exceptions-custom-instance")
@@ -134,40 +130,44 @@ let instanceType = "url"
let config = await utils.getConfig()
for (const service in config.services) {
- document.getElementById(service).addEventListener("change", async event => {
- let options = await utils.getOptions()
- if (event.target.checked && !options.popupServices.includes(service)) options.popupServices.push(service)
- else if (options.popupServices.includes(service)) {
- var index = options.popupServices.indexOf(service)
- if (index !== -1) options.popupServices.splice(index, 1)
- }
- browser.storage.local.set({ options })
- })
+ document.getElementById(service).addEventListener("change", async event => {
+ let options = await utils.getOptions()
+ if (event.target.checked && !options.popupServices.includes(service)) options.popupServices.push(service)
+ else if (options.popupServices.includes(service)) {
+ var index = options.popupServices.indexOf(service)
+ if (index !== -1) options.popupServices.splice(index, 1)
+ }
+ browser.storage.local.set({ options })
+ })
}
let options = await utils.getOptions()
themeElement.value = options.theme
fetchInstancesElement.value = options.fetchInstances
redirectOnlyInIncognitoElement.checked = options.redirectOnlyInIncognito
-browser.permissions.contains({ permissions: ["bookmarks"] }, r => bookmarksMenuElement.checked = r)
-for (const service in config.services) document.getElementById(service).checked = options.popupServices.includes(service)
+browser.permissions.contains({ permissions: ["bookmarks"] }, r => (bookmarksMenuElement.checked = r))
+for (const service in config.services)
+ document.getElementById(service).checked = options.popupServices.includes(service)
instanceTypeElement.addEventListener("change", event => {
- instanceType = event.target.options[instanceTypeElement.selectedIndex].value
- if (instanceType == "url") {
- nameCustomInstanceInput.setAttribute("type", "url")
- nameCustomInstanceInput.setAttribute("placeholder", "https://www.google.com")
- } else if (instanceType == "regex") {
- nameCustomInstanceInput.setAttribute("type", "text")
- nameCustomInstanceInput.setAttribute("placeholder", "https?://(www.|)youtube.com/")
- }
+ instanceType = event.target.options[instanceTypeElement.selectedIndex].value
+ if (instanceType == "url") {
+ nameCustomInstanceInput.setAttribute("type", "url")
+ nameCustomInstanceInput.setAttribute("placeholder", "https://www.google.com")
+ } else if (instanceType == "regex") {
+ nameCustomInstanceInput.setAttribute("type", "text")
+ nameCustomInstanceInput.setAttribute("placeholder", "https?://(www.|)youtube.com/")
+ }
})
let exceptionsCustomInstances = options.exceptions
function calcExceptionsCustomInstances() {
- document.getElementById("exceptions-custom-checklist").innerHTML = [...exceptionsCustomInstances.url, ...exceptionsCustomInstances.regex]
- .map(
- x => `<div>
+ document.getElementById("exceptions-custom-checklist").innerHTML = [
+ ...exceptionsCustomInstances.url,
+ ...exceptionsCustomInstances.regex,
+ ]
+ .map(
+ x => `<div>
${x}
<button class="add" id="clear-${x}">
<svg xmlns="https://www.w3.org/2000/svg" height="20px" viewBox="0 0 24 24" width="20px"
@@ -177,40 +177,40 @@ function calcExceptionsCustomInstances() {
</button>
</div>
<hr>`
- )
- .join("\n")
+ )
+ .join("\n")
- for (const x of [...exceptionsCustomInstances.url, ...exceptionsCustomInstances.regex]) {
- document.getElementById(`clear-${x}`).addEventListener("click", async () => {
- let index
- index = exceptionsCustomInstances.url.indexOf(x)
- if (index > -1) exceptionsCustomInstances.url.splice(index, 1)
- else {
- index = exceptionsCustomInstances.regex.indexOf(x)
- if (index > -1) exceptionsCustomInstances.regex.splice(index, 1)
- }
- options = await utils.getOptions()
- options.exceptions = exceptionsCustomInstances
- browser.storage.local.set({ options })
- calcExceptionsCustomInstances()
- })
- }
+ for (const x of [...exceptionsCustomInstances.url, ...exceptionsCustomInstances.regex]) {
+ document.getElementById(`clear-${x}`).addEventListener("click", async () => {
+ let index
+ index = exceptionsCustomInstances.url.indexOf(x)
+ if (index > -1) exceptionsCustomInstances.url.splice(index, 1)
+ else {
+ index = exceptionsCustomInstances.regex.indexOf(x)
+ if (index > -1) exceptionsCustomInstances.regex.splice(index, 1)
+ }
+ options = await utils.getOptions()
+ options.exceptions = exceptionsCustomInstances
+ browser.storage.local.set({ options })
+ calcExceptionsCustomInstances()
+ })
+ }
}
calcExceptionsCustomInstances()
document.getElementById("custom-exceptions-instance-form").addEventListener("submit", async event => {
- event.preventDefault()
- let val
- if (instanceType == "url" && nameCustomInstanceInput.validity.valid) {
- val = nameCustomInstanceInput.value
- if (!exceptionsCustomInstances.url.includes(val)) exceptionsCustomInstances.url.push(val)
- } else if (instanceType == "regex") {
- val = nameCustomInstanceInput.value
- if (val.trim() != "" && !exceptionsCustomInstances.regex.includes(val)) exceptionsCustomInstances.regex.push(val)
- }
- if (val) {
- options = await utils.getOptions()
- options.exceptions = exceptionsCustomInstances
- browser.storage.local.set({ options }, () => nameCustomInstanceInput.value = "")
- }
- calcExceptionsCustomInstances()
+ event.preventDefault()
+ let val
+ if (instanceType == "url" && nameCustomInstanceInput.validity.valid) {
+ val = nameCustomInstanceInput.value
+ if (!exceptionsCustomInstances.url.includes(val)) exceptionsCustomInstances.url.push(val)
+ } else if (instanceType == "regex") {
+ val = nameCustomInstanceInput.value
+ if (val.trim() != "" && !exceptionsCustomInstances.regex.includes(val)) exceptionsCustomInstances.regex.push(val)
+ }
+ if (val) {
+ options = await utils.getOptions()
+ options.exceptions = exceptionsCustomInstances
+ browser.storage.local.set({ options }, () => (nameCustomInstanceInput.value = ""))
+ }
+ calcExceptionsCustomInstances()
})
diff --git a/src/pages/popup/popup.js b/src/pages/popup/popup.js
index 485ec0e0..77a43000 100644
--- a/src/pages/popup/popup.js
+++ b/src/pages/popup/popup.js
@@ -5,15 +5,15 @@ import servicesHelper from "../../assets/javascripts/services.js"
import utils from "../../assets/javascripts/utils.js"
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 => {
- switch (r.os) {
- case "fuchsia":
- case "ios":
- case "android":
- document.getElementsByTagName("html")[0].classList.add("mobile")
- }
+ switch (r.os) {
+ case "fuchsia":
+ case "ios":
+ case "android":
+ document.getElementsByTagName("html")[0].classList.add("mobile")
+ }
})
const allSites = document.getElementById("all_sites")
@@ -24,94 +24,96 @@ const config = await utils.getConfig()
const divs = {}
for (const service in config.services) {
- divs[service] = {}
+ divs[service] = {}
- divs[service].all = allSites.getElementsByClassName(service)[0]
- divs[service].current = currSite.getElementsByClassName(service)[0]
+ divs[service].all = allSites.getElementsByClassName(service)[0]
+ divs[service].current = currSite.getElementsByClassName(service)[0]
- divs[service].all_toggle = allSites.getElementsByClassName(`${service}-enabled`)[0]
- divs[service].all_toggle.addEventListener("change", async () => {
- const options = await utils.getOptions()
- options[service].enabled = divs[service].all_toggle.checked
- browser.storage.local.set({ options })
- })
+ divs[service].all_toggle = allSites.getElementsByClassName(`${service}-enabled`)[0]
+ divs[service].all_toggle.addEventListener("change", async () => {
+ const options = await utils.getOptions()
+ options[service].enabled = divs[service].all_toggle.checked
+ browser.storage.local.set({ options })
+ })
- allSites.getElementsByClassName(`${service}-change_instance`)[0].addEventListener("click", () => {
- browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
- if (tabs[0].url) {
- const url = new URL(tabs[0].url)
- browser.tabs.update({ url: await servicesHelper.switchInstance(url, service) })
- }
- })
- })
+ allSites.getElementsByClassName(`${service}-change_instance`)[0].addEventListener("click", () => {
+ browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+ if (tabs[0].url) {
+ const url = new URL(tabs[0].url)
+ browser.tabs.update({ url: await servicesHelper.switchInstance(url, service) })
+ }
+ })
+ })
- divs[service].current_toggle = currSite.getElementsByClassName(`${service}-enabled`)[0]
- divs[service].current_toggle.addEventListener("change", async () => {
- const options = await utils.getOptions()
- options[service].enabled = divs[service].current_toggle.checked
- browser.storage.local.set({ options })
- })
+ divs[service].current_toggle = currSite.getElementsByClassName(`${service}-enabled`)[0]
+ divs[service].current_toggle.addEventListener("change", async () => {
+ const options = await utils.getOptions()
+ options[service].enabled = divs[service].current_toggle.checked
+ browser.storage.local.set({ options })
+ })
- currSite.getElementsByClassName(`${service}-change_instance`)[0].addEventListener("click", () => {
- browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
- if (tabs[0].url) {
- const url = new URL(tabs[0].url)
- browser.tabs.update({ url: await servicesHelper.switchInstance(url, service) })
- }
- })
- })
+ currSite.getElementsByClassName(`${service}-change_instance`)[0].addEventListener("click", () => {
+ browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
+ if (tabs[0].url) {
+ const url = new URL(tabs[0].url)
+ browser.tabs.update({ url: await servicesHelper.switchInstance(url, service) })
+ }
+ })
+ })
}
browser.tabs.query({ active: true, currentWindow: true }, async tabs => {
- // Set visibility of control buttons
- if (tabs[0].url) {
- const hr = document.getElementById("hr")
- var url = new URL(tabs[0].url)
- servicesHelper.switchInstance(url).then(r => {
- if (r) {
- document.getElementById("change_instance_div").style.display = ""
- hr.style.display = ""
- document.getElementById("change_instance").addEventListener("click", async () =>
- browser.tabs.update({ url: await servicesHelper.switchInstance(url) })
- )
- }
- })
- servicesHelper.reverse(url).then(r => {
- if (r) {
- hr.style.display = ""
+ // Set visibility of control buttons
+ if (tabs[0].url) {
+ const hr = document.getElementById("hr")
+ var url = new URL(tabs[0].url)
+ servicesHelper.switchInstance(url).then(r => {
+ if (r) {
+ document.getElementById("change_instance_div").style.display = ""
+ hr.style.display = ""
+ document
+ .getElementById("change_instance")
+ .addEventListener("click", async () => browser.tabs.update({ url: await servicesHelper.switchInstance(url) }))
+ }
+ })
+ servicesHelper.reverse(url).then(r => {
+ if (r) {
+ hr.style.display = ""
- document.getElementById("copy_original_div").style.display = ""
- document.getElementById("copy_original").addEventListener("click", () => servicesHelper.copyRaw(url))
+ document.getElementById("copy_original_div").style.display = ""
+ document.getElementById("copy_original").addEventListener("click", () => servicesHelper.copyRaw(url))
- document.getElementById("redirect_to_original_div").style.display = ""
- 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 = ""
- hr.style.display = ""
- document.getElementById("redirect").addEventListener("click", () => browser.runtime.sendMessage("redirectTab"))
- }
- })
- }
+ document.getElementById("redirect_to_original_div").style.display = ""
+ 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 = ""
+ 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
- for (const service of options.popupServices) {
- divs[service].all.classList.remove("hide")
- divs[service].all_toggle.checked = options[service].enabled
- }
+ // Set visibility of all service buttons
+ for (const service of options.popupServices) {
+ divs[service].all.classList.remove("hide")
+ divs[service].all_toggle.checked = options[service].enabled
+ }
- // Set visibility of current page service button
- if (url) {
- const service = await servicesHelper.computeService(url)
- if (service) {
- divs[service].all.classList.add("hide")
- divs[service].current.classList.remove("hide")
- divs[service].current_toggle.checked = options[service].enabled
- currentSiteDivider.style.display = ""
- }
- }
-}) \ No newline at end of file
+ // Set visibility of current page service button
+ if (url) {
+ const service = await servicesHelper.computeService(url)
+ if (service) {
+ divs[service].all.classList.add("hide")
+ divs[service].current.classList.remove("hide")
+ divs[service].current_toggle.checked = options[service].enabled
+ currentSiteDivider.style.display = ""
+ }
+ }
+})
diff --git a/src/pages/popup/style.css b/src/pages/popup/style.css
index 5e39b4ed..41510615 100644
--- a/src/pages/popup/style.css
+++ b/src/pages/popup/style.css
@@ -1,65 +1,65 @@
body {
- width: 270px;
- min-height: auto;
+ width: 270px;
+ min-height: auto;
}
html,
body {
- margin: 0;
+ margin: 0;
}
.hide {
- display: none !important;
+ display: none !important;
}
.button {
- display: flex;
- margin: 0 auto;
- justify-content: space-between;
+ display: flex;
+ margin: 0 auto;
+ justify-content: space-between;
}
.button svg {
- width: 26px;
- height: 26px;
+ width: 26px;
+ height: 26px;
}
.bottom-button {
- width: 100%;
+ width: 100%;
}
.space {
- height: 10px;
+ height: 10px;
}
input {
- height: 23px;
- width: 46px;
+ height: 23px;
+ width: 46px;
}
div.block label {
- margin: 0;
- font-size: 18px;
- font-weight: bold;
- max-width: 180px;
+ margin: 0;
+ font-size: 18px;
+ font-weight: bold;
+ max-width: 180px;
}
div.block label:hover {
- cursor: pointer;
+ cursor: pointer;
}
div.block div {
- display: flex;
+ display: flex;
}
html.mobile body {
- width: 100%;
+ width: 100%;
}
html.mobile div.block label {
- font-size: 24px;
+ font-size: 24px;
}
html.mobile .button svg {
- width: 30px;
- height: 30px;
-} \ No newline at end of file
+ width: 30px;
+ height: 30px;
+}
diff --git a/src/pages/stylesheets/styles.css b/src/pages/stylesheets/styles.css
index b220efdb..ca6f0dca 100644
--- a/src/pages/stylesheets/styles.css
+++ b/src/pages/stylesheets/styles.css
@@ -1,450 +1,449 @@
body {
- --text: #fff;
- --bg-main: #121212;
- --bg-secondary: #202020;
- --active: #fbc117;
- --danger: #f04141;
- --light-grey: #c3c3c3;
+ --text: #fff;
+ --bg-main: #121212;
+ --bg-secondary: #202020;
+ --active: #fbc117;
+ --danger: #f04141;
+ --light-grey: #c3c3c3;
}
@font-face {
- font-family: "Inter";
- src: url("Inter-VariableFont_slnt,wght.ttf");
- font-weight: normal;
- font-style: normal;
+ font-family: "Inter";
+ src: url("Inter-VariableFont_slnt,wght.ttf");
+ font-weight: normal;
+ font-style: normal;
}
@font-face {
- font-family: "Vazirmatn";
- src: url("Vazirmatn-VariableFont_wght.ttf");
- font-weight: normal;
- font-style: normal;
+ font-family: "Vazirmatn";
+ src: url("Vazirmatn-VariableFont_wght.ttf");
+ font-weight: normal;
+ font-style: normal;
}
body {
- margin: auto;
- padding: 0;
- font-family: "Inter";
- font-size: 16px;
- background-color: var(--bg-main);
- color: var(--text);
+ margin: auto;
+ padding: 0;
+ font-family: "Inter";
+ font-size: 16px;
+ background-color: var(--bg-main);
+ color: var(--text);
}
body * {
- font-family: "Inter";
+ font-family: "Inter";
}
body.rtl {
- font-family: "Vazirmatn";
+ font-family: "Vazirmatn";
}
body.rtl * {
- font-family: "Vazirmatn";
+ font-family: "Vazirmatn";
}
div.block input[type="checkbox"] {
- appearance: none;
- -moz-appearance: none;
- -webkit-appearance: none;
+ appearance: none;
+ -moz-appearance: none;
+ -webkit-appearance: none;
}
.title {
- display: flex;
- align-items: center;
- text-decoration: none;
- width: min-content;
- color: var(--text);
- transition: .1s;
+ display: flex;
+ align-items: center;
+ text-decoration: none;
+ width: min-content;
+ color: var(--text);
+ transition: 0.1s;
}
.title:hover {
- opacity: 1 !important;
+ opacity: 1 !important;
}
.title:hover a {
- color: var(--active);
+ color: var(--active);
}
img,
svg {
- margin-right: 10px;
- height: 26px;
- width: 26px;
- color: var(--text);
+ margin-right: 10px;
+ height: 26px;
+ width: 26px;
+ color: var(--text);
}
body.rtl img,
body.rtl svg {
- margin-right: 0px;
- margin-left: 10px;
+ margin-right: 0px;
+ margin-left: 10px;
}
input[type="url"],
input[type="text"],
select {
- font-weight: bold;
- box-sizing: border-box;
- border-style: solid;
- border-color: #767676;
- color: var(--text);
- font-size: 16px;
- padding: 8px;
- background-color: var(--bg-secondary);
- border: none;
- margin: 0;
- max-width: 500px;
- border-radius: 3px;
+ font-weight: bold;
+ box-sizing: border-box;
+ border-style: solid;
+ border-color: #767676;
+ color: var(--text);
+ font-size: 16px;
+ padding: 8px;
+ background-color: var(--bg-secondary);
+ border: none;
+ margin: 0;
+ max-width: 500px;
+ border-radius: 3px;
}
input[type="url"],
input[type="text"] {
- width: 400px;
- cursor: text;
+ width: 400px;
+ cursor: text;
}
input:invalid {
- color: var(--danger);
+ color: var(--danger);
}
.button svg {
- height: 18px;
- width: 18px;
+ height: 18px;
+ width: 18px;
}
section.block-option {
- width: 750px;
- margin: 0 50px;
+ width: 750px;
+ margin: 0 50px;
}
section.block-option h2 {
- margin: 0;
+ margin: 0;
}
body.option {
- display: flex;
- padding: 40px;
- width: 1160px;
+ display: flex;
+ padding: 40px;
+ width: 1160px;
}
section.links {
- display: flex;
- flex-wrap: wrap;
- flex-direction: column;
- width: 350px;
- max-height: 1030px;
+ display: flex;
+ flex-wrap: wrap;
+ flex-direction: column;
+ width: 350px;
+ max-height: 1030px;
}
section.links div {
- margin: 10px;
- width: max-content;
+ margin: 10px;
+ width: max-content;
}
a {
- text-decoration: none;
- color: var(--text);
- transition: 0.1s;
+ text-decoration: none;
+ color: var(--text);
+ transition: 0.1s;
}
a:hover {
- color: var(--active);
+ color: var(--active);
}
section.links a {
- display: flex;
- align-items: center;
- font-size: 18px;
- text-decoration: none;
- color: white;
- transition: 0.1s;
+ display: flex;
+ align-items: center;
+ font-size: 18px;
+ text-decoration: none;
+ color: white;
+ transition: 0.1s;
}
section.links a:hover,
section.links .selected {
- opacity: 1 !important;
+ opacity: 1 !important;
}
section.links .selected a {
- color: var(--active);
+ color: var(--active);
}
::placeholder {
- color: var(--text);
- opacity: 0.7;
+ color: var(--text);
+ opacity: 0.7;
}
hr {
- height: 2px;
- margin: 0 15px;
- background-color: rgb(77, 77, 77);
- border: none;
+ height: 2px;
+ margin: 0 15px;
+ background-color: rgb(77, 77, 77);
+ border: none;
}
div.block {
- padding: 0 15px;
- justify-content: space-between;
- display: flex;
- align-items: center;
- margin-top: 10px;
- margin-bottom: 10px;
+ padding: 0 15px;
+ justify-content: space-between;
+ display: flex;
+ align-items: center;
+ margin-top: 10px;
+ margin-bottom: 10px;
}
div.block-option {
- margin: 30px 0;
+ margin: 30px 0;
}
div.block-option label {
- margin-right: 5px;
- width: 80%;
- min-width: 150px;
- font-size: 18px;
+ margin-right: 5px;
+ width: 80%;
+ min-width: 150px;
+ font-size: 18px;
}
div.block-option h1 {
- margin: 0;
- font-size: 28px;
- color: var(--text);
+ margin: 0;
+ font-size: 28px;
+ color: var(--text);
}
div.block-option div {
- text-align: center;
+ text-align: center;
}
div.block input[type="checkbox"] {
- width: 46px;
- height: 24px;
- background-color: var(--light-grey);
- border-radius: 50px;
- transition: 0.4s;
- cursor: pointer;
+ width: 46px;
+ height: 24px;
+ background-color: var(--light-grey);
+ border-radius: 50px;
+ transition: 0.4s;
+ cursor: pointer;
}
div.block input[type="checkbox"]:checked {
- background-color: var(--active);
+ background-color: var(--active);
}
div.block input[type="checkbox"]::before {
- content: "";
- display: inline-block;
- width: 18px;
- height: 18px;
- box-sizing: border-box;
- position: relative;
- top: 2.5px;
- left: 3.5px;
- background-color: white;
- border-radius: 50%;
- transition: 0.3s;
+ content: "";
+ display: inline-block;
+ width: 18px;
+ height: 18px;
+ box-sizing: border-box;
+ position: relative;
+ top: 2.5px;
+ left: 3.5px;
+ background-color: white;
+ border-radius: 50%;
+ transition: 0.3s;
}
body.rtl div.block input[type="checkbox"]::before {
- left: auto;
- right: 4px;
+ left: auto;
+ right: 4px;
}
div.block input[type="checkbox"]:checked::before {
- left: 24px;
+ left: 24px;
}
body.rtl div.block input[type="checkbox"]:checked::before {
- left: auto;
- right: 24px;
+ left: auto;
+ right: 24px;
}
div.buttons {
- display: flex;
- margin: 0 15px;
- margin-bottom: 15px;
- margin-top: 15px;
- flex-wrap: wrap;
- align-items: center;
- justify-content: start;
+ display: flex;
+ margin: 0 15px;
+ margin-bottom: 15px;
+ margin-top: 15px;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: start;
}
.button {
- color: var(--text);
- font-size: 16px;
- font-weight: bold;
- text-decoration: none;
- cursor: pointer;
- transition-duration: 0.1s;
+ color: var(--text);
+ font-size: 16px;
+ font-weight: bold;
+ text-decoration: none;
+ cursor: pointer;
+ transition-duration: 0.1s;
}
.button:hover {
- color: var(--active);
+ color: var(--active);
}
.button svg {
- width: auto;
- height: auto;
- padding: 0;
- margin-right: 5px;
+ width: auto;
+ height: auto;
+ padding: 0;
+ margin-right: 5px;
}
.button:hover svg {
- color: var(--active);
+ color: var(--active);
}
.button-inline {
- display: inline-flex;
- align-items: center;
- margin: 7.5px 0;
- background-color: var(--bg-secondary);
- border-radius: 5px;
- padding: 10px;
+ display: inline-flex;
+ align-items: center;
+ margin: 7.5px 0;
+ background-color: var(--bg-secondary);
+ border-radius: 5px;
+ padding: 10px;
}
.button:active {
- transform: translateY(1px);
+ transform: translateY(1px);
}
button svg {
- color: var(--text);
+ color: var(--text);
}
div.checklist div {
- justify-content: space-between;
- margin: 5px 15px;
- padding: 10px 0;
- word-wrap: break-word;
- display: flex;
+ justify-content: space-between;
+ margin: 5px 15px;
+ padding: 10px 0;
+ word-wrap: break-word;
+ display: flex;
}
div.checklist a {
- text-decoration: none;
- color: var(--text);
+ text-decoration: none;
+ color: var(--text);
}
div.checklist a:hover {
- text-decoration: underline;
+ text-decoration: underline;
}
div.custom-checklist x a {
- color: var(--active);
+ color: var(--active);
}
button.add {
- background-color: transparent;
- border: none;
- padding: 0;
- margin: 0;
- text-decoration: none;
- display: inline-block;
- cursor: pointer;
+ background-color: transparent;
+ border: none;
+ padding: 0;
+ margin: 0;
+ text-decoration: none;
+ display: inline-block;
+ cursor: pointer;
}
body.light-theme {
- --text: black;
- --bg-main: white;
- --bg-secondary: #e4e4e4;
- --active: #fb9817;
+ --text: black;
+ --bg-main: white;
+ --bg-secondary: #e4e4e4;
+ --active: #fb9817;
}
body.light-theme select {
- border: 1px solid black;
+ border: 1px solid black;
}
body.light-theme a {
- color: black;
+ color: black;
}
body.light-theme a:hover {
- color: var(--active)
+ color: var(--active);
}
button {
- background-color: transparent;
- color: var(--text);
- border: none;
- text-decoration: none;
- display: inline-block;
- cursor: pointer;
- border-radius: 5px;
+ background-color: transparent;
+ color: var(--text);
+ border: none;
+ text-decoration: none;
+ display: inline-block;
+ cursor: pointer;
+ border-radius: 5px;
}
body div section {
- display: none;
+ display: none;
}
select:disabled {
- opacity: 0.6;
- cursor: not-allowed;
+ opacity: 0.6;
+ cursor: not-allowed;
}
input:disabled {
- opacity: 0.6;
- cursor: not-allowed;
+ opacity: 0.6;
+ cursor: not-allowed;
}
@media (max-width: 1250px) {
- body.option {
- flex-direction: column;
- width: 95vw;
- align-items: center;
- padding: 40px 0px;
- }
+ body.option {
+ flex-direction: column;
+ width: 95vw;
+ align-items: center;
+ padding: 40px 0px;
+ }
- section.links {
- flex-direction: row;
- width: 95vw;
- padding: 0 55px;
- }
+ section.links {
+ flex-direction: row;
+ width: 95vw;
+ padding: 0 55px;
+ }
- section.block-option {
- width: 95vw;
- }
+ section.block-option {
+ width: 95vw;
+ }
- div.checklist div x {
- overflow: hidden;
- }
+ div.checklist div x {
+ overflow: hidden;
+ }
}
html.mobile img,
html.mobile svg {
- margin-right: 10px;
- height: 30px;
- width: 30px;
- color: var(--text);
+ margin-right: 10px;
+ height: 30px;
+ width: 30px;
+ color: var(--text);
}
html.mobile div.block {
- padding: 0 15px;
- justify-content: space-between;
- display: flex;
- align-items: center;
- margin-top: 20px;
- margin-bottom: 20px;
+ padding: 0 15px;
+ justify-content: space-between;
+ display: flex;
+ align-items: center;
+ margin-top: 20px;
+ margin-bottom: 20px;
}
html.mobile div.block input[type="checkbox"] {
- width: 58px;
- height: 30px;
+ width: 58px;
+ height: 30px;
}
html.mobile div.block input[type="checkbox"]::before {
- width: 24px;
- height: 24px;
- top: 3px;
- left: 3.5px;
+ width: 24px;
+ height: 24px;
+ top: 3px;
+ left: 3.5px;
}
html.mobile div.block input[type="checkbox"]:checked::before {
- left: 30px;
+ left: 30px;
}
html.mobile body.option {
- flex-direction: column;
- width: 100%;
- padding: 0;
- align-items: center;
+ flex-direction: column;
+ width: 100%;
+ padding: 0;
+ align-items: center;
}
-
html.mobile section.links {
- flex-direction: row;
- width: 100%;
- padding: 0 55px;
+ flex-direction: row;
+ width: 100%;
+ padding: 0 55px;
}
html.mobile section.block-option {
- width: 100%;
-} \ No newline at end of file
+ width: 100%;
+}