您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Ensures that all URLs to Lemmy instances always point to your main/home instance.
当前为
- // ==UserScript==
- // @name Lemmy Universal Link Switcher
- // @namespace http://azzurite.tv/
- // @license GPLv3
- // @version 1.3.3
- // @description Ensures that all URLs to Lemmy instances always point to your main/home instance.
- // @homepageURL https://gitlab.com/azzurite/lemmy-universal-link-switcher
- // @supportURL https://gitlab.com/azzurite/lemmy-universal-link-switcher/-/issues
- // @author Azzurite
- // @match *://*/*
- // @icon https://gitlab.com/azzurite/lemmy-universal-link-switcher/-/raw/main/resources/favicon.png?inline=true
- // @grant GM.setValue
- // @grant GM.getValue
- // @grant GM.xmlHttpRequest
- // @grant GM.registerMenuCommand
- // @connect *
- // @require https://unpkg.com/@popperjs/core@2
- // @require https://unpkg.com/tippy.js@6
- // ==/UserScript==
- (() => {
- // src/debug.js
- var DEBUG = false;
- function debug() {
- if (DEBUG)
- console.debug(`Rewriter | `, ...arguments);
- }
- function trace() {
- if (DEBUG === `trace`)
- console.debug(`Rewriter Trace | `, ...arguments);
- }
- // src/instances.js
- function isLemmyInstance(url) {
- return isInstance(INSTANCES_LEMMY, url);
- }
- function isKbinInstance(url) {
- return isInstance(INSTANCES_KBIN, url);
- }
- function isInstance(instances, url) {
- if (url.origin) {
- return instances.has(url.origin);
- } else {
- return false;
- }
- }
- trace(`Define instances sets start`);
- var INSTANCES_LEMMY = /* @__PURE__ */ new Set([
- "http://lemmy.brdsnest.net",
- "http://lemmy.nijboer.chat",
- "https://0v0.social",
- "https://0xdd.org.ru",
- "https://1337lemmy.com",
- "https://2dl.eu",
- "https://3t.au",
- "https://4wd.social",
- "https://7.62x54r.ru",
- "https://acqrs.co.uk",
- "https://actuallyruben.nl",
- "https://adding.space",
- "https://adultswim.fan",
- "https://agora.nop.chat",
- "https://aiparadise.moe",
- "https://algebro.xyz",
- "https://alien.top",
- "https://anarch.is",
- "https://androiddev.network",
- "https://ani.social",
- "https://animoe.xyz",
- "https://areality.social",
- "https://arpa.dev",
- "https://asocial.thedroth.rocks",
- "https://astraea.pink",
- "https://aussie.zone",
- "https://awful.systems",
- "https://azgil.net",
- "https://badatbeing.social",
- "https://baraza.africa",
- "https://bbs.9tail.net",
- "https://bbs.wznmickey.com",
- "https://beehaw.org",
- "https://beer.andma.la",
- "https://belfry.rip",
- "https://bethe.kingofdog.de",
- "https://biglemmowski.win",
- "https://bin.pztrn.online",
- "https://bluuit.org",
- "https://board.minimally.online",
- "https://bolha.forum",
- "https://bookwormstory.social",
- "https://boomer.casino",
- "https://borg.chat",
- "https://botnet.club",
- "https://boulder.ly",
- "https://bulletintree.com",
- "https://burggit.moe",
- "https://butts.international",
- "https://campfyre.nickwebster.dev",
- "https://casavaga.com",
- "https://catata.fish",
- "https://catgirl.pub",
- "https://cavy.rocks",
- "https://cdda.social",
- "https://chinese.lol",
- "https://citizensgaming.com",
- "https://civilloquy.com",
- "https://cocte.au",
- "https://code4lib.net",
- "https://codesink.io",
- "https://communick.news",
- "https://communities.azkware.net",
- "https://community.adiquaints.moe",
- "https://community.nicfab.it",
- "https://community.o0o.social",
- "https://compuverse.uk",
- "https://corndog.social",
- "https://corrigan.space",
- "https://crowfx.web.id",
- "https://ctrlaltelite.xyz",
- "https://czech-lemmy.eu",
- "https://dandroid.app",
- "https://dendarii.alaeron.com",
- "https://derp.foo",
- "https://derpzilla.net",
- "https://dev.automationwise.com",
- "https://digipres.cafe",
- "https://dirtbag.social",
- "https://dis.ney.ink",
- "https://disc.0x-ia.moe",
- "https://discover.deltanauten.de",
- "https://discuss.dizl.de",
- "https://discuss.icewind.me",
- "https://discuss.jacen.moe",
- "https://discuss.ntfy.sh",
- "https://discuss.online",
- "https://discuss.tchncs.de",
- "https://discuss.yussuv.com",
- "https://disflux.org",
- "https://distress.digital",
- "https://dit.reformed.social",
- "https://diyrpg.org",
- "https://dmv.social",
- "https://donky.social",
- "https://dormi.zone",
- "https://drlemmy.net",
- "https://ds9.lemmy.ml",
- "https://dubvee.org",
- "https://dumbdomain.xyz",
- "https://einweckglas.com",
- "https://emmyl.com",
- "https://endlesstalk.org",
- "https://enterprise.lemmy.ml",
- "https://esc.ozar.dev",
- "https://eslemmy.es",
- "https://eventfrontier.com",
- "https://eviltoast.org",
- "https://exabytes.space",
- "https://ezekielrage.com",
- "https://falconry.party",
- "https://fanaticus.social",
- "https://fanexus.com",
- "https://fed.dyne.org",
- "https://fed.rosssi.co.uk",
- "https://feddit.ch",
- "https://feddit.cl",
- "https://feddit.de",
- "https://feddit.dk",
- "https://feddit.eu",
- "https://feddit.fun",
- "https://feddit.it",
- "https://feddit.jp",
- "https://feddit.nl",
- "https://feddit.nu",
- "https://feddit.nz",
- "https://feddit.ro",
- "https://feddit.rocks",
- "https://feddit.site",
- "https://feddit.strike23.de",
- "https://feddit.uk",
- "https://federated.community",
- "https://federation.red",
- "https://fedi.alc.im",
- "https://fedii.me",
- "https://fedimav.win",
- "https://fedit.io",
- "https://fediverse.ro",
- "https://fediverse.zeekzack.com",
- "https://feed.newt.wtf",
- "https://fjdk.uk",
- "https://fl0w.cc",
- "https://flamewar.social",
- "https://fludiblu.xyz",
- "https://forkk.me",
- "https://foro.markoop.org",
- "https://foros.fediverso.gal",
- "https://forum.liberatedsystems.co.uk",
- "https://forum.stellarcastle.net",
- "https://fost.hu",
- "https://fry.gs",
- "https://futurology.today",
- "https://gadgetro.id",
- "https://gathering.pikafan.de",
- "https://gioia.news",
- "https://gonelemmy.xyz",
- "https://goon.vip",
- "https://group.lt",
- "https://hackertalks.com",
- "https://halubilo.social",
- "https://happysl.app",
- "https://hedge.town",
- "https://hexagonsun.one",
- "https://hexbear.net",
- "https://hive.atlanten.se",
- "https://hobbit.world",
- "https://hoihoi.superboi.eu.org",
- "https://hoodlem.me",
- "https://hq.unit520.wtf",
- "https://hyperfair.link",
- "https://i.d0ntknow.me",
- "https://iceorchid.net",
- "https://ihax0r.com",
- "https://info.prou.be",
- "https://infosec.pub",
- "https://iusearchlinux.fyi",
- "https://jamie.moe",
- "https://jemmy.jeena.net",
- "https://jlai.lu",
- "https://just.vibin.wtf",
- "https://kaijus.us",
- "https://kbin.tancomps.net",
- "https://keeb.lol",
- "https://kerala.party",
- "https://keylog.zip",
- "https://kleptonix.com",
- "https://klingon.nl",
- "https://krabb.org",
- "https://kulupu.duckdns.org",
- "https://kutsuya.dev",
- "https://kuu.kohana.fi",
- "https://kyberpunk.social",
- "https://kyu.de",
- "https://l.60228.dev",
- "https://l.7rg1nt.moe",
- "https://l.alexdevs.me",
- "https://l.antiope.link",
- "https://l.biendeo.com",
- "https://l.clearbackblast.com",
- "https://l.dusty-radio.com",
- "https://l.hedonistlab.com",
- "https://l.henlo.fi",
- "https://l.jugregator.org",
- "https://l.matestmc.ru",
- "https://l.mathers.fr",
- "https://l.mchome.net",
- "https://l.os33.co",
- "https://l.plabs.social",
- "https://l.quou.xyz",
- "https://l.r6e.dev",
- "https://l.roofo.cc",
- "https://l.shoddy.site",
- "https://l.sw0.com",
- "https://l.vidja.social",
- "https://l3mmy.com",
- "https://lassmich.wtf",
- "https://lazysoci.al",
- "https://le-me.xyz",
- "https://le.fduck.net",
- "https://le.mnau.xyz",
- "https://le.weme.wtf",
- "https://leaf.dance",
- "https://lebowski.social",
- "https://leby.dev",
- "https://leddit.danmark.party",
- "https://lef.li",
- "https://lem.cochrun.xyz",
- "https://lem.free.as",
- "https://lem.lucitt.social",
- "https://lem.monster",
- "https://lem.nimmog.uk",
- "https://lem.serkozh.me",
- "https://lem.simple-gear.com",
- "https://lem.trashbrain.org",
- "https://lem.ur-mom.gay",
- "https://lemdro.id",
- "https://leminal.space",
- "https://lemm.ee",
- "https://lemmerz.org",
- "https://lemmie.be",
- "https://lemming.quest",
- "https://lemmings.sopelj.ca",
- "https://lemmings.world",
- "https://lemmit.online",
- "https://lemmony.click",
- "https://lemmus.org",
- "https://lemmy-api.ten4ward.social",
- "https://lemmy.0upti.me",
- "https://lemmy.100010101.xyz",
- "https://lemmy.1204.org",
- "https://lemmy.3cm.us",
- "https://lemmy.505dude.com",
- "https://lemmy.86thumbs.net",
- "https://lemmy.8th.world",
- "https://lemmy.absolutesix.com",
- "https://lemmy.aguiarvieira.pt",
- "https://lemmy.ahall.se",
- "https://lemmy.al",
- "https://lemmy.albertoluna.es",
- "https://lemmy.amxl.com",
- "https://lemmy.ananace.dev",
- "https://lemmy.animal-machine.com",
- "https://lemmy.anonion.social",
- "https://lemmy.antemeridiem.xyz",
- "https://lemmy.antisocial.ly",
- "https://lemmy.anymore.nl",
- "https://lemmy.appeine.com",
- "https://lemmy.asc6.org",
- "https://lemmy.avata.social",
- "https://lemmy.basedcount.com",
- "https://lemmy.baswag.de",
- "https://lemmy.bayern",
- "https://lemmy.belegost.net",
- "https://lemmy.beru.co",
- "https://lemmy.best",
- "https://lemmy.bezzie.world",
- "https://lemmy.bg",
- "https://lemmy.bigsecretwebsite.net",
- "https://lemmy.billiam.net",
- "https://lemmy.bismith.net",
- "https://lemmy.bitgoblin.tech",
- "https://lemmy.blad.is",
- "https://lemmy.blahaj.zone",
- "https://lemmy.bleh.au",
- "https://lemmy.bloodmoon-network.de",
- "https://lemmy.blugatch.tube",
- "https://lemmy.bmck.au",
- "https://lemmy.boltwolf.net",
- "https://lemmy.bond",
- "https://lemmy.bosio.info",
- "https://lemmy.bothhands.ca",
- "https://lemmy.brad.ee",
- "https://lemmy.brennoflavio.com.br",
- "https://lemmy.brief.guru",
- "https://lemmy.bringdaruck.us",
- "https://lemmy.browntown.dev",
- "https://lemmy.bryant.moe",
- "https://lemmy.brynstuff.co.uk",
- "https://lemmy.buller.cc",
- "https://lemmy.byrdcrouse.com",
- "https://lemmy.byte-a.net",
- "https://lemmy.byteunion.com",
- "https://lemmy.ca",
- "https://lemmy.cafe",
- "https://lemmy.calvss.com",
- "https://lemmy.canyonero.net",
- "https://lemmy.capebreton.social",
- "https://lemmy.cat",
- "https://lemmy.catasaur.xyz",
- "https://lemmy.catattack.win",
- "https://lemmy.catgirl.biz",
- "https://lemmy.cfras.net",
- "https://lemmy.ch3n2k.com",
- "https://lemmy.chiisana.net",
- "https://lemmy.cloud.aboutcher.co.uk",
- "https://lemmy.clueware.org",
- "https://lemmy.cnschn.com",
- "https://lemmy.co.nz",
- "https://lemmy.coffee-beanz.com",
- "https://lemmy.cogindo.net",
- "https://lemmy.comfysnug.space",
- "https://lemmy.commodore.social",
- "https://lemmy.conk.me",
- "https://lemmy.conorab.com",
- "https://lemmy.cook.gg",
- "https://lemmy.craftycanine.net",
- "https://lemmy.crimedad.work",
- "https://lemmy.criticalbasics.xyz",
- "https://lemmy.croc.pw",
- "https://lemmy.crowpro.net",
- "https://lemmy.cryptoriot.org",
- "https://lemmy.csupes.page",
- "https://lemmy.cultimean.group",
- "https://lemmy.cwain.dk",
- "https://lemmy.d.thewooskeys.com",
- "https://lemmy.darmstadt.social",
- "https://lemmy.darvit.nl",
- "https://lemmy.datatekniker.dev",
- "https://lemmy.davidfreina.at",
- "https://lemmy.davidzeiger.net",
- "https://lemmy.dayl.in",
- "https://lemmy.dblclk.dev",
- "https://lemmy.dbzer0.com",
- "https://lemmy.dcmrobertson.com",
- "https://lemmy.dcrich.net",
- "https://lemmy.decronym.xyz",
- "https://lemmy.deepspace.gay",
- "https://lemmy.deev.io",
- "https://lemmy.deltaa.xyz",
- "https://lemmy.demonoftheday.eu",
- "https://lemmy.despotes.nl",
- "https://lemmy.dexlit.xyz",
- "https://lemmy.digital-alchemy.app",
- "https://lemmy.digitalcharon.in",
- "https://lemmy.digitalfall.net",
- "https://lemmy.digitalutopia.xyz",
- "https://lemmy.dingo-zebra.ts.net",
- "https://lemmy.discothe.quest",
- "https://lemmy.dnet.social",
- "https://lemmy.dogboy.xyz",
- "https://lemmy.doomeer.com",
- "https://lemmy.dormedas.com",
- "https://lemmy.dosh.dk",
- "https://lemmy.dropdoos.nl",
- "https://lemmy.ds2600.com",
- "https://lemmy.dudeami.win",
- "https://lemmy.dynatron.me",
- "https://lemmy.eatsleepcode.ca",
- "https://lemmy.ecliptik.com",
- "https://lemmy.eco.br",
- "https://lemmy.einval.net",
- "https://lemmy.elashri.xyz",
- "https://lemmy.elest.io",
- "https://lemmy.emerald.show",
- "https://lemmy.emopolarbear.com",
- "https://lemmy.emphisia.nl",
- "https://lemmy.enchanted.social",
- "https://lemmy.escapebigtech.info",
- "https://lemmy.estrogen.plus",
- "https://lemmy.eus",
- "https://lemmy.eyeofthestorm.place",
- "https://lemmy.fait.ch",
- "https://lemmy.farley.pro",
- "https://lemmy.fbsweb.de",
- "https://lemmy.fbxl.net",
- "https://lemmy.fdr8.us",
- "https://lemmy.federated.club",
- "https://lemmy.fedi.bub.org",
- "https://lemmy.fedifriends.social",
- "https://lemmy.fedihub.social",
- "https://lemmy.fedireads.com",
- "https://lemmy.fenbushi.site",
- "https://lemmy.fgilcc.eu",
- "https://lemmy.finance",
- "https://lemmy.fish",
- "https://lemmy.fluffyb.net",
- "https://lemmy.foldling.org",
- "https://lemmy.fornaxian.tech",
- "https://lemmy.forty-two.sh",
- "https://lemmy.fosshost.com",
- "https://lemmy.foxden.party",
- "https://lemmy.freddeliv.me",
- "https://lemmy.fredhs.net",
- "https://lemmy.friheter.com",
- "https://lemmy.fromshado.ws",
- "https://lemmy.frozeninferno.xyz",
- "https://lemmy.funami.tech",
- "https://lemmy.funkylab.xyz",
- "https://lemmy.fwgx.uk",
- "https://lemmy.fyi",
- "https://lemmy.g97.top",
- "https://lemmy.gamingnight.uy",
- "https://lemmy.gar.dev",
- "https://lemmy.gareth.computer",
- "https://lemmy.geekforbes.com",
- "https://lemmy.genkmc.de",
- "https://lemmy.geofox.org",
- "https://lemmy.getmeotter.work",
- "https://lemmy.giftedmc.com",
- "https://lemmy.gimmin.com",
- "https://lemmy.glasgow.social",
- "https://lemmy.gleeson.org",
- "https://lemmy.globe.pub",
- "https://lemmy.gmprojects.pro",
- "https://lemmy.graphics",
- "https://lemmy.graz.social",
- "https://lemmy.grimace.life",
- "https://lemmy.gross.hosting",
- "https://lemmy.grys.it",
- "https://lemmy.gsp8181.co.uk",
- "https://lemmy.hackerbots.net",
- "https://lemmy.hacktheplanet.be",
- "https://lemmy.haigner.me",
- "https://lemmy.halfbro.xyz",
- "https://lemmy.happyharry.org",
- "https://lemmy.helheim.net",
- "https://lemmy.helios42.de",
- "https://lemmy.hellwhore.com",
- "https://lemmy.help",
- "https://lemmy.heorot.uk",
- "https://lemmy.hobbyhorse.dev",
- "https://lemmy.hogru.ch",
- "https://lemmy.holmosapien.com",
- "https://lemmy.home.titey.net",
- "https://lemmy.hoyle.me.uk",
- "https://lemmy.hqueue.dev",
- "https://lemmy.hstx.eu",
- "https://lemmy.hugovr.dev",
- "https://lemmy.hybridsarcasm.xyz",
- "https://lemmy.id",
- "https://lemmy.imagisphe.re",
- "https://lemmy.inbutts.lol",
- "https://lemmy.insley.cloud",
- "https://lemmy.institute",
- "https://lemmy.iqecke.de",
- "https://lemmy.itsallbadsyntax.com",
- "https://lemmy.iwentto.science",
- "https://lemmy.iys.io",
- "https://lemmy.jacaranda.club",
- "https://lemmy.jamesj999.co.uk",
- "https://lemmy.jasondove.me",
- "https://lemmy.jasonsanta.xyz",
- "https://lemmy.javant.xyz",
- "https://lemmy.jaypg.pw",
- "https://lemmy.jeefy.dev",
- "https://lemmy.jgholistic.com",
- "https://lemmy.jhjacobs.nl",
- "https://lemmy.jigoku.us.to",
- "https://lemmy.jlh.name",
- "https://lemmy.jmatyka.com",
- "https://lemmy.jmtr.org",
- "https://lemmy.jnks.xyz",
- "https://lemmy.johnnei.org",
- "https://lemmy.johnpanos.com",
- "https://lemmy.johnscloud.net",
- "https://lemmy.jonaharagon.net",
- "https://lemmy.jpaulus.io",
- "https://lemmy.juggler.jp",
- "https://lemmy.kagura.eu",
- "https://lemmy.kaouenn-noz.fr",
- "https://lemmy.kaytse.fun",
- "https://lemmy.kde.social",
- "https://lemmy.kennery.com",
- "https://lemmy.keychat.org",
- "https://lemmy.khorne.me",
- "https://lemmy.killtime.online",
- "https://lemmy.kilo666.com",
- "https://lemmy.kizaing.ca",
- "https://lemmy.kmoneyserver.com",
- "https://lemmy.kms.onl",
- "https://lemmy.ko4abp.com",
- "https://lemmy.kodemystic.dev",
- "https://lemmy.korz.dev",
- "https://lemmy.kutara.io",
- "https://lemmy.kwain.net",
- "https://lemmy.kweb.ovh",
- "https://lemmy.kya.moe",
- "https://lemmy.l00p.org",
- "https://lemmy.l0nax.org",
- "https://lemmy.lacaveatonton.ovh",
- "https://lemmy.lantian.pub",
- "https://lemmy.latrans.cloud",
- "https://lemmy.lemist.de",
- "https://lemmy.lettucegarden.net",
- "https://lemmy.libreprime.io",
- "https://lemmy.linden.social",
- "https://lemmy.linuxuserspace.show",
- "https://lemmy.lmkra.eu",
- "https://lemmy.loomy.li",
- "https://lemmy.lt",
- "https://lemmy.lukeog.com",
- "https://lemmy.lundgrensjostrom.com",
- "https://lemmy.lylapol.com",
- "https://lemmy.m1k.cloud",
- "https://lemmy.macaddict89.me",
- "https://lemmy.magnor.ovh",
- "https://lemmy.majorshouse.com",
- "https://lemmy.maples.dev",
- "https://lemmy.mariusdavid.fr",
- "https://lemmy.marksism.space",
- "https://lemmy.marud.fr",
- "https://lemmy.masip.cat",
- "https://lemmy.masto.community",
- "https://lemmy.mats.ooo",
- "https://lemmy.max-p.me",
- "https://lemmy.mayer.rocks",
- "https://lemmy.mbirth.uk",
- "https://lemmy.mbl.social",
- "https://lemmy.meg.li",
- "https://lemmy.meissners.me",
- "https://lemmy.menf.in",
- "https://lemmy.menos.gotdns.org",
- "https://lemmy.meowchat.xyz",
- "https://lemmy.mildgrim.com",
- "https://lemmy.mindoki.com",
- "https://lemmy.minecloud.ro",
- "https://lemmy.minie4.de",
- "https://lemmy.minigubben.se",
- "https://lemmy.minionflo.net",
- "https://lemmy.minji.xyz",
- "https://lemmy.mkwarman.com",
- "https://lemmy.ml",
- "https://lemmy.mlaga97.space",
- "https://lemmy.mlsn.fr",
- "https://lemmy.mods4ever.com",
- "https://lemmy.modshiftx.com",
- "https://lemmy.mohammadodeh.com",
- "https://lemmy.mongle.us",
- "https://lemmy.monster",
- "https://lemmy.moocloud.party",
- "https://lemmy.moonling.nl",
- "https://lemmy.mooo.com",
- "https://lemmy.mrm.one",
- "https://lemmy.mrmonkey.xyz",
- "https://lemmy.mrrl.me",
- "https://lemmy.muffalings.com",
- "https://lemmy.multivers.cc",
- "https://lemmy.mws.rocks",
- "https://lemmy.my-box.dev",
- "https://lemmy.my.id",
- "https://lemmy.myserv.one",
- "https://lemmy.myspamtrap.com",
- "https://lemmy.nauk.io",
- "https://lemmy.ndlug.org",
- "https://lemmy.nekusoul.de",
- "https://lemmy.neoluxcommunications.com",
- "https://lemmy.nerdcore.social",
- "https://lemmy.nexus",
- "https://lemmy.nicknakin.com",
- "https://lemmy.nikore.net",
- "https://lemmy.nine-hells.net",
- "https://lemmy.ninja",
- "https://lemmy.nix-community.org",
- "https://lemmy.noice.social",
- "https://lemmy.noogs.me",
- "https://lemmy.nope.foo",
- "https://lemmy.nope.ly",
- "https://lemmy.nopro.be",
- "https://lemmy.nowsci.com",
- "https://lemmy.nrd.li",
- "https://lemmy.nrgup.net",
- "https://lemmy.nyc.what.if.ua",
- "https://lemmy.nz",
- "https://lemmy.obrell.se",
- "https://lemmy.ohaa.xyz",
- "https://lemmy.okla.social",
- "https://lemmy.oldtr.uk",
- "https://lemmy.omat.nl",
- "https://lemmy.one",
- "https://lemmy.onlylans.io",
- "https://lemmy.opensupply.space",
- "https://lemmy.orefice.win",
- "https://lemmy.org.il",
- "https://lemmy.otakufarms.com",
- "https://lemmy.oursphere.space",
- "https://lemmy.over-world.org",
- "https://lemmy.p3nguin.org",
- "https://lemmy.packitsolutions.net",
- "https://lemmy.parastor.net",
- "https://lemmy.pastwind.top",
- "https://lemmy.paulstevens.org",
- "https://lemmy.pcft.eu",
- "https://lemmy.pe1uca.dev",
- "https://lemmy.penguinore.net",
- "https://lemmy.phoenix591.com",
- "https://lemmy.physfluids.fr",
- "https://lemmy.pierre-couy.fr",
- "https://lemmy.pipe01.net",
- "https://lemmy.piperservers.net",
- "https://lemmy.pit.ninja",
- "https://lemmy.pixelcollider.net",
- "https://lemmy.place",
- "https://lemmy.plaseo.org",
- "https://lemmy.podycust.co.uk",
- "https://lemmy.potatoe.ca",
- "https://lemmy.poundncashdown.com",
- "https://lemmy.praxis.red",
- "https://lemmy.procrastinati.org",
- "https://lemmy.prograhamming.com",
- "https://lemmy.programmerhumor.io",
- "https://lemmy.pt",
- "https://lemmy.pub",
- "https://lemmy.pubsub.fun",
- "https://lemmy.pussthecat.org",
- "https://lemmy.pwarde.nl",
- "https://lemmy.radio",
- "https://lemmy.ramble.moe",
- "https://lemmy.rat.academy",
- "https://lemmy.ravc.tech",
- "https://lemmy.razbot.xyz",
- "https://lemmy.reckless.dev",
- "https://lemmy.remotelab.uk",
- "https://lemmy.remoteplay.im",
- "https://lemmy.rhetro.de",
- "https://lemmy.rhymelikedi.me",
- "https://lemmy.rimkus.it",
- "https://lemmy.robotra.sh",
- "https://lemmy.roembol.nl",
- "https://lemmy.ruhr",
- "https://lemmy.run",
- "https://lemmy.s9m.xyz",
- "https://lemmy.safe-internet.org",
- "https://lemmy.saik0.com",
- "https://lemmy.samad.one",
- "https://lemmy.sambands.net",
- "https://lemmy.sarcasticdeveloper.com",
- "https://lemmy.scam-mail.me",
- "https://lemmy.schlunker.com",
- "https://lemmy.schoenwolf-schroeder.com",
- "https://lemmy.schuerz.at",
- "https://lemmy.scootaloo.pl",
- "https://lemmy.scottlabs.io",
- "https://lemmy.sdf.org",
- "https://lemmy.sdfcn.org",
- "https://lemmy.sdfeu.org",
- "https://lemmy.sdfjp.org",
- "https://lemmy.sedimentarymountains.com",
- "https://lemmy.seifert.id",
- "https://lemmy.self-hosted.site",
- "https://lemmy.servarr.com",
- "https://lemmy.server.fifthdread.com",
- "https://lemmy.setzman.synology.me",
- "https://lemmy.sherry.moe",
- "https://lemmy.shihaam.me",
- "https://lemmy.shinomoroll.net",
- "https://lemmy.shiny-task.com",
- "https://lemmy.shtuf.eu",
- "https://lemmy.sietch.online",
- "https://lemmy.simpl.website",
- "https://lemmy.skoops.social",
- "https://lemmy.skyjake.fi",
- "https://lemmy.smay.dev",
- "https://lemmy.smeargle.fans",
- "https://lemmy.snoot.tube",
- "https://lemmy.socdojo.com",
- "https://lemmy.sotu.casa",
- "https://lemmy.spacestation14.com",
- "https://lemmy.srcfiles.zip",
- "https://lemmy.srv.eco",
- "https://lemmy.srv0.lol",
- "https://lemmy.ssba.com",
- "https://lemmy.st",
- "https://lemmy.stad.social",
- "https://lemmy.staphup.nl",
- "https://lemmy.stark-enterprise.net",
- "https://lemmy.starless.one",
- "https://lemmy.starlightkel.xyz",
- "https://lemmy.starmade.de",
- "https://lemmy.steken.xyz",
- "https://lemmy.stonansh.org",
- "https://lemmy.stuart.fun",
- "https://lemmy.sudoer.ch",
- "https://lemmy.sumuun.net",
- "https://lemmy.svc.vesey.tech",
- "https://lemmy.sveri.de",
- "https://lemmy.syntrofos.xyz",
- "https://lemmy.sysctl.io",
- "https://lemmy.tangresh.ch",
- "https://lemmy.tanktrace.de",
- "https://lemmy.tario.org",
- "https://lemmy.tarsis.org",
- "https://lemmy.tazy.xyz",
- "https://lemmy.teaisatfour.com",
- "https://lemmy.tealshark.net",
- "https://lemmy.team",
- "https://lemmy.tebro.fi",
- "https://lemmy.techhaven.io",
- "https://lemmy.technosorcery.net",
- "https://lemmy.technowizardry.net",
- "https://lemmy.techtailors.net",
- "https://lemmy.techtriage.guru",
- "https://lemmy.tedomum.net",
- "https://lemmy.telaax.com",
- "https://lemmy.temporus.me",
- "https://lemmy.tespia.org",
- "https://lemmy.teuto.icu",
- "https://lemmy.tf",
- "https://lemmy.tgxn.net",
- "https://lemmy.the-burrow.com",
- "https://lemmy.thebias.nl",
- "https://lemmy.theia.cafe",
- "https://lemmy.themainframe.org",
- "https://lemmy.thenewgaming.de",
- "https://lemmy.theonecurly.page",
- "https://lemmy.thepixelproject.com",
- "https://lemmy.thesanewriter.com",
- "https://lemmy.thias.xyz",
- "https://lemmy.timwaterhouse.com",
- "https://lemmy.titanplusplus.online",
- "https://lemmy.tobyvin.dev",
- "https://lemmy.today",
- "https://lemmy.toldi.eu",
- "https://lemmy.toot.pt",
- "https://lemmy.tr00st.co.uk",
- "https://lemmy.trevor.coffee",
- "https://lemmy.trippy.pizza",
- "https://lemmy.trizz.io",
- "https://lemmy.trojaner.dev",
- "https://lemmy.ubergeek77.chat",
- "https://lemmy.uhhoh.com",
- "https://lemmy.umainfo.live",
- "https://lemmy.unboiled.info",
- "https://lemmy.unfiltered.social",
- "https://lemmy.uninsane.org",
- "https://lemmy.unknownsys.com",
- "https://lemmy.unryzer.eu",
- "https://lemmy.urbanhost.top",
- "https://lemmy.utveckla.re",
- "https://lemmy.va-11-hall-a.cafe",
- "https://lemmy.vepta.org",
- "https://lemmy.vinodjam.com",
- "https://lemmy.virtim.dev",
- "https://lemmy.void.bh",
- "https://lemmy.vonbergcompany.de",
- "https://lemmy.vrchat-dev.tech",
- "https://lemmy.vyizis.tech",
- "https://lemmy.w9r.de",
- "https://lemmy.website",
- "https://lemmy.weomucat.com",
- "https://lemmy.whitedragonofcroatia.com",
- "https://lemmy.wizjenkins.com",
- "https://lemmy.works",
- "https://lemmy.world",
- "https://lemmy.wtf",
- "https://lemmy.x3i.tech",
- "https://lemmy.xcoolgroup.com",
- "https://lemmy.xeviousx.eu",
- "https://lemmy.xoynq.com",
- "https://lemmy.xylight.dev",
- "https://lemmy.yachts",
- "https://lemmy.z0r.co",
- "https://lemmy.zell-mbc.com",
- "https://lemmy.zhukov.al",
- "https://lemmy.zimage.com",
- "https://lemmy.zip",
- "https://lemmy.zwanenburg.info",
- "https://lemmy.zxcvn.xyz",
- "https://lemmy2.addictmud.org",
- "https://lemmyadmin.site",
- "https://lemmyf.uk",
- "https://lemmyfi.com",
- "https://lemmyfly.org",
- "https://lemmygrad.ml",
- "https://lemmyhub.com",
- "https://lemmyis.fun",
- "https://lemmyland.com",
- "https://lemmyngs.social",
- "https://lemmynsfw.com",
- "https://lemmyonline.com",
- "https://lemmys.hivemind.at",
- "https://lemmyunchained.net",
- "https://lemmyverse.org",
- "https://lemthony.com",
- "https://lemux.minnix.dev",
- "https://lemy.lol",
- "https://lemy.nl",
- "https://leuker.me",
- "https://lib.lgbt",
- "https://libreauto.app",
- "https://libretechni.ca",
- "https://liminal.southfox.me",
- "https://linkage.ds8.zone",
- "https://linkopath.com",
- "https://links.aa4.eu",
- "https://links.dartboard.social",
- "https://links.esq.social",
- "https://links.hackliberty.org",
- "https://links.rebel.ar",
- "https://links.rocks",
- "https://links.roobre.es",
- "https://linux.community",
- "https://linz.city",
- "https://literature.cafe",
- "https://livesound.world",
- "https://lm.bittervets.org",
- "https://lm.blythhub.com",
- "https://lm.boing.icu",
- "https://lm.byteme.social",
- "https://lm.gsk.moe",
- "https://lm.helilot.com",
- "https://lm.inu.is",
- "https://lm.korako.me",
- "https://lm.madiator.cloud",
- "https://lm.mythoranium.com",
- "https://lm.paradisus.day",
- "https://lm.put.tf",
- "https://lm.sethp.cc",
- "https://lm.suitwaffle.com",
- "https://lm.thesqooid.com",
- "https://lm.williampuckering.com",
- "https://lmmy.dk",
- "https://lmmy.io",
- "https://lmmy.net",
- "https://lmmy.tvdl.dev",
- "https://lmy.brx.io",
- "https://lmy.drundo.com.au",
- "https://lmy.sagf.io",
- "https://lmy.singinwhale.com",
- "https://local106.com",
- "https://lonestarlemmy.mooo.com",
- "https://lostcheese.com",
- "https://ls.buckodr.ink",
- "https://lsmu.schmurian.xyz",
- "https://lu.skbo.net",
- "https://lululemmy.com",
- "https://lx.pontual.social",
- "https://mander.xyz",
- "https://matejc.com",
- "https://matts.digital",
- "https://meganice.online",
- "https://mementomori.school",
- "https://merv.news",
- "https://metapowers.org",
- "https://mh.znark.us",
- "https://michelsup.org",
- "https://midwest.social",
- "https://milksteak.org",
- "https://mimiclem.me",
- "https://mlem.hackular.com",
- "https://mlem.me",
- "https://monero.im",
- "https://monero.town",
- "https://monyet.cc",
- "https://moto.teamswollen.org",
- "https://mtgzone.com",
- "https://mujico.org",
- "https://murffys-place.club",
- "https://my.lserver.dev",
- "https://mylemmy.win",
- "https://nano.garden",
- "https://nc.gnzl.cl",
- "https://netmonkey.tech",
- "https://news.cosocial.ca",
- "https://news.idlestate.org",
- "https://nichenerdery.duckdns.org",
- "https://nickbuilds.net",
- "https://nlemmy.nl",
- "https://no.lastname.nz",
- "https://nom.mom",
- "https://nopeeking.link",
- "https://normalcity.life",
- "https://notdigg.com",
- "https://notyour.rodeo",
- "https://nwdr.club",
- "https://occult.social",
- "https://odin.lanofthedead.xyz",
- "https://orbiting.observer",
- "https://orcas.enjoying.yachts",
- "https://ou0.de",
- "https://outpost.zeuslink.net",
- "https://overctrl.dbzer0.com",
- "https://parenti.sh",
- "https://partizle.com",
- "https://pasta.faith",
- "https://pawb.social",
- "https://poptalk.scrubbles.tech",
- "https://pornlemmy.com",
- "https://positroni.ddns.net",
- "https://possumpat.io",
- "https://posta.no",
- "https://postit.quantentoast.de",
- "https://precious.net",
- "https://preserve.games",
- "https://pricefield.org",
- "https://prime8s.xyz",
- "https://programming.dev",
- "https://proit.org",
- "https://psychedelia.ink",
- "https://purpleit.net",
- "https://qlemmy.com",
- "https://quokk.au",
- "https://r-sauna.fi",
- "https://r.irithyll.cc",
- "https://r.nf",
- "https://radiation.party",
- "https://raeuberwald.de",
- "https://random-hero.com",
- "https://rblind.com",
- "https://rc.liftoff-app.org",
- "https://re.tei.li",
- "https://real.lemmy.fan",
- "https://red.cyberhase.de",
- "https://reddit.moonbeam.town",
- "https://reddrefuge.com",
- "https://reddthat.com",
- "https://rekabu.ru",
- "https://rentadrunk.org",
- "https://reseed.it",
- "https://retarded.dev",
- "https://retrolemmy.com",
- "https://ripo.st",
- "https://rollenspiel.forum",
- "https://rqd2.net",
- "https://rss.confusedoncoffee.com",
- "https://s.jape.work",
- "https://sammich.es",
- "https://scooby.one",
- "https://seemel.ink",
- "https://selfhosted.forum",
- "https://septic.win",
- "https://seriously.iamincredibly.gay",
- "https://sffa.community",
- "https://sh.itjust.works",
- "https://sha1.nl",
- "https://shi.tmight.work",
- "https://shinobu.cloud",
- "https://showeq.com",
- "https://silkky.pub",
- "https://slangenettet.pyjam.as",
- "https://slrpnk.net",
- "https://snake.substantialplumbing.repair",
- "https://snowstorm.online",
- "https://snuv.win",
- "https://soc.ebmn.io",
- "https://soccer.forum",
- "https://social.bug.expert",
- "https://social.coalition.space",
- "https://social.cyb3r.dog",
- "https://social.fr4me.io",
- "https://social.ggbox.fr",
- "https://social.jears.at",
- "https://social.kennypu.com",
- "https://social.nerdhouse.io",
- "https://social.packetloss.gg",
- "https://social.poisson.me",
- "https://social.rocketsfall.net",
- "https://social.venith.net",
- "https://some.institute",
- "https://sopuli.xyz",
- "https://spgrn.com",
- "https://spudwart.com",
- "https://stable.liftoff-app.org",
- "https://stammtisch.hallertau.social",
- "https://startrek.website",
- "https://sub.wetshaving.social",
- "https://subsubd.com",
- "https://superdark.social",
- "https://supernova.place",
- "https://suppo.fi",
- "https://support.futbol",
- "https://surom.de",
- "https://swg-empire.de",
- "https://switter.su",
- "https://szmer.info",
- "https://t.bobamilktea.xyz",
- "https://tabletop.place",
- "https://tacobu.de",
- "https://talk.kururin.tech",
- "https://talk.macstack.net",
- "https://talk.neovibe.app",
- "https://talka.live",
- "https://techy.news",
- "https://textandmetal.com",
- "https://the.unknowing.dance",
- "https://theculture.social",
- "https://thelemmy.club",
- "https://theotter.social",
- "https://thesidewalkends.io",
- "https://thevapor.space",
- "https://threads.ruin.io",
- "https://tiny.lizard.supply",
- "https://tkohhh.social",
- "https://toast.ooo",
- "https://toons.zone",
- "https://tortoisewrath.com",
- "https://tsck.org",
- "https://ttrpg.network",
- "https://tucson.social",
- "https://twisti.ca",
- "https://unreachable.cloud",
- "https://upvote.au",
- "https://usenet.lol",
- "https://va11halla.bar",
- "https://vastactive.online",
- "https://victoriagaming.ca",
- "https://voltage.vn",
- "https://voxpop.social",
- "https://voyager.lemmy.ml",
- "https://werm.social",
- "https://whemic.xyz",
- "https://whiskers.bim.boats",
- "https://wilbo.tech",
- "https://wired.bluemarch.art",
- "https://withachanceof.com",
- "https://x69.org",
- "https://xbdv.com",
- "https://xffxe4.lol",
- "https://xrs.cx",
- "https://xyz.higurashi.xyz",
- "https://yah.lol",
- "https://yall.theatl.social",
- "https://yamasaur.com",
- "https://yiffit.net",
- "https://ythreektech.com",
- "https://yukistorm.com",
- "https://zemmy.cc",
- "https://zen.lema.cl",
- "https://zerobytes.monster",
- "https://zoo.splitlinux.org",
- "https://zorg.social"
- ]);
- var INSTANCES_KBIN = /* @__PURE__ */ new Set([
- "https://artemis.camp",
- "https://atakbin.spacehost.dev",
- "https://bin.pol.social",
- "https://community.yshi.org",
- "https://dgngrnder.com",
- "https://feddit.online",
- "https://fedinews.net",
- "https://fusionpatrol.social",
- "https://hotaudiofiction.social",
- "https://jlailu.social",
- "https://k.fe.derate.me",
- "https://karab.in",
- "https://kayb.ee",
- "https://kbin-u3.vm.elestio.app",
- "https://kbin.bitgoblin.tech",
- "https://kbin.cafe",
- "https://kbin.chat",
- "https://kbin.ectolab.net",
- "https://kbin.fedi.cr",
- "https://kbin.life",
- "https://kbin.nz",
- "https://kbin.pieho.me",
- "https://kbin.pithyphrase.net",
- "https://kbin.projectsegfau.lt",
- "https://kbin.social",
- "https://kbin.tenkuu.social",
- "https://kbin.thicknahalf.com",
- "https://kbin.wangwood.house",
- "https://kglitch.social",
- "https://kopnij.in",
- "https://kx.pontual.social",
- "https://link.fossdle.org",
- "https://longley.ws",
- "https://nadajnik.org",
- "https://polesie.pol.social",
- "https://rainy.place",
- "https://social.tath.link",
- "https://supermeter.social",
- "https://teacup.social",
- "https://wiku.hu"
- ]);
- trace(`Define instances sets end`);
- // src/our-changes.js
- var OUR_CHANGES = { addedNodes: {} };
- function getAddedNodesSelectors() {
- return Object.values(OUR_CHANGES.addedNodes);
- }
- function registerAddedNode(id, selector) {
- OUR_CHANGES.addedNodes[id] = selector;
- }
- // src/constants.js
- var constants_default = {
- ICON_CLASS: withNS(`icon`),
- ICON_LOADING_CLASS: withNS(`loading`),
- ICON_STYLES_ID: withNS(`icon-styles`),
- ICON_LINK_CLASS: withNS(`icon-link`),
- ICON_LINK_SYMBOL_ID: withNS(`icon-link-symbol`),
- ICON_SPINNER_CLASS: withNS(`icon-spinner`),
- ICON_SPINNER_SYMBOL_ID: withNS(`icon-spinner-symbol`),
- ORIGINAL_LINK_CLASS: withNS(`original-link`),
- SHOW_AT_HOME_BUTTON_CLASS: withNS(`show-at-home`),
- ICON_SVG_TEMPLATE_ID: withNS(`icon-template`),
- AUTH_WRONG: `AUTH_WRONG`,
- AUTH_MISSING: `AUTH_MISSING`,
- REWRITE_STATUS: withNSCamelCase(`localUrlStatus`),
- REWRITE_STATUS_PENDING: `pending`,
- REWRITE_STATUS_SUCCESS: `success`,
- REWRITE_STATUS_ERROR: `error`,
- REWRITE_STATUS_UNRESOLVED: `unresolved`,
- SETUP_AUTH_MESSAGE: `Lemmy Universal Link Switcher: Visit your home instance once to set up post/comment rewriting`,
- SETTINGS_BUTTON_ID: withNS(`open-settings-button`),
- SETTINGS_MENU_ID: withNS(`settings`),
- SETTINGS_STYLES_ID: withNS(`settings-styles`)
- };
- function withNS(identifier) {
- return `lemmy-rewrite-urls-` + identifier;
- }
- function withNSCamelCase(identifier) {
- return `lemmyRewriteUrls` + identifier.charAt(0).toUpperCase() + identifier.slice(1);
- }
- // src/rewriting/helpers.js
- function isHashLink(link) {
- return link.hash && link.origin + link.pathname + link.search === location.origin + location.pathname + location.search;
- }
- function isSamePage(url1, url2) {
- return url1.host === url2.host && url1.pathname === url2.pathname;
- }
- function isV17() {
- return window.isoData?.site_res?.version.startsWith(`0.17`);
- }
- var stopEventHandler = (event) => {
- event.preventDefault();
- event.stopPropagation();
- };
- // src/gm.js
- async function setValue(key, value) {
- trace(`GM.setValue key ${key}, value ${value}`);
- await GM.setValue(key, value);
- }
- async function getValue(key) {
- return await GM.getValue(key);
- }
- function parseResponse(response) {
- try {
- return JSON.parse(response.response);
- } catch (e) {
- debug(`Error parsing response JSON`, e);
- return response.response;
- }
- }
- function logRequest(response, data) {
- trace(
- `FinalUrl`,
- response.finalUrl,
- `status`,
- response.status,
- `text`,
- response.statusText,
- `response`,
- data || response.response
- );
- trace(`responseHeaders`, response.responseHeaders);
- }
- function performXmlHttpRequest(url, doneCallback) {
- GM.xmlHttpRequest({
- url,
- onloadend: (response) => {
- const data = parseResponse(response);
- logRequest(response, data);
- doneCallback(response, data);
- }
- });
- }
- function registerMenuCommand(name, onClick) {
- GM.registerMenuCommand(name, onClick);
- }
- // src/rewriting/auth.js
- var AUTH;
- function getAuthFromCookie() {
- return document.cookie.split("; ").find((row) => row.startsWith("jwt="))?.split("=")[1];
- }
- async function setAuth(auth) {
- AUTH = auth;
- await setValue(`auth`, auth);
- }
- async function initAuth() {
- const curAuth = await getAuth();
- if (curAuth) {
- AUTH = curAuth;
- return;
- }
- if (location.origin === HOME) {
- const newAuth = getAuthFromCookie();
- await setAuth(newAuth);
- if (newAuth && await getValue(`authNoticeShown`)) {
- alert(`Lemmy Universal Link Switcher: Post/comment rewriting has been set up successfully`);
- await setValue(`authNoticeShown`, null);
- }
- } else if (HOME && !await getValue(`authNoticeShown`)) {
- await setValue(`authNoticeShown`, `true`);
- alert(constants_default.SETUP_AUTH_MESSAGE);
- }
- }
- function updateAuthPeriodically() {
- setInterval(async () => {
- const prev = AUTH;
- const newAuth = location.origin === HOME ? getAuthFromCookie() : await getAuth();
- if (prev !== newAuth) {
- debug(`Auth changed`);
- await setAuth(newAuth);
- clearMissingUrlsInCache();
- triggerRewrite();
- }
- }, 1234);
- }
- async function getAuth() {
- return await getValue(`auth`);
- }
- // src/rewriting/url-mapping.js
- function splitPaths(url) {
- return url.pathname.split(`/`).slice(1);
- }
- function isRemoteLemmyUrl(url) {
- return !isHomeInstance(url) && isLemmyInstance(url);
- }
- function isRemoteKbinUrl(url) {
- return !isHomeInstance(url) && isKbinInstance(url);
- }
- function isRemoteUrl(url) {
- return isRemoteLemmyUrl(url) || isRemoteKbinUrl(url);
- }
- function findLocalUrlForStandardAtFormat(url, rootPath) {
- const paths = splitPaths(url);
- const name = paths[1].includes(`@`) ? paths[1] : paths[1] + `@` + url.host;
- return `${HOME}/${rootPath || paths[0]}/${name}` + url.search + url.hash;
- }
- function findLocalUrlForLemmyUrl(url) {
- if (isLemmyUserOrCommunityUrl(url)) {
- return findLocalUrlForStandardAtFormat(url);
- } else {
- return null;
- }
- }
- function findLocalUrlForKbinUserUrl(url) {
- const paths = splitPaths(url);
- const user = paths[1].startsWith(`@`) ? paths[1].substring(1) : paths[1];
- const name = user.includes(`@`) ? user : user + `@` + url.host;
- return `${HOME}/u/${name}` + url.search + url.hash;
- }
- function findLocalUrlForKbinUrl(url) {
- if (isKbinMagazineUrl(url)) {
- return findLocalUrlForStandardAtFormat(url, mappedKbinRootPath(url));
- } else if (isKbinUserUrl(url)) {
- return findLocalUrlForKbinUserUrl(url);
- } else {
- return null;
- }
- }
- function findLocalUrl(url) {
- if (isRemoteLemmyUrl(url))
- return findLocalUrlForLemmyUrl(url);
- if (isRemoteKbinUrl(url))
- return findLocalUrlForKbinUrl(url);
- return null;
- }
- async function fetchApIdFromRemote(url) {
- const endpoint = isLemmyPostUrl(url) ? `post` : `comment`;
- const paths = splitPaths(url);
- const id = paths[1];
- return new Promise((resolve, reject) => {
- performXmlHttpRequest(`${url.origin}/api/v3/${endpoint}?id=${id}`, (response, data) => {
- const apId = data[`${endpoint}_view`]?.[endpoint]?.ap_id;
- if (response.status === 200 && apId) {
- resolve(apId);
- } else {
- handleFailedRequest(`fetching AP ID`, response, reject);
- }
- });
- });
- }
- function handleFailedRequest(requestName, response, reject) {
- if (response.status >= 200 && response.status <= 299) {
- reject(`${requestName}: Unhandled successful response, status: ${response.status}`);
- } else if (response.status >= 400 && response.status <= 599) {
- reject(`${requestName}: Error, status: ${response.status}`);
- } else {
- reject(`${requestName}: Something weird happened, status: ${response.status}`);
- }
- }
- async function resolveObjectFromHome(url) {
- return new Promise(async (resolve, reject) => {
- const auth = await getAuth();
- if (!auth) {
- debug(`No auth token found`);
- reject(constants_default.AUTH_MISSING);
- }
- performXmlHttpRequest(
- `${HOME}/api/v3/resolve_object?auth=${auth}&q=${encodeURIComponent(url.href)}`,
- (response, data) => {
- if (response.status === 200 && data.post?.post?.id) {
- resolve(`${HOME}/post/${data.post.post.id}${url.search}${url.hash}`);
- } else if (response.status === 200 && data.comment?.comment?.id) {
- resolve(`${HOME}/comment/${data.comment.comment.id}${url.search}${url.hash}`);
- } else if (response.status === 400 && data?.error === `couldnt_find_object`) {
- resolve(null);
- } else if (response.status === 400 && data?.error === `not_logged_in`) {
- reject(constants_default.AUTH_WRONG);
- } else {
- handleFailedRequest(`resolving object`, response, reject);
- }
- }
- );
- });
- }
- var urlCache = {};
- function clearMissingUrlsInCache() {
- for (const value of Object.values(urlCache)) {
- if (value.error)
- delete value.error;
- if (value.localUrl === null)
- delete value.localUrl;
- }
- }
- function getCacheKey(url) {
- return url.host + url.pathname + url.search;
- }
- function cacheResult(url, localUrl) {
- const key = getCacheKey(url);
- if (!urlCache[key]) {
- urlCache[key] = {};
- }
- if (urlCache[key].error)
- delete urlCache[key].error;
- urlCache[key].localUrl = localUrl;
- return localUrl;
- }
- function cacheErrorResult(url, error) {
- const key = getCacheKey(url);
- if (!urlCache[key]) {
- urlCache[key] = {};
- }
- urlCache[key].error = error;
- }
- function getLocalUrlfromCache(url) {
- const key = getCacheKey(url);
- if (urlCache[key]?.error) {
- throw urlCache[key]?.error;
- } else {
- return urlCache[key]?.localUrl;
- }
- }
- async function fetchLocalUrl(url, loadFromCache = true) {
- if (loadFromCache) {
- const cached = getLocalUrlfromCache(url);
- if (cached !== void 0) {
- trace(`Found URL ${url} in cache: ${cached}`);
- return cached;
- }
- }
- try {
- return cacheResult(url, await fetchLocalUrlNoCache(url));
- } catch (e) {
- debug(`fetchLocalUrl error`, e);
- cacheErrorResult(url, e);
- throw e;
- }
- }
- async function fetchLocalUrlNoCache(url) {
- trace(`Trying to resolve URL ${url} directly`);
- const localUrl = await resolveObjectFromHome(url);
- if (localUrl !== null) {
- return localUrl;
- } else {
- trace(`Did not find URL ${url} directly`);
- }
- const apId = new URL(await fetchApIdFromRemote(url));
- trace(`Found AP ID for URL ${url}: ${apId}`);
- if (!apId.search) {
- apId.search = url.search;
- }
- if (!apId.hash) {
- apId.hash = url.hash;
- }
- if (isSamePage(url, apId)) {
- trace(`Previous URL was AP ID already, URL not federated for some reason`);
- return null;
- } else {
- return await resolveObjectFromHome(apId);
- }
- }
- function isInstantlyRewritable(url) {
- return isRemoteLemmyUrl(url) && isLemmyUserOrCommunityUrl(url) || isRemoteKbinUrl(url) && (isKbinMagazineUrl(url) || isKbinUserUrl(url));
- }
- function isRewritableAfterResolving(url) {
- return isRemoteLemmyUrl(url) && (isLemmyPostUrl(url) || isLemmyCommentUrl(url));
- }
- function isLemmyPostUrl(url) {
- const paths = splitPaths(url);
- return paths[0] === `post`;
- }
- function isLemmyCommentUrl(url) {
- const paths = splitPaths(url);
- return paths[0] === `comment`;
- }
- function isLemmyUserOrCommunityUrl(url) {
- const paths = splitPaths(url);
- return paths[0] === `c` || paths[0] === `u`;
- }
- function isKbinPostUrl(url) {
- const paths = splitPaths(url);
- return paths[0] === `m` && paths.length > 2 && paths[2] === `t`;
- }
- function isKbinMicroblogUrl(url) {
- const paths = splitPaths(url);
- return paths[0] === `m` && paths.length > 2 && paths[2] === `p`;
- }
- function isKbinMicroblogOverviewUrl(url) {
- const paths = splitPaths(url);
- return paths[0] === `m` && paths.length > 2 && paths[2] === `microblog`;
- }
- function isKbinMagazinePeopleUrl(url) {
- const paths = splitPaths(url);
- return paths[0] === `m` && paths.length > 2 && paths[2] === `people`;
- }
- function isKbinMagazineUrl(url) {
- const paths = splitPaths(url);
- return paths[0] === `m` && !isKbinPostUrl(url) && !isKbinMagazinePeopleUrl(url) && !isKbinMicroblogUrl(url) && !isKbinMicroblogOverviewUrl(url);
- }
- function mappedKbinRootPath(url) {
- const paths = splitPaths(url);
- if (paths[0] === `m`) {
- return `c`;
- } else {
- return null;
- }
- }
- function isKbinUserUrl(url) {
- const paths = splitPaths(url);
- return paths.length === 2 && paths[0] === `u`;
- }
- // src/rewriting/links/icon.js
- function getIcon(link) {
- return link.querySelector(`.` + constants_default.ICON_CLASS);
- }
- function createIcon(link) {
- ensureTemplateAvailable();
- ensureIconStylesAdded();
- const wrapper = document.createElement(`span`);
- registerAddedNode(constants_default.ICON_CLASS, `.` + constants_default.ICON_CLASS);
- wrapper.classList.add(constants_default.ICON_CLASS);
- if (link.children.length === 0 || getComputedStyle(link.children[link.children.length - 1]).marginRight === `0px`) {
- wrapper.style.marginLeft = `0.5em`;
- }
- const linkIcon = createSVG();
- linkIcon.classList.add(constants_default.ICON_LINK_CLASS);
- linkIcon.innerHTML = `<use href=#${constants_default.ICON_LINK_SYMBOL_ID} />`;
- wrapper.append(linkIcon);
- const spinnerIcon = createSVG();
- spinnerIcon.classList.add(constants_default.ICON_SPINNER_CLASS);
- spinnerIcon.innerHTML = `<use href=#${constants_default.ICON_SPINNER_SYMBOL_ID} />`;
- wrapper.append(spinnerIcon);
- return wrapper;
- }
- function createSVG() {
- return document.createElementNS(`http://www.w3.org/2000/svg`, `svg`);
- }
- function ensureTemplateAvailable() {
- if (document.querySelector(`#` + constants_default.ICON_SVG_TEMPLATE_ID))
- return;
- const template = createSVG();
- template.id = constants_default.ICON_SVG_TEMPLATE_ID;
- template.innerHTML = `<defs>
- <symbol id=${constants_default.ICON_LINK_SYMBOL_ID} viewBox="0 0 100 100"><path d="M52.8 34.6c.8.8 1.8 1.2 2.8 1.2s2-.4 2.8-1.2c1.5-1.5 1.5-4 0-5.6l-5.2-5.2h26v30.6c0 2.2 1.8 3.9 3.9 3.9 2.2 0 3.9-1.8 3.9-3.9V19.8c0-2.2-1.8-3.9-3.9-3.9h-30l5.2-5.2c1.5-1.5 1.5-4 0-5.6s-4-1.5-5.6 0l-11.8 12c-1.5 1.5-1.5 4 0 5.6l11.9 11.9zM31.1 28.7V11c0-3-2.5-5.5-5.5-5.5H8C5 5.5 2.5 8 2.5 11v17.7c0 3 2.5 5.5 5.5 5.5h17.7c3 0 5.4-2.5 5.4-5.5zM47.2 65.4c-1.5-1.5-4-1.5-5.6 0s-1.5 4 0 5.6l5.2 5.2h-26V45.6c0-2.2-1.8-3.9-3.9-3.9S13 43.5 13 45.6v34.5c0 2.2 1.8 3.9 3.9 3.9h30l-5.2 5.2c-1.5 1.5-1.5 4 0 5.6.8.8 1.8 1.2 2.8 1.2s2-.4 2.8-1.2l11.9-11.9c1.5-1.5 1.5-4 0-5.6l-12-11.9zM92 65.8H74.4c-3 0-5.5 2.5-5.5 5.5V89c0 3 2.5 5.5 5.5 5.5H92c3 0 5.5-2.5 5.5-5.5V71.3c0-3-2.5-5.5-5.5-5.5z"/></symbol>
- <symbol id=${constants_default.ICON_SPINNER_SYMBOL_ID} viewBox="0 0 32 32"><path d="M16 32c-4.274 0-8.292-1.664-11.314-4.686s-4.686-7.040-4.686-11.314c0-3.026 0.849-5.973 2.456-8.522 1.563-2.478 3.771-4.48 6.386-5.791l1.344 2.682c-2.126 1.065-3.922 2.693-5.192 4.708-1.305 2.069-1.994 4.462-1.994 6.922 0 7.168 5.832 13 13 13s13-5.832 13-13c0-2.459-0.69-4.853-1.994-6.922-1.271-2.015-3.066-3.643-5.192-4.708l1.344-2.682c2.615 1.31 4.824 3.313 6.386 5.791 1.607 2.549 2.456 5.495 2.456 8.522 0 4.274-1.664 8.292-4.686 11.314s-7.040 4.686-11.314 4.686z"/></symbol>
- </defs>`;
- registerAddedNode(constants_default.ICON_SVG_TEMPLATE_ID, `#` + constants_default.ICON_SVG_TEMPLATE_ID);
- document.head.append(template);
- }
- function ensureIconStylesAdded() {
- if (document.querySelector(`#` + constants_default.ICON_STYLES_ID))
- return;
- const style = document.createElement(`style`);
- style.id = constants_default.ICON_STYLES_ID;
- style.innerHTML = `
- .${constants_default.ICON_SPINNER_CLASS} {
- display: none;
- animation: spins 2s linear infinite;
- }
- .${constants_default.ICON_LINK_CLASS} {
- display: inline-block;
- }
- .${constants_default.ICON_LOADING_CLASS} > .${constants_default.ICON_LINK_CLASS} {
- display: none;
- }
- .${constants_default.ICON_LOADING_CLASS} > .${constants_default.ICON_SPINNER_CLASS} {
- display: inline-block;
- }
- .${constants_default.ICON_CLASS} > svg {
- vertical-align: sub;
- height: 1em; width: 1em;
- stroke: currentColor;
- fill: currentColor;
- }`;
- registerAddedNode(constants_default.ICON_STYLES_ID, `#` + constants_default.ICON_STYLES_ID);
- document.head.append(style);
- }
- // src/tippy.js
- var tippy_default = window.tippy;
- // src/rewriting/links/tooltip.js
- function getOriginalLinkHtml(originalHref) {
- registerAddedNode(constants_default.ORIGINAL_LINK_CLASS, `.` + constants_default.ORIGINAL_LINK_CLASS);
- return `Original link: <a class="${constants_default.ORIGINAL_LINK_CLASS}" href="${originalHref}">${originalHref}</a>`;
- }
- function defaultOptions(link) {
- return {
- appendTo: () => link.parentNode,
- allowHTML: true,
- interactive: true,
- animation: false,
- placement: "bottom",
- hideOnClick: false
- };
- }
- function createOriginalLinkTooltip(link, originalHref) {
- trace(`Create original link tooltip`, link, originalHref);
- getIcon(link).addEventListener(`click`, stopEventHandler);
- return createLinkTooltip(link, getOriginalLinkHtml(originalHref));
- }
- function createLinkTooltip(link, content) {
- return tippy_default(getIcon(link), {
- ...defaultOptions(link),
- content
- });
- }
- function createLinkLoadTooltip(link) {
- trace(`Create link load tooltip`, link);
- getIcon(link).classList.add(constants_default.ICON_LOADING_CLASS);
- return createLinkTooltip(link, `Loading home URL...<br />Don't want to wait? ${getOriginalLinkHtml(link.href)}`);
- }
- function linkLoadTooltipSuccess(tooltip, originalHref) {
- linkLoadResult(tooltip, `\u2714\uFE0F Changed link to home instance`, getOriginalLinkHtml(originalHref));
- }
- function linkLoadTooltipError(tooltip, error) {
- linkLoadResult(tooltip, `\u274C ` + error);
- }
- function linkLoadResult(tooltip, result, finalContent = result) {
- const icon = tooltip.reference;
- icon.classList.remove(constants_default.ICON_LOADING_CLASS);
- icon.addEventListener(`click`, stopEventHandler);
- if (tooltip.state.isVisible) {
- tooltip.setContent(result);
- setTimeout(() => {
- tooltip.hide();
- tooltip.setContent(finalContent);
- }, 2e3);
- } else {
- tooltip.setContent(finalContent);
- }
- }
- // src/rewriting/links/links.js
- function changeLinkHref(link, localUrl) {
- const treeWalker = document.createTreeWalker(link, NodeFilter.SHOW_TEXT, (node) => {
- if (node.textContent.toLowerCase().trim() === link.href.toLowerCase().trim()) {
- return NodeFilter.FILTER_ACCEPT;
- } else {
- return NodeFilter.FILTER_SKIP;
- }
- });
- let textNode;
- while ((textNode = treeWalker.nextNode()) !== null) {
- textNode.textContent = localUrl;
- }
- link.href = localUrl;
- link.addEventListener(`click`, (event) => {
- if (event.button === 0 && !event.ctrlKey && !event.metaKey && link.target !== `_blank`) {
- location.href = localUrl;
- }
- });
- }
- function appendIconTo(elem, icon) {
- if (elem.children.length === 0 || getComputedStyle(elem.children[elem.children.length - 1]).display !== `inline-block`) {
- elem.append(icon);
- } else {
- appendIconTo(elem.children[elem.children.length - 1], icon);
- }
- }
- function addFetchLocalUrlHandler(link) {
- let tooltip;
- const handler = async (event) => {
- if (event.type === `click`) {
- stopEventHandler(event);
- if (tooltip)
- tooltip.show();
- return;
- }
- link.removeEventListener(`focus`, handler);
- link.removeEventListener(`mouseenter`, handler);
- if (link.dataset[constants_default.REWRITE_STATUS] === constants_default.REWRITE_STATUS_PENDING)
- return;
- link.dataset[constants_default.REWRITE_STATUS] = constants_default.REWRITE_STATUS_PENDING;
- tooltip = createLinkLoadTooltip(link);
- try {
- const localUrl = await fetchLocalUrl(link);
- if (!localUrl) {
- debug(`Local URL for ${link.href} could not be found`);
- linkLoadTooltipError(tooltip, `Home URL could not be found`);
- return;
- }
- trace(`Local URL for ${link.href} found: ${localUrl}`);
- const oldHref = link.href;
- changeLinkHref(link, localUrl);
- linkLoadTooltipSuccess(tooltip, oldHref);
- link.dataset[constants_default.REWRITE_STATUS] = constants_default.REWRITE_STATUS_SUCCESS;
- } catch (e) {
- debug(`Error while trying to resolve local URL`, e);
- let msg;
- if (e === constants_default.AUTH_WRONG) {
- msg = `Saved login expired. Return to your home instance and log in again.`;
- } else if (e === constants_default.AUTH_MISSING) {
- msg = constants_default.SETUP_AUTH_MESSAGE;
- } else {
- msg = `Error while trying to find home URL`;
- }
- linkLoadTooltipError(tooltip, msg);
- link.dataset[constants_default.REWRITE_STATUS] = constants_default.REWRITE_STATUS_ERROR;
- } finally {
- link.removeEventListener(`click`, handler);
- }
- };
- link.addEventListener(`click`, handler);
- link.addEventListener(`focus`, handler);
- link.addEventListener(`mouseenter`, handler);
- }
- function isFediverseLink(link) {
- const svg = link.querySelector(`svg`);
- if (!svg)
- return false;
- if (svg.children.length === 0)
- return false;
- return svg.children[0].getAttribute(`xlink:href`)?.includes(`#icon-fedilink`);
- }
- function rewriteToLocal(link) {
- if (!link.parentNode)
- return false;
- if (link.classList.contains(constants_default.ORIGINAL_LINK_CLASS))
- return false;
- if (link.dataset[constants_default.REWRITE_STATUS] === constants_default.REWRITE_STATUS_SUCCESS)
- return false;
- if (isHashLink(link))
- return false;
- if (!isRemoteUrl(link))
- return false;
- if (isFediverseLink(link))
- return false;
- if (isInstantlyRewritable(link)) {
- const localUrl = findLocalUrl(link);
- if (!localUrl)
- return false;
- if (isSamePage(new URL(localUrl), location))
- return false;
- const oldHref = link.href;
- changeLinkHref(link, localUrl);
- const icon = createIcon(link);
- appendIconTo(link, icon);
- createOriginalLinkTooltip(link, oldHref);
- link.dataset[constants_default.REWRITE_STATUS] = constants_default.REWRITE_STATUS_SUCCESS;
- trace(`Rewrite link`, link, ` from`, oldHref, `to`, localUrl);
- return true;
- } else if (isRewritableAfterResolving(link)) {
- if (!getIcon(link)) {
- appendIconTo(link, createIcon(link));
- }
- if (!link.dataset[constants_default.REWRITE_STATUS]) {
- link.dataset[constants_default.REWRITE_STATUS] = constants_default.REWRITE_STATUS_UNRESOLVED;
- addFetchLocalUrlHandler(link);
- }
- }
- }
- function findLinksInChange(change) {
- if (change.type === `childList`) {
- const links = Array.from(change.addedNodes).flatMap((addedNode) => {
- if (addedNode.tagName?.toLowerCase() === `a`) {
- return addedNode;
- } else if (addedNode.querySelectorAll) {
- return Array.from(addedNode.querySelectorAll(`a`));
- } else {
- return [];
- }
- });
- if (links.length > 0)
- trace(`Change`, change, `contained the links`, links);
- return links;
- } else if (change.type === `attributes`) {
- return change.target.matches?.(`a`) ? change.target : [];
- } else {
- return [];
- }
- }
- function findLinksToRewrite(changes) {
- if (!changes) {
- return document.querySelectorAll(`a`);
- }
- return changes.flatMap(findLinksInChange);
- }
- async function rewriteLinksToLocal(changes) {
- const links = findLinksToRewrite(changes);
- const chunkSize = 50;
- return await async function processChunk(currentChunk) {
- const startIdx = currentChunk * chunkSize;
- const endChunkIdx = (currentChunk + 1) * chunkSize;
- const endIdx = Math.min(links.length, endChunkIdx);
- debug(
- `Processing ${links.length} links, current chunk `,
- currentChunk,
- `processing links ${startIdx} to ${endIdx}`
- );
- let anyRewritten = false;
- for (let i = startIdx; i < endIdx; ++i) {
- const rewritten = rewriteToLocal(links[i]);
- anyRewritten = anyRewritten || rewritten;
- }
- debug(`Processed links ${startIdx} to ${endIdx}`);
- if (endChunkIdx >= links.length) {
- return anyRewritten;
- }
- const chunkResult = await new Promise((resolve) => setTimeout(async () => {
- resolve(await processChunk(currentChunk + 1));
- }, 0));
- return anyRewritten || chunkResult;
- }(0);
- }
- // src/settings.js
- var refreshMethods = [];
- async function refresh() {
- for (const refreshMethod of refreshMethods) {
- await refreshMethod();
- }
- }
- function closeSettings() {
- document.querySelector(`#` + constants_default.SETTINGS_MENU_ID)?.remove();
- refreshMethods = [];
- }
- function initSettings() {
- registerMenuCommand(`Open Settings`, showSettings);
- }
- function styles(elem, style) {
- for (const [prop, val] of Object.entries(style)) {
- elem.style[prop] = val;
- }
- }
- var defaultBorder = `1px solid #AAA`;
- function createButton(content, options = {}) {
- const button = document.createElement(`div`);
- button.innerHTML = content;
- styles(button, {
- display: options.inline ? `inline-block` : `block`,
- backgroundColor: `#666`,
- textAlign: `center`,
- border: defaultBorder,
- cursor: `pointer`,
- borderRadius: `8px 8px`,
- padding: `5px`
- });
- return button;
- }
- function ensureMenuStylesAdded() {
- if (document.querySelector(`#` + constants_default.SETTINGS_STYLES_ID))
- return;
- const style = document.createElement(`style`);
- style.id = constants_default.SETTINGS_STYLES_ID;
- style.innerHTML = `
- #${constants_default.SETTINGS_MENU_ID} * {
- box-sizing: border-box;
- }
- `;
- document.head.append(style);
- }
- function showSettings() {
- if (document.querySelector(`#` + constants_default.SETTINGS_MENU_ID))
- return;
- if (window !== window.top)
- return;
- ensureMenuStylesAdded();
- const background = document.createElement(`div`);
- background.id = constants_default.SETTINGS_MENU_ID;
- registerAddedNode(constants_default.SETTINGS_MENU_ID, `#` + constants_default.SETTINGS_MENU_ID);
- styles(background, {
- position: `fixed`,
- top: `0`,
- left: `0`,
- zIndex: 9999,
- width: `100vw`,
- height: `100vh`,
- backgroundColor: `#00000099`,
- backdropFilter: `blur(6px)`,
- display: `grid`,
- grid: `grid`,
- alignItems: `center`,
- justifyContent: `space-around`
- });
- background.addEventListener(`click`, (event) => {
- if (event.target === background) {
- closeSettings();
- }
- });
- const menu = document.createElement(`div`);
- styles(menu, {
- position: `relative`,
- color: `#ddd`,
- maxWidth: `90vw`,
- maxHeight: `90vh`,
- backgroundColor: `#555`,
- padding: `20px`,
- border: defaultBorder,
- borderRadius: `8px`,
- overflow: `hidden auto`
- });
- background.append(menu);
- const closeButton = createButton(`\u{1F5D9}`);
- styles(closeButton, {
- position: `absolute`,
- top: `-1px`,
- right: `-1px`,
- width: `30px`,
- height: `30px`,
- lineHeight: `25px`,
- fontSize: `25px`,
- borderRadius: `0 8px`,
- padding: 0
- });
- closeButton.addEventListener(`click`, () => closeSettings());
- menu.append(closeButton);
- const menuHeader = createSettingHeader(`Lemmy Universal Link Switcher Settings`, 1.5);
- styles(menuHeader, {
- textDecoration: `underline`,
- marginTop: 0
- });
- menu.append(menuHeader);
- addMainHomeInstanceSetting(menu);
- addMoreHomeInstancesSettings(menu);
- document.body.append(background);
- }
- function createSettingHeader(text, sizeMultiplicator = 1) {
- const header = document.createElement(`div`);
- header.innerHTML = text;
- styles(header, {
- marginRight: `10px`,
- fontSize: `${sizeMultiplicator * 1.4}em`,
- lineHeight: `${sizeMultiplicator * 1.4}em`,
- fontWeight: `bold`,
- marginTop: `${sizeMultiplicator * 25}px`,
- marginBottom: `${sizeMultiplicator * 10}px`
- });
- return header;
- }
- function addInfo(elem, content) {
- const info = document.createElement(`span`);
- info.innerHTML = ` \u{1F6C8}`;
- tippy_default(info, {
- content,
- placement: "bottom",
- triggerTarget: elem
- });
- elem.append(info);
- }
- function createInput(options) {
- const inputElement = document.createElement(`input`);
- styles(inputElement, {
- width: `100%`,
- margin: 0
- });
- if (options.placeholder) {
- inputElement.placeholder = options.placeholder;
- }
- if (options.getter) {
- Promise.resolve(options.getter()).then((result) => inputElement.value = result);
- }
- if (!inputElement.value) {
- inputElement.setCustomValidity(`empty`);
- }
- styles(inputElement, { border: defaultBorder });
- if (!options.validator && !options.setter) {
- return;
- }
- let validator = options.validator || (() => true);
- let setter = options.setter || (() => {
- });
- inputElement.addEventListener(`input`, async () => {
- const validated = await validator(inputElement.value);
- if (!inputElement.value || validated) {
- inputElement.setCustomValidity(`empty`);
- styles(inputElement, {
- border: defaultBorder
- });
- }
- if (inputElement.value) {
- if (validated) {
- inputElement.setCustomValidity(``);
- await setter(inputElement.value);
- } else if (!validated) {
- inputElement.setCustomValidity(`fail`);
- styles(inputElement, {
- border: `1px solid red`
- });
- }
- }
- });
- return inputElement;
- }
- var urlValidator = (value) => {
- try {
- new URL(value);
- return true;
- } catch (e) {
- return false;
- }
- };
- var instancePlaceholder = `The full link (including http(s)://) to the instance`;
- function addMainHomeInstanceSetting(addTo) {
- const header = createSettingHeader(`Main Home Instance`);
- addInfo(header, `All links will be rewritten to this instance, except for links to your secondary home instances.`);
- addTo.append(header);
- const mainInstance = createInput({
- getter: () => HOME,
- setter: (value) => {
- const url = new URL(value);
- if (HOME !== url.origin) {
- setHome(url.origin);
- refresh();
- }
- },
- validator: urlValidator,
- placeholder: instancePlaceholder
- });
- addTo.append(mainInstance);
- const homeButtonWrapper = document.createElement(`div`);
- const refreshMakeHomeButton = () => {
- homeButtonWrapper.replaceChildren();
- if (location.origin === HOME)
- return;
- const makeHomeButton = createButton(`Use current page as home instance`);
- styles(makeHomeButton, {
- borderRadius: `0 0 8px 8px`
- });
- makeHomeButton.addEventListener(`click`, () => {
- mainInstance.value = location.origin;
- mainInstance.dispatchEvent(new Event(`input`));
- });
- homeButtonWrapper.append(makeHomeButton);
- };
- refreshMakeHomeButton();
- refreshMethods.push(refreshMakeHomeButton);
- addTo.append(homeButtonWrapper);
- }
- async function addMoreHomeInstancesSettings(addTo) {
- const header = createSettingHeader(`Secondary Home Instances`);
- addInfo(header, `All links pointing to these instances will not be changed.`);
- addTo.append(header);
- addTo.append(createListInput(
- async () => await getSecondaryHomeInstances(),
- async (value) => {
- const homeInstances = await getSecondaryHomeInstances();
- homeInstances.push(new URL(value).origin);
- await setSecondaryHomeInstances(homeInstances);
- },
- async (value) => {
- const homeInstances = await getSecondaryHomeInstances();
- var index = homeInstances.indexOf(new URL(value).origin);
- if (index > -1) {
- homeInstances.splice(index, 1);
- }
- await setSecondaryHomeInstances(homeInstances);
- }
- ));
- }
- function createListItem(item, onClick) {
- const listItem = createButton(item + ` \u{1F5D9}`, { inline: true });
- styles(listItem, {
- margin: `0px 5px 5px 0`
- });
- listItem.addEventListener(`click`, onClick);
- return listItem;
- }
- function createListInput(getter, add, remove) {
- const wrapper = document.createElement(`div`);
- const list = document.createElement(`div`);
- styles(list, {
- marginBottom: `8px`
- });
- const refreshList = async () => {
- const items = (await getter()).sort().map((item) => createListItem(item, async () => {
- await remove(item);
- refresh();
- }));
- if (items.length) {
- list.replaceChildren(...items);
- } else {
- list.replaceChildren(`<None>`);
- }
- };
- refreshList();
- refreshMethods.push(refreshList);
- wrapper.append(list);
- const addInput = createInput({
- validator: async (value) => {
- return urlValidator(value) && !(await getter()).includes(value);
- },
- placeholder: instancePlaceholder
- });
- wrapper.append(addInput);
- const buttonWrapper = document.createElement(`div`);
- const refreshButtons = async () => {
- buttonWrapper.replaceChildren();
- const isCurrentPageHome = HOME === location.origin || (await getter()).includes(location.origin);
- const addButton2 = createButton(`Add`);
- styles(addButton2, {
- borderRadius: isCurrentPageHome ? `0 0 8px 8px` : `0`
- });
- addButton2.addEventListener(`click`, async () => {
- if (addInput.validity.valid) {
- await add(addInput.value);
- addInput.value = ``;
- refresh();
- }
- });
- buttonWrapper.append(addButton2);
- if (!isCurrentPageHome) {
- const addCurrentButton = createButton(`Add current page`);
- styles(addCurrentButton, {
- borderRadius: `0 0 8px 8px`
- });
- addCurrentButton.addEventListener(`click`, async () => {
- await add(location.origin);
- addInput.value = ``;
- refresh();
- });
- buttonWrapper.append(addCurrentButton);
- }
- };
- refreshButtons();
- refreshMethods.push(refreshButtons);
- wrapper.append(buttonWrapper);
- return wrapper;
- }
- // src/rewriting/settings-buttons.js
- function addSettingsButton() {
- if (location.pathname !== `/settings`)
- return false;
- if (!document.querySelector(`[name="Description"][content="Lemmy"]`))
- return false;
- if (document.querySelector(`#` + constants_default.SETTINGS_BUTTON_ID))
- return false;
- const insertAfter = document.querySelector(`#user-password`)?.closest(`.card`);
- if (!insertAfter)
- return;
- const button = document.createElement(`button`);
- button.id = constants_default.SETTINGS_BUTTON_ID;
- button.setAttribute(`class`, `btn btn-block btn-primary mr-4 w-100`);
- button.innerHTML = `Lemmy Universal Link Switcher Settings`;
- button.addEventListener(`click`, showSettings);
- registerAddedNode(constants_default.SETTINGS_BUTTON_ID, `#` + constants_default.SETTINGS_BUTTON_ID);
- insertAfter.insertAdjacentElement("afterend", button);
- return true;
- }
- // src/rewriting/show-at-home.js
- function showAtHomeButtonText() {
- const host = new URL(HOME).hostname;
- return `Show on ${host}`;
- }
- function createShowAtHomeAnchor(localUrl) {
- const showAtHome = document.createElement(`a`);
- showAtHome.dataset.creationHref = location.href;
- showAtHome.classList.add(constants_default.SHOW_AT_HOME_BUTTON_CLASS);
- showAtHome.innerHTML = showAtHomeButtonText();
- showAtHome.href = localUrl;
- registerAddedNode(constants_default.SHOW_AT_HOME_BUTTON_CLASS, `.` + constants_default.SHOW_AT_HOME_BUTTON_CLASS);
- return showAtHome;
- }
- function addLemmyShowAtHomeButton(localUrl) {
- const logo = document.querySelector(`a.navbar-brand`);
- const navbarIcons = isV17() ? document.querySelector(`[title="Search"]`)?.closest(`.navbar-nav`) : document.querySelector(`#navbarIcons`);
- if (!logo || !navbarIcons) {
- debug(`Could not find position to insert ShowAtHomeButton at`);
- return false;
- }
- const mobile = createShowAtHomeAnchor(localUrl);
- mobile.classList.add(`d-md-none`);
- mobile.style[`margin-right`] = `8px`;
- mobile.style[`margin-left`] = `auto`;
- if (isV17()) {
- document.querySelector(`.navbar-nav.ml-auto`)?.classList.remove(`ml-auto`);
- }
- logo.insertAdjacentElement("afterend", mobile);
- const desktop = createShowAtHomeAnchor(localUrl);
- desktop.classList.add(`d-md-inline`);
- desktop.style[`margin-right`] = `8px`;
- navbarIcons.insertAdjacentElement("beforebegin", desktop);
- return true;
- }
- function addKbinShowAtHomeButton(localUrl) {
- const prependTo = document.querySelector(`#header menu:not(.head-nav__menu)`);
- if (!prependTo) {
- debug(`Could not find position to insert ShowAtHomeButton at`);
- return false;
- }
- const item = document.createElement(`li`);
- item.append(createShowAtHomeAnchor(localUrl));
- prependTo.prepend(item);
- return true;
- }
- function addButton(localUrl) {
- const oldButton = document.querySelectorAll(`.` + constants_default.SHOW_AT_HOME_BUTTON_CLASS);
- if (oldButton.length > 0 && oldButton[0].dataset.creationHref !== location.href) {
- debug(`Removing old show at home button`);
- oldButton.forEach((btn) => btn.remove());
- } else if (oldButton.length > 0) {
- debug(`Old show at home button still exists`);
- return false;
- }
- if (!localUrl) {
- debug(`No local URL for show at home button found`);
- return false;
- } else if (isRemoteLemmyUrl(location)) {
- return addLemmyShowAtHomeButton(localUrl);
- } else if (isRemoteKbinUrl(location)) {
- return addKbinShowAtHomeButton(localUrl);
- } else {
- return false;
- }
- }
- async function addShowAtHomeButton() {
- if (isInstantlyRewritable(location)) {
- return addButton(findLocalUrl(location));
- } else if (isRewritableAfterResolving(location)) {
- try {
- return addButton(await fetchLocalUrl(location));
- } catch (e) {
- debug(`Error while trying to add "show at home" button`, e);
- }
- }
- }
- // src/rewriting/rewrite.js
- function triggerRewrite() {
- doAllDomChanges();
- }
- function isOrHasOurAddedNode(node) {
- return getAddedNodesSelectors().some((selector) => node.matches?.(selector) || node.querySelector?.(selector));
- }
- function isIrrelevantChange(change) {
- if (change.type === `childList`) {
- if (Array.from(change.removedNodes).some(isOrHasOurAddedNode)) {
- trace(`Change`, change, `is relevant because a removed node is/has ours`);
- return false;
- }
- if (!Array.from(change.addedNodes).every(isOrHasOurAddedNode)) {
- trace(`Change`, change, `is relevant because not every added node is/has ours`);
- return false;
- }
- } else if (change.type === `attributes` && isRemoteUrl(new URL(change.target.href, location.origin))) {
- trace(`Change`, change, `is relevant because href changed to a remote URL`);
- return false;
- }
- trace(`Change`, change, `is irrelevant`);
- return true;
- }
- async function startRewriting() {
- new MutationObserver((changes, observer) => {
- debug(`MutationObserver triggered`, changes);
- if (changes.every(isIrrelevantChange)) {
- debug(`All observed changes are irrelevant`);
- return;
- }
- doAllDomChanges(changes);
- }).observe(document.body, {
- subtree: true,
- childList: true,
- attributeFilter: [`href`]
- });
- await doAllDomChanges();
- }
- async function doAllDomChanges(changes) {
- debug(`doAllDomChanges start`);
- const addedSettingsButtons = addSettingsButton();
- if (addedSettingsButtons)
- debug(`Added Settings Buttons`);
- const addedShowAtHomeButton = HOME ? addShowAtHomeButton() : false;
- if (addedShowAtHomeButton)
- debug(`Added Show At Home Button`);
- const rewrittenLinks = HOME ? await rewriteLinksToLocal(changes) : false;
- if (rewrittenLinks)
- debug(`Rewritten some links`);
- debug(`doAllDomChanges end`);
- }
- // src/home.js
- var HOME;
- var secondaryHomes = [];
- async function initHome() {
- HOME = await getValue(`home`);
- secondaryHomes = await getSecondaryHomeInstances();
- if (!HOME && isLemmyInstance(location) && confirm(`Lemmy Universal Link Switcher: Set this instance to be your home instance to which all URLs get rewritten to?`)) {
- setHome(location.origin);
- }
- }
- async function setHome(newHome) {
- trace(`Set HOME from ${HOME} to ${newHome}`);
- if (typeof newHome !== `string`) {
- newHome = null;
- }
- HOME = newHome;
- await setValue(`home`, newHome);
- }
- async function getHome() {
- return await getValue(`home`);
- }
- function updateHomePeriodically() {
- trace(`Current HOME`, HOME);
- setInterval(async () => {
- const prevHome = HOME;
- const prevSecondaries = secondaryHomes;
- HOME = await getHome();
- secondaryHomes = await getSecondaryHomeInstances();
- if (prevHome !== HOME) {
- debug(`HOME changed externally from`, prevHome, `to`, HOME);
- triggerRewrite();
- } else if (prevSecondaries.length !== secondaryHomes.length || !prevSecondaries.every((v) => secondaryHomes.includes(v))) {
- debug(`secondaryHomes changed externally from`, prevSecondaries, `to`, secondaryHomes);
- triggerRewrite();
- }
- }, 1337);
- }
- function isHomeInstance(url) {
- return secondaryHomes.concat(HOME).includes(url.origin);
- }
- async function getSecondaryHomeInstances() {
- const homeInstancesStr = await getValue(`secondaryHomes`);
- return homeInstancesStr ? JSON.parse(homeInstancesStr) : [];
- }
- async function setSecondaryHomeInstances(homeInstances) {
- await setValue(`secondaryHomes`, JSON.stringify(homeInstances));
- }
- // src/main.js
- (async () => {
- await initHome();
- updateHomePeriodically();
- await initAuth();
- updateAuthPeriodically();
- initSettings();
- startRewriting();
- })();
- })();