Lemmy Universal Link Switcher

Ensures that all URLs to Lemmy instances always point to your main/home instance.

当前为 2025-02-24 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Lemmy Universal Link Switcher
  3. // @namespace http://azzurite.tv/
  4. // @license GPLv3
  5. // @version 1.3.3
  6. // @description Ensures that all URLs to Lemmy instances always point to your main/home instance.
  7. // @homepageURL https://gitlab.com/azzurite/lemmy-universal-link-switcher
  8. // @supportURL https://gitlab.com/azzurite/lemmy-universal-link-switcher/-/issues
  9. // @author Azzurite
  10. // @match *://*/*
  11. // @icon https://gitlab.com/azzurite/lemmy-universal-link-switcher/-/raw/main/resources/favicon.png?inline=true
  12. // @grant GM.setValue
  13. // @grant GM.getValue
  14. // @grant GM.xmlHttpRequest
  15. // @grant GM.registerMenuCommand
  16. // @connect *
  17. // @require https://unpkg.com/@popperjs/core@2
  18. // @require https://unpkg.com/tippy.js@6
  19. // ==/UserScript==
  20.  
  21. (() => {
  22. // src/debug.js
  23. var DEBUG = false;
  24. function debug() {
  25. if (DEBUG)
  26. console.debug(`Rewriter | `, ...arguments);
  27. }
  28. function trace() {
  29. if (DEBUG === `trace`)
  30. console.debug(`Rewriter Trace | `, ...arguments);
  31. }
  32.  
  33. // src/instances.js
  34. function isLemmyInstance(url) {
  35. return isInstance(INSTANCES_LEMMY, url);
  36. }
  37. function isKbinInstance(url) {
  38. return isInstance(INSTANCES_KBIN, url);
  39. }
  40. function isInstance(instances, url) {
  41. if (url.origin) {
  42. return instances.has(url.origin);
  43. } else {
  44. return false;
  45. }
  46. }
  47. trace(`Define instances sets start`);
  48. var INSTANCES_LEMMY = /* @__PURE__ */ new Set([
  49. "http://lemmy.brdsnest.net",
  50. "http://lemmy.nijboer.chat",
  51. "https://0v0.social",
  52. "https://0xdd.org.ru",
  53. "https://1337lemmy.com",
  54. "https://2dl.eu",
  55. "https://3t.au",
  56. "https://4wd.social",
  57. "https://7.62x54r.ru",
  58. "https://acqrs.co.uk",
  59. "https://actuallyruben.nl",
  60. "https://adding.space",
  61. "https://adultswim.fan",
  62. "https://agora.nop.chat",
  63. "https://aiparadise.moe",
  64. "https://algebro.xyz",
  65. "https://alien.top",
  66. "https://anarch.is",
  67. "https://androiddev.network",
  68. "https://ani.social",
  69. "https://animoe.xyz",
  70. "https://areality.social",
  71. "https://arpa.dev",
  72. "https://asocial.thedroth.rocks",
  73. "https://astraea.pink",
  74. "https://aussie.zone",
  75. "https://awful.systems",
  76. "https://azgil.net",
  77. "https://badatbeing.social",
  78. "https://baraza.africa",
  79. "https://bbs.9tail.net",
  80. "https://bbs.wznmickey.com",
  81. "https://beehaw.org",
  82. "https://beer.andma.la",
  83. "https://belfry.rip",
  84. "https://bethe.kingofdog.de",
  85. "https://biglemmowski.win",
  86. "https://bin.pztrn.online",
  87. "https://bluuit.org",
  88. "https://board.minimally.online",
  89. "https://bolha.forum",
  90. "https://bookwormstory.social",
  91. "https://boomer.casino",
  92. "https://borg.chat",
  93. "https://botnet.club",
  94. "https://boulder.ly",
  95. "https://bulletintree.com",
  96. "https://burggit.moe",
  97. "https://butts.international",
  98. "https://campfyre.nickwebster.dev",
  99. "https://casavaga.com",
  100. "https://catata.fish",
  101. "https://catgirl.pub",
  102. "https://cavy.rocks",
  103. "https://cdda.social",
  104. "https://chinese.lol",
  105. "https://citizensgaming.com",
  106. "https://civilloquy.com",
  107. "https://cocte.au",
  108. "https://code4lib.net",
  109. "https://codesink.io",
  110. "https://communick.news",
  111. "https://communities.azkware.net",
  112. "https://community.adiquaints.moe",
  113. "https://community.nicfab.it",
  114. "https://community.o0o.social",
  115. "https://compuverse.uk",
  116. "https://corndog.social",
  117. "https://corrigan.space",
  118. "https://crowfx.web.id",
  119. "https://ctrlaltelite.xyz",
  120. "https://czech-lemmy.eu",
  121. "https://dandroid.app",
  122. "https://dendarii.alaeron.com",
  123. "https://derp.foo",
  124. "https://derpzilla.net",
  125. "https://dev.automationwise.com",
  126. "https://digipres.cafe",
  127. "https://dirtbag.social",
  128. "https://dis.ney.ink",
  129. "https://disc.0x-ia.moe",
  130. "https://discover.deltanauten.de",
  131. "https://discuss.dizl.de",
  132. "https://discuss.icewind.me",
  133. "https://discuss.jacen.moe",
  134. "https://discuss.ntfy.sh",
  135. "https://discuss.online",
  136. "https://discuss.tchncs.de",
  137. "https://discuss.yussuv.com",
  138. "https://disflux.org",
  139. "https://distress.digital",
  140. "https://dit.reformed.social",
  141. "https://diyrpg.org",
  142. "https://dmv.social",
  143. "https://donky.social",
  144. "https://dormi.zone",
  145. "https://drlemmy.net",
  146. "https://ds9.lemmy.ml",
  147. "https://dubvee.org",
  148. "https://dumbdomain.xyz",
  149. "https://einweckglas.com",
  150. "https://emmyl.com",
  151. "https://endlesstalk.org",
  152. "https://enterprise.lemmy.ml",
  153. "https://esc.ozar.dev",
  154. "https://eslemmy.es",
  155. "https://eventfrontier.com",
  156. "https://eviltoast.org",
  157. "https://exabytes.space",
  158. "https://ezekielrage.com",
  159. "https://falconry.party",
  160. "https://fanaticus.social",
  161. "https://fanexus.com",
  162. "https://fed.dyne.org",
  163. "https://fed.rosssi.co.uk",
  164. "https://feddit.ch",
  165. "https://feddit.cl",
  166. "https://feddit.de",
  167. "https://feddit.dk",
  168. "https://feddit.eu",
  169. "https://feddit.fun",
  170. "https://feddit.it",
  171. "https://feddit.jp",
  172. "https://feddit.nl",
  173. "https://feddit.nu",
  174. "https://feddit.nz",
  175. "https://feddit.ro",
  176. "https://feddit.rocks",
  177. "https://feddit.site",
  178. "https://feddit.strike23.de",
  179. "https://feddit.uk",
  180. "https://federated.community",
  181. "https://federation.red",
  182. "https://fedi.alc.im",
  183. "https://fedii.me",
  184. "https://fedimav.win",
  185. "https://fedit.io",
  186. "https://fediverse.ro",
  187. "https://fediverse.zeekzack.com",
  188. "https://feed.newt.wtf",
  189. "https://fjdk.uk",
  190. "https://fl0w.cc",
  191. "https://flamewar.social",
  192. "https://fludiblu.xyz",
  193. "https://forkk.me",
  194. "https://foro.markoop.org",
  195. "https://foros.fediverso.gal",
  196. "https://forum.liberatedsystems.co.uk",
  197. "https://forum.stellarcastle.net",
  198. "https://fost.hu",
  199. "https://fry.gs",
  200. "https://futurology.today",
  201. "https://gadgetro.id",
  202. "https://gathering.pikafan.de",
  203. "https://gioia.news",
  204. "https://gonelemmy.xyz",
  205. "https://goon.vip",
  206. "https://group.lt",
  207. "https://hackertalks.com",
  208. "https://halubilo.social",
  209. "https://happysl.app",
  210. "https://hedge.town",
  211. "https://hexagonsun.one",
  212. "https://hexbear.net",
  213. "https://hive.atlanten.se",
  214. "https://hobbit.world",
  215. "https://hoihoi.superboi.eu.org",
  216. "https://hoodlem.me",
  217. "https://hq.unit520.wtf",
  218. "https://hyperfair.link",
  219. "https://i.d0ntknow.me",
  220. "https://iceorchid.net",
  221. "https://ihax0r.com",
  222. "https://info.prou.be",
  223. "https://infosec.pub",
  224. "https://iusearchlinux.fyi",
  225. "https://jamie.moe",
  226. "https://jemmy.jeena.net",
  227. "https://jlai.lu",
  228. "https://just.vibin.wtf",
  229. "https://kaijus.us",
  230. "https://kbin.tancomps.net",
  231. "https://keeb.lol",
  232. "https://kerala.party",
  233. "https://keylog.zip",
  234. "https://kleptonix.com",
  235. "https://klingon.nl",
  236. "https://krabb.org",
  237. "https://kulupu.duckdns.org",
  238. "https://kutsuya.dev",
  239. "https://kuu.kohana.fi",
  240. "https://kyberpunk.social",
  241. "https://kyu.de",
  242. "https://l.60228.dev",
  243. "https://l.7rg1nt.moe",
  244. "https://l.alexdevs.me",
  245. "https://l.antiope.link",
  246. "https://l.biendeo.com",
  247. "https://l.clearbackblast.com",
  248. "https://l.dusty-radio.com",
  249. "https://l.hedonistlab.com",
  250. "https://l.henlo.fi",
  251. "https://l.jugregator.org",
  252. "https://l.matestmc.ru",
  253. "https://l.mathers.fr",
  254. "https://l.mchome.net",
  255. "https://l.os33.co",
  256. "https://l.plabs.social",
  257. "https://l.quou.xyz",
  258. "https://l.r6e.dev",
  259. "https://l.roofo.cc",
  260. "https://l.shoddy.site",
  261. "https://l.sw0.com",
  262. "https://l.vidja.social",
  263. "https://l3mmy.com",
  264. "https://lassmich.wtf",
  265. "https://lazysoci.al",
  266. "https://le-me.xyz",
  267. "https://le.fduck.net",
  268. "https://le.mnau.xyz",
  269. "https://le.weme.wtf",
  270. "https://leaf.dance",
  271. "https://lebowski.social",
  272. "https://leby.dev",
  273. "https://leddit.danmark.party",
  274. "https://lef.li",
  275. "https://lem.cochrun.xyz",
  276. "https://lem.free.as",
  277. "https://lem.lucitt.social",
  278. "https://lem.monster",
  279. "https://lem.nimmog.uk",
  280. "https://lem.serkozh.me",
  281. "https://lem.simple-gear.com",
  282. "https://lem.trashbrain.org",
  283. "https://lem.ur-mom.gay",
  284. "https://lemdro.id",
  285. "https://leminal.space",
  286. "https://lemm.ee",
  287. "https://lemmerz.org",
  288. "https://lemmie.be",
  289. "https://lemming.quest",
  290. "https://lemmings.sopelj.ca",
  291. "https://lemmings.world",
  292. "https://lemmit.online",
  293. "https://lemmony.click",
  294. "https://lemmus.org",
  295. "https://lemmy-api.ten4ward.social",
  296. "https://lemmy.0upti.me",
  297. "https://lemmy.100010101.xyz",
  298. "https://lemmy.1204.org",
  299. "https://lemmy.3cm.us",
  300. "https://lemmy.505dude.com",
  301. "https://lemmy.86thumbs.net",
  302. "https://lemmy.8th.world",
  303. "https://lemmy.absolutesix.com",
  304. "https://lemmy.aguiarvieira.pt",
  305. "https://lemmy.ahall.se",
  306. "https://lemmy.al",
  307. "https://lemmy.albertoluna.es",
  308. "https://lemmy.amxl.com",
  309. "https://lemmy.ananace.dev",
  310. "https://lemmy.animal-machine.com",
  311. "https://lemmy.anonion.social",
  312. "https://lemmy.antemeridiem.xyz",
  313. "https://lemmy.antisocial.ly",
  314. "https://lemmy.anymore.nl",
  315. "https://lemmy.appeine.com",
  316. "https://lemmy.asc6.org",
  317. "https://lemmy.avata.social",
  318. "https://lemmy.basedcount.com",
  319. "https://lemmy.baswag.de",
  320. "https://lemmy.bayern",
  321. "https://lemmy.belegost.net",
  322. "https://lemmy.beru.co",
  323. "https://lemmy.best",
  324. "https://lemmy.bezzie.world",
  325. "https://lemmy.bg",
  326. "https://lemmy.bigsecretwebsite.net",
  327. "https://lemmy.billiam.net",
  328. "https://lemmy.bismith.net",
  329. "https://lemmy.bitgoblin.tech",
  330. "https://lemmy.blad.is",
  331. "https://lemmy.blahaj.zone",
  332. "https://lemmy.bleh.au",
  333. "https://lemmy.bloodmoon-network.de",
  334. "https://lemmy.blugatch.tube",
  335. "https://lemmy.bmck.au",
  336. "https://lemmy.boltwolf.net",
  337. "https://lemmy.bond",
  338. "https://lemmy.bosio.info",
  339. "https://lemmy.bothhands.ca",
  340. "https://lemmy.brad.ee",
  341. "https://lemmy.brennoflavio.com.br",
  342. "https://lemmy.brief.guru",
  343. "https://lemmy.bringdaruck.us",
  344. "https://lemmy.browntown.dev",
  345. "https://lemmy.bryant.moe",
  346. "https://lemmy.brynstuff.co.uk",
  347. "https://lemmy.buller.cc",
  348. "https://lemmy.byrdcrouse.com",
  349. "https://lemmy.byte-a.net",
  350. "https://lemmy.byteunion.com",
  351. "https://lemmy.ca",
  352. "https://lemmy.cafe",
  353. "https://lemmy.calvss.com",
  354. "https://lemmy.canyonero.net",
  355. "https://lemmy.capebreton.social",
  356. "https://lemmy.cat",
  357. "https://lemmy.catasaur.xyz",
  358. "https://lemmy.catattack.win",
  359. "https://lemmy.catgirl.biz",
  360. "https://lemmy.cfras.net",
  361. "https://lemmy.ch3n2k.com",
  362. "https://lemmy.chiisana.net",
  363. "https://lemmy.cloud.aboutcher.co.uk",
  364. "https://lemmy.clueware.org",
  365. "https://lemmy.cnschn.com",
  366. "https://lemmy.co.nz",
  367. "https://lemmy.coffee-beanz.com",
  368. "https://lemmy.cogindo.net",
  369. "https://lemmy.comfysnug.space",
  370. "https://lemmy.commodore.social",
  371. "https://lemmy.conk.me",
  372. "https://lemmy.conorab.com",
  373. "https://lemmy.cook.gg",
  374. "https://lemmy.craftycanine.net",
  375. "https://lemmy.crimedad.work",
  376. "https://lemmy.criticalbasics.xyz",
  377. "https://lemmy.croc.pw",
  378. "https://lemmy.crowpro.net",
  379. "https://lemmy.cryptoriot.org",
  380. "https://lemmy.csupes.page",
  381. "https://lemmy.cultimean.group",
  382. "https://lemmy.cwain.dk",
  383. "https://lemmy.d.thewooskeys.com",
  384. "https://lemmy.darmstadt.social",
  385. "https://lemmy.darvit.nl",
  386. "https://lemmy.datatekniker.dev",
  387. "https://lemmy.davidfreina.at",
  388. "https://lemmy.davidzeiger.net",
  389. "https://lemmy.dayl.in",
  390. "https://lemmy.dblclk.dev",
  391. "https://lemmy.dbzer0.com",
  392. "https://lemmy.dcmrobertson.com",
  393. "https://lemmy.dcrich.net",
  394. "https://lemmy.decronym.xyz",
  395. "https://lemmy.deepspace.gay",
  396. "https://lemmy.deev.io",
  397. "https://lemmy.deltaa.xyz",
  398. "https://lemmy.demonoftheday.eu",
  399. "https://lemmy.despotes.nl",
  400. "https://lemmy.dexlit.xyz",
  401. "https://lemmy.digital-alchemy.app",
  402. "https://lemmy.digitalcharon.in",
  403. "https://lemmy.digitalfall.net",
  404. "https://lemmy.digitalutopia.xyz",
  405. "https://lemmy.dingo-zebra.ts.net",
  406. "https://lemmy.discothe.quest",
  407. "https://lemmy.dnet.social",
  408. "https://lemmy.dogboy.xyz",
  409. "https://lemmy.doomeer.com",
  410. "https://lemmy.dormedas.com",
  411. "https://lemmy.dosh.dk",
  412. "https://lemmy.dropdoos.nl",
  413. "https://lemmy.ds2600.com",
  414. "https://lemmy.dudeami.win",
  415. "https://lemmy.dynatron.me",
  416. "https://lemmy.eatsleepcode.ca",
  417. "https://lemmy.ecliptik.com",
  418. "https://lemmy.eco.br",
  419. "https://lemmy.einval.net",
  420. "https://lemmy.elashri.xyz",
  421. "https://lemmy.elest.io",
  422. "https://lemmy.emerald.show",
  423. "https://lemmy.emopolarbear.com",
  424. "https://lemmy.emphisia.nl",
  425. "https://lemmy.enchanted.social",
  426. "https://lemmy.escapebigtech.info",
  427. "https://lemmy.estrogen.plus",
  428. "https://lemmy.eus",
  429. "https://lemmy.eyeofthestorm.place",
  430. "https://lemmy.fait.ch",
  431. "https://lemmy.farley.pro",
  432. "https://lemmy.fbsweb.de",
  433. "https://lemmy.fbxl.net",
  434. "https://lemmy.fdr8.us",
  435. "https://lemmy.federated.club",
  436. "https://lemmy.fedi.bub.org",
  437. "https://lemmy.fedifriends.social",
  438. "https://lemmy.fedihub.social",
  439. "https://lemmy.fedireads.com",
  440. "https://lemmy.fenbushi.site",
  441. "https://lemmy.fgilcc.eu",
  442. "https://lemmy.finance",
  443. "https://lemmy.fish",
  444. "https://lemmy.fluffyb.net",
  445. "https://lemmy.foldling.org",
  446. "https://lemmy.fornaxian.tech",
  447. "https://lemmy.forty-two.sh",
  448. "https://lemmy.fosshost.com",
  449. "https://lemmy.foxden.party",
  450. "https://lemmy.freddeliv.me",
  451. "https://lemmy.fredhs.net",
  452. "https://lemmy.friheter.com",
  453. "https://lemmy.fromshado.ws",
  454. "https://lemmy.frozeninferno.xyz",
  455. "https://lemmy.funami.tech",
  456. "https://lemmy.funkylab.xyz",
  457. "https://lemmy.fwgx.uk",
  458. "https://lemmy.fyi",
  459. "https://lemmy.g97.top",
  460. "https://lemmy.gamingnight.uy",
  461. "https://lemmy.gar.dev",
  462. "https://lemmy.gareth.computer",
  463. "https://lemmy.geekforbes.com",
  464. "https://lemmy.genkmc.de",
  465. "https://lemmy.geofox.org",
  466. "https://lemmy.getmeotter.work",
  467. "https://lemmy.giftedmc.com",
  468. "https://lemmy.gimmin.com",
  469. "https://lemmy.glasgow.social",
  470. "https://lemmy.gleeson.org",
  471. "https://lemmy.globe.pub",
  472. "https://lemmy.gmprojects.pro",
  473. "https://lemmy.graphics",
  474. "https://lemmy.graz.social",
  475. "https://lemmy.grimace.life",
  476. "https://lemmy.gross.hosting",
  477. "https://lemmy.grys.it",
  478. "https://lemmy.gsp8181.co.uk",
  479. "https://lemmy.hackerbots.net",
  480. "https://lemmy.hacktheplanet.be",
  481. "https://lemmy.haigner.me",
  482. "https://lemmy.halfbro.xyz",
  483. "https://lemmy.happyharry.org",
  484. "https://lemmy.helheim.net",
  485. "https://lemmy.helios42.de",
  486. "https://lemmy.hellwhore.com",
  487. "https://lemmy.help",
  488. "https://lemmy.heorot.uk",
  489. "https://lemmy.hobbyhorse.dev",
  490. "https://lemmy.hogru.ch",
  491. "https://lemmy.holmosapien.com",
  492. "https://lemmy.home.titey.net",
  493. "https://lemmy.hoyle.me.uk",
  494. "https://lemmy.hqueue.dev",
  495. "https://lemmy.hstx.eu",
  496. "https://lemmy.hugovr.dev",
  497. "https://lemmy.hybridsarcasm.xyz",
  498. "https://lemmy.id",
  499. "https://lemmy.imagisphe.re",
  500. "https://lemmy.inbutts.lol",
  501. "https://lemmy.insley.cloud",
  502. "https://lemmy.institute",
  503. "https://lemmy.iqecke.de",
  504. "https://lemmy.itsallbadsyntax.com",
  505. "https://lemmy.iwentto.science",
  506. "https://lemmy.iys.io",
  507. "https://lemmy.jacaranda.club",
  508. "https://lemmy.jamesj999.co.uk",
  509. "https://lemmy.jasondove.me",
  510. "https://lemmy.jasonsanta.xyz",
  511. "https://lemmy.javant.xyz",
  512. "https://lemmy.jaypg.pw",
  513. "https://lemmy.jeefy.dev",
  514. "https://lemmy.jgholistic.com",
  515. "https://lemmy.jhjacobs.nl",
  516. "https://lemmy.jigoku.us.to",
  517. "https://lemmy.jlh.name",
  518. "https://lemmy.jmatyka.com",
  519. "https://lemmy.jmtr.org",
  520. "https://lemmy.jnks.xyz",
  521. "https://lemmy.johnnei.org",
  522. "https://lemmy.johnpanos.com",
  523. "https://lemmy.johnscloud.net",
  524. "https://lemmy.jonaharagon.net",
  525. "https://lemmy.jpaulus.io",
  526. "https://lemmy.juggler.jp",
  527. "https://lemmy.kagura.eu",
  528. "https://lemmy.kaouenn-noz.fr",
  529. "https://lemmy.kaytse.fun",
  530. "https://lemmy.kde.social",
  531. "https://lemmy.kennery.com",
  532. "https://lemmy.keychat.org",
  533. "https://lemmy.khorne.me",
  534. "https://lemmy.killtime.online",
  535. "https://lemmy.kilo666.com",
  536. "https://lemmy.kizaing.ca",
  537. "https://lemmy.kmoneyserver.com",
  538. "https://lemmy.kms.onl",
  539. "https://lemmy.ko4abp.com",
  540. "https://lemmy.kodemystic.dev",
  541. "https://lemmy.korz.dev",
  542. "https://lemmy.kutara.io",
  543. "https://lemmy.kwain.net",
  544. "https://lemmy.kweb.ovh",
  545. "https://lemmy.kya.moe",
  546. "https://lemmy.l00p.org",
  547. "https://lemmy.l0nax.org",
  548. "https://lemmy.lacaveatonton.ovh",
  549. "https://lemmy.lantian.pub",
  550. "https://lemmy.latrans.cloud",
  551. "https://lemmy.lemist.de",
  552. "https://lemmy.lettucegarden.net",
  553. "https://lemmy.libreprime.io",
  554. "https://lemmy.linden.social",
  555. "https://lemmy.linuxuserspace.show",
  556. "https://lemmy.lmkra.eu",
  557. "https://lemmy.loomy.li",
  558. "https://lemmy.lt",
  559. "https://lemmy.lukeog.com",
  560. "https://lemmy.lundgrensjostrom.com",
  561. "https://lemmy.lylapol.com",
  562. "https://lemmy.m1k.cloud",
  563. "https://lemmy.macaddict89.me",
  564. "https://lemmy.magnor.ovh",
  565. "https://lemmy.majorshouse.com",
  566. "https://lemmy.maples.dev",
  567. "https://lemmy.mariusdavid.fr",
  568. "https://lemmy.marksism.space",
  569. "https://lemmy.marud.fr",
  570. "https://lemmy.masip.cat",
  571. "https://lemmy.masto.community",
  572. "https://lemmy.mats.ooo",
  573. "https://lemmy.max-p.me",
  574. "https://lemmy.mayer.rocks",
  575. "https://lemmy.mbirth.uk",
  576. "https://lemmy.mbl.social",
  577. "https://lemmy.meg.li",
  578. "https://lemmy.meissners.me",
  579. "https://lemmy.menf.in",
  580. "https://lemmy.menos.gotdns.org",
  581. "https://lemmy.meowchat.xyz",
  582. "https://lemmy.mildgrim.com",
  583. "https://lemmy.mindoki.com",
  584. "https://lemmy.minecloud.ro",
  585. "https://lemmy.minie4.de",
  586. "https://lemmy.minigubben.se",
  587. "https://lemmy.minionflo.net",
  588. "https://lemmy.minji.xyz",
  589. "https://lemmy.mkwarman.com",
  590. "https://lemmy.ml",
  591. "https://lemmy.mlaga97.space",
  592. "https://lemmy.mlsn.fr",
  593. "https://lemmy.mods4ever.com",
  594. "https://lemmy.modshiftx.com",
  595. "https://lemmy.mohammadodeh.com",
  596. "https://lemmy.mongle.us",
  597. "https://lemmy.monster",
  598. "https://lemmy.moocloud.party",
  599. "https://lemmy.moonling.nl",
  600. "https://lemmy.mooo.com",
  601. "https://lemmy.mrm.one",
  602. "https://lemmy.mrmonkey.xyz",
  603. "https://lemmy.mrrl.me",
  604. "https://lemmy.muffalings.com",
  605. "https://lemmy.multivers.cc",
  606. "https://lemmy.mws.rocks",
  607. "https://lemmy.my-box.dev",
  608. "https://lemmy.my.id",
  609. "https://lemmy.myserv.one",
  610. "https://lemmy.myspamtrap.com",
  611. "https://lemmy.nauk.io",
  612. "https://lemmy.ndlug.org",
  613. "https://lemmy.nekusoul.de",
  614. "https://lemmy.neoluxcommunications.com",
  615. "https://lemmy.nerdcore.social",
  616. "https://lemmy.nexus",
  617. "https://lemmy.nicknakin.com",
  618. "https://lemmy.nikore.net",
  619. "https://lemmy.nine-hells.net",
  620. "https://lemmy.ninja",
  621. "https://lemmy.nix-community.org",
  622. "https://lemmy.noice.social",
  623. "https://lemmy.noogs.me",
  624. "https://lemmy.nope.foo",
  625. "https://lemmy.nope.ly",
  626. "https://lemmy.nopro.be",
  627. "https://lemmy.nowsci.com",
  628. "https://lemmy.nrd.li",
  629. "https://lemmy.nrgup.net",
  630. "https://lemmy.nyc.what.if.ua",
  631. "https://lemmy.nz",
  632. "https://lemmy.obrell.se",
  633. "https://lemmy.ohaa.xyz",
  634. "https://lemmy.okla.social",
  635. "https://lemmy.oldtr.uk",
  636. "https://lemmy.omat.nl",
  637. "https://lemmy.one",
  638. "https://lemmy.onlylans.io",
  639. "https://lemmy.opensupply.space",
  640. "https://lemmy.orefice.win",
  641. "https://lemmy.org.il",
  642. "https://lemmy.otakufarms.com",
  643. "https://lemmy.oursphere.space",
  644. "https://lemmy.over-world.org",
  645. "https://lemmy.p3nguin.org",
  646. "https://lemmy.packitsolutions.net",
  647. "https://lemmy.parastor.net",
  648. "https://lemmy.pastwind.top",
  649. "https://lemmy.paulstevens.org",
  650. "https://lemmy.pcft.eu",
  651. "https://lemmy.pe1uca.dev",
  652. "https://lemmy.penguinore.net",
  653. "https://lemmy.phoenix591.com",
  654. "https://lemmy.physfluids.fr",
  655. "https://lemmy.pierre-couy.fr",
  656. "https://lemmy.pipe01.net",
  657. "https://lemmy.piperservers.net",
  658. "https://lemmy.pit.ninja",
  659. "https://lemmy.pixelcollider.net",
  660. "https://lemmy.place",
  661. "https://lemmy.plaseo.org",
  662. "https://lemmy.podycust.co.uk",
  663. "https://lemmy.potatoe.ca",
  664. "https://lemmy.poundncashdown.com",
  665. "https://lemmy.praxis.red",
  666. "https://lemmy.procrastinati.org",
  667. "https://lemmy.prograhamming.com",
  668. "https://lemmy.programmerhumor.io",
  669. "https://lemmy.pt",
  670. "https://lemmy.pub",
  671. "https://lemmy.pubsub.fun",
  672. "https://lemmy.pussthecat.org",
  673. "https://lemmy.pwarde.nl",
  674. "https://lemmy.radio",
  675. "https://lemmy.ramble.moe",
  676. "https://lemmy.rat.academy",
  677. "https://lemmy.ravc.tech",
  678. "https://lemmy.razbot.xyz",
  679. "https://lemmy.reckless.dev",
  680. "https://lemmy.remotelab.uk",
  681. "https://lemmy.remoteplay.im",
  682. "https://lemmy.rhetro.de",
  683. "https://lemmy.rhymelikedi.me",
  684. "https://lemmy.rimkus.it",
  685. "https://lemmy.robotra.sh",
  686. "https://lemmy.roembol.nl",
  687. "https://lemmy.ruhr",
  688. "https://lemmy.run",
  689. "https://lemmy.s9m.xyz",
  690. "https://lemmy.safe-internet.org",
  691. "https://lemmy.saik0.com",
  692. "https://lemmy.samad.one",
  693. "https://lemmy.sambands.net",
  694. "https://lemmy.sarcasticdeveloper.com",
  695. "https://lemmy.scam-mail.me",
  696. "https://lemmy.schlunker.com",
  697. "https://lemmy.schoenwolf-schroeder.com",
  698. "https://lemmy.schuerz.at",
  699. "https://lemmy.scootaloo.pl",
  700. "https://lemmy.scottlabs.io",
  701. "https://lemmy.sdf.org",
  702. "https://lemmy.sdfcn.org",
  703. "https://lemmy.sdfeu.org",
  704. "https://lemmy.sdfjp.org",
  705. "https://lemmy.sedimentarymountains.com",
  706. "https://lemmy.seifert.id",
  707. "https://lemmy.self-hosted.site",
  708. "https://lemmy.servarr.com",
  709. "https://lemmy.server.fifthdread.com",
  710. "https://lemmy.setzman.synology.me",
  711. "https://lemmy.sherry.moe",
  712. "https://lemmy.shihaam.me",
  713. "https://lemmy.shinomoroll.net",
  714. "https://lemmy.shiny-task.com",
  715. "https://lemmy.shtuf.eu",
  716. "https://lemmy.sietch.online",
  717. "https://lemmy.simpl.website",
  718. "https://lemmy.skoops.social",
  719. "https://lemmy.skyjake.fi",
  720. "https://lemmy.smay.dev",
  721. "https://lemmy.smeargle.fans",
  722. "https://lemmy.snoot.tube",
  723. "https://lemmy.socdojo.com",
  724. "https://lemmy.sotu.casa",
  725. "https://lemmy.spacestation14.com",
  726. "https://lemmy.srcfiles.zip",
  727. "https://lemmy.srv.eco",
  728. "https://lemmy.srv0.lol",
  729. "https://lemmy.ssba.com",
  730. "https://lemmy.st",
  731. "https://lemmy.stad.social",
  732. "https://lemmy.staphup.nl",
  733. "https://lemmy.stark-enterprise.net",
  734. "https://lemmy.starless.one",
  735. "https://lemmy.starlightkel.xyz",
  736. "https://lemmy.starmade.de",
  737. "https://lemmy.steken.xyz",
  738. "https://lemmy.stonansh.org",
  739. "https://lemmy.stuart.fun",
  740. "https://lemmy.sudoer.ch",
  741. "https://lemmy.sumuun.net",
  742. "https://lemmy.svc.vesey.tech",
  743. "https://lemmy.sveri.de",
  744. "https://lemmy.syntrofos.xyz",
  745. "https://lemmy.sysctl.io",
  746. "https://lemmy.tangresh.ch",
  747. "https://lemmy.tanktrace.de",
  748. "https://lemmy.tario.org",
  749. "https://lemmy.tarsis.org",
  750. "https://lemmy.tazy.xyz",
  751. "https://lemmy.teaisatfour.com",
  752. "https://lemmy.tealshark.net",
  753. "https://lemmy.team",
  754. "https://lemmy.tebro.fi",
  755. "https://lemmy.techhaven.io",
  756. "https://lemmy.technosorcery.net",
  757. "https://lemmy.technowizardry.net",
  758. "https://lemmy.techtailors.net",
  759. "https://lemmy.techtriage.guru",
  760. "https://lemmy.tedomum.net",
  761. "https://lemmy.telaax.com",
  762. "https://lemmy.temporus.me",
  763. "https://lemmy.tespia.org",
  764. "https://lemmy.teuto.icu",
  765. "https://lemmy.tf",
  766. "https://lemmy.tgxn.net",
  767. "https://lemmy.the-burrow.com",
  768. "https://lemmy.thebias.nl",
  769. "https://lemmy.theia.cafe",
  770. "https://lemmy.themainframe.org",
  771. "https://lemmy.thenewgaming.de",
  772. "https://lemmy.theonecurly.page",
  773. "https://lemmy.thepixelproject.com",
  774. "https://lemmy.thesanewriter.com",
  775. "https://lemmy.thias.xyz",
  776. "https://lemmy.timwaterhouse.com",
  777. "https://lemmy.titanplusplus.online",
  778. "https://lemmy.tobyvin.dev",
  779. "https://lemmy.today",
  780. "https://lemmy.toldi.eu",
  781. "https://lemmy.toot.pt",
  782. "https://lemmy.tr00st.co.uk",
  783. "https://lemmy.trevor.coffee",
  784. "https://lemmy.trippy.pizza",
  785. "https://lemmy.trizz.io",
  786. "https://lemmy.trojaner.dev",
  787. "https://lemmy.ubergeek77.chat",
  788. "https://lemmy.uhhoh.com",
  789. "https://lemmy.umainfo.live",
  790. "https://lemmy.unboiled.info",
  791. "https://lemmy.unfiltered.social",
  792. "https://lemmy.uninsane.org",
  793. "https://lemmy.unknownsys.com",
  794. "https://lemmy.unryzer.eu",
  795. "https://lemmy.urbanhost.top",
  796. "https://lemmy.utveckla.re",
  797. "https://lemmy.va-11-hall-a.cafe",
  798. "https://lemmy.vepta.org",
  799. "https://lemmy.vinodjam.com",
  800. "https://lemmy.virtim.dev",
  801. "https://lemmy.void.bh",
  802. "https://lemmy.vonbergcompany.de",
  803. "https://lemmy.vrchat-dev.tech",
  804. "https://lemmy.vyizis.tech",
  805. "https://lemmy.w9r.de",
  806. "https://lemmy.website",
  807. "https://lemmy.weomucat.com",
  808. "https://lemmy.whitedragonofcroatia.com",
  809. "https://lemmy.wizjenkins.com",
  810. "https://lemmy.works",
  811. "https://lemmy.world",
  812. "https://lemmy.wtf",
  813. "https://lemmy.x3i.tech",
  814. "https://lemmy.xcoolgroup.com",
  815. "https://lemmy.xeviousx.eu",
  816. "https://lemmy.xoynq.com",
  817. "https://lemmy.xylight.dev",
  818. "https://lemmy.yachts",
  819. "https://lemmy.z0r.co",
  820. "https://lemmy.zell-mbc.com",
  821. "https://lemmy.zhukov.al",
  822. "https://lemmy.zimage.com",
  823. "https://lemmy.zip",
  824. "https://lemmy.zwanenburg.info",
  825. "https://lemmy.zxcvn.xyz",
  826. "https://lemmy2.addictmud.org",
  827. "https://lemmyadmin.site",
  828. "https://lemmyf.uk",
  829. "https://lemmyfi.com",
  830. "https://lemmyfly.org",
  831. "https://lemmygrad.ml",
  832. "https://lemmyhub.com",
  833. "https://lemmyis.fun",
  834. "https://lemmyland.com",
  835. "https://lemmyngs.social",
  836. "https://lemmynsfw.com",
  837. "https://lemmyonline.com",
  838. "https://lemmys.hivemind.at",
  839. "https://lemmyunchained.net",
  840. "https://lemmyverse.org",
  841. "https://lemthony.com",
  842. "https://lemux.minnix.dev",
  843. "https://lemy.lol",
  844. "https://lemy.nl",
  845. "https://leuker.me",
  846. "https://lib.lgbt",
  847. "https://libreauto.app",
  848. "https://libretechni.ca",
  849. "https://liminal.southfox.me",
  850. "https://linkage.ds8.zone",
  851. "https://linkopath.com",
  852. "https://links.aa4.eu",
  853. "https://links.dartboard.social",
  854. "https://links.esq.social",
  855. "https://links.hackliberty.org",
  856. "https://links.rebel.ar",
  857. "https://links.rocks",
  858. "https://links.roobre.es",
  859. "https://linux.community",
  860. "https://linz.city",
  861. "https://literature.cafe",
  862. "https://livesound.world",
  863. "https://lm.bittervets.org",
  864. "https://lm.blythhub.com",
  865. "https://lm.boing.icu",
  866. "https://lm.byteme.social",
  867. "https://lm.gsk.moe",
  868. "https://lm.helilot.com",
  869. "https://lm.inu.is",
  870. "https://lm.korako.me",
  871. "https://lm.madiator.cloud",
  872. "https://lm.mythoranium.com",
  873. "https://lm.paradisus.day",
  874. "https://lm.put.tf",
  875. "https://lm.sethp.cc",
  876. "https://lm.suitwaffle.com",
  877. "https://lm.thesqooid.com",
  878. "https://lm.williampuckering.com",
  879. "https://lmmy.dk",
  880. "https://lmmy.io",
  881. "https://lmmy.net",
  882. "https://lmmy.tvdl.dev",
  883. "https://lmy.brx.io",
  884. "https://lmy.drundo.com.au",
  885. "https://lmy.sagf.io",
  886. "https://lmy.singinwhale.com",
  887. "https://local106.com",
  888. "https://lonestarlemmy.mooo.com",
  889. "https://lostcheese.com",
  890. "https://ls.buckodr.ink",
  891. "https://lsmu.schmurian.xyz",
  892. "https://lu.skbo.net",
  893. "https://lululemmy.com",
  894. "https://lx.pontual.social",
  895. "https://mander.xyz",
  896. "https://matejc.com",
  897. "https://matts.digital",
  898. "https://meganice.online",
  899. "https://mementomori.school",
  900. "https://merv.news",
  901. "https://metapowers.org",
  902. "https://mh.znark.us",
  903. "https://michelsup.org",
  904. "https://midwest.social",
  905. "https://milksteak.org",
  906. "https://mimiclem.me",
  907. "https://mlem.hackular.com",
  908. "https://mlem.me",
  909. "https://monero.im",
  910. "https://monero.town",
  911. "https://monyet.cc",
  912. "https://moto.teamswollen.org",
  913. "https://mtgzone.com",
  914. "https://mujico.org",
  915. "https://murffys-place.club",
  916. "https://my.lserver.dev",
  917. "https://mylemmy.win",
  918. "https://nano.garden",
  919. "https://nc.gnzl.cl",
  920. "https://netmonkey.tech",
  921. "https://news.cosocial.ca",
  922. "https://news.idlestate.org",
  923. "https://nichenerdery.duckdns.org",
  924. "https://nickbuilds.net",
  925. "https://nlemmy.nl",
  926. "https://no.lastname.nz",
  927. "https://nom.mom",
  928. "https://nopeeking.link",
  929. "https://normalcity.life",
  930. "https://notdigg.com",
  931. "https://notyour.rodeo",
  932. "https://nwdr.club",
  933. "https://occult.social",
  934. "https://odin.lanofthedead.xyz",
  935. "https://orbiting.observer",
  936. "https://orcas.enjoying.yachts",
  937. "https://ou0.de",
  938. "https://outpost.zeuslink.net",
  939. "https://overctrl.dbzer0.com",
  940. "https://parenti.sh",
  941. "https://partizle.com",
  942. "https://pasta.faith",
  943. "https://pawb.social",
  944. "https://poptalk.scrubbles.tech",
  945. "https://pornlemmy.com",
  946. "https://positroni.ddns.net",
  947. "https://possumpat.io",
  948. "https://posta.no",
  949. "https://postit.quantentoast.de",
  950. "https://precious.net",
  951. "https://preserve.games",
  952. "https://pricefield.org",
  953. "https://prime8s.xyz",
  954. "https://programming.dev",
  955. "https://proit.org",
  956. "https://psychedelia.ink",
  957. "https://purpleit.net",
  958. "https://qlemmy.com",
  959. "https://quokk.au",
  960. "https://r-sauna.fi",
  961. "https://r.irithyll.cc",
  962. "https://r.nf",
  963. "https://radiation.party",
  964. "https://raeuberwald.de",
  965. "https://random-hero.com",
  966. "https://rblind.com",
  967. "https://rc.liftoff-app.org",
  968. "https://re.tei.li",
  969. "https://real.lemmy.fan",
  970. "https://red.cyberhase.de",
  971. "https://reddit.moonbeam.town",
  972. "https://reddrefuge.com",
  973. "https://reddthat.com",
  974. "https://rekabu.ru",
  975. "https://rentadrunk.org",
  976. "https://reseed.it",
  977. "https://retarded.dev",
  978. "https://retrolemmy.com",
  979. "https://ripo.st",
  980. "https://rollenspiel.forum",
  981. "https://rqd2.net",
  982. "https://rss.confusedoncoffee.com",
  983. "https://s.jape.work",
  984. "https://sammich.es",
  985. "https://scooby.one",
  986. "https://seemel.ink",
  987. "https://selfhosted.forum",
  988. "https://septic.win",
  989. "https://seriously.iamincredibly.gay",
  990. "https://sffa.community",
  991. "https://sh.itjust.works",
  992. "https://sha1.nl",
  993. "https://shi.tmight.work",
  994. "https://shinobu.cloud",
  995. "https://showeq.com",
  996. "https://silkky.pub",
  997. "https://slangenettet.pyjam.as",
  998. "https://slrpnk.net",
  999. "https://snake.substantialplumbing.repair",
  1000. "https://snowstorm.online",
  1001. "https://snuv.win",
  1002. "https://soc.ebmn.io",
  1003. "https://soccer.forum",
  1004. "https://social.bug.expert",
  1005. "https://social.coalition.space",
  1006. "https://social.cyb3r.dog",
  1007. "https://social.fr4me.io",
  1008. "https://social.ggbox.fr",
  1009. "https://social.jears.at",
  1010. "https://social.kennypu.com",
  1011. "https://social.nerdhouse.io",
  1012. "https://social.packetloss.gg",
  1013. "https://social.poisson.me",
  1014. "https://social.rocketsfall.net",
  1015. "https://social.venith.net",
  1016. "https://some.institute",
  1017. "https://sopuli.xyz",
  1018. "https://spgrn.com",
  1019. "https://spudwart.com",
  1020. "https://stable.liftoff-app.org",
  1021. "https://stammtisch.hallertau.social",
  1022. "https://startrek.website",
  1023. "https://sub.wetshaving.social",
  1024. "https://subsubd.com",
  1025. "https://superdark.social",
  1026. "https://supernova.place",
  1027. "https://suppo.fi",
  1028. "https://support.futbol",
  1029. "https://surom.de",
  1030. "https://swg-empire.de",
  1031. "https://switter.su",
  1032. "https://szmer.info",
  1033. "https://t.bobamilktea.xyz",
  1034. "https://tabletop.place",
  1035. "https://tacobu.de",
  1036. "https://talk.kururin.tech",
  1037. "https://talk.macstack.net",
  1038. "https://talk.neovibe.app",
  1039. "https://talka.live",
  1040. "https://techy.news",
  1041. "https://textandmetal.com",
  1042. "https://the.unknowing.dance",
  1043. "https://theculture.social",
  1044. "https://thelemmy.club",
  1045. "https://theotter.social",
  1046. "https://thesidewalkends.io",
  1047. "https://thevapor.space",
  1048. "https://threads.ruin.io",
  1049. "https://tiny.lizard.supply",
  1050. "https://tkohhh.social",
  1051. "https://toast.ooo",
  1052. "https://toons.zone",
  1053. "https://tortoisewrath.com",
  1054. "https://tsck.org",
  1055. "https://ttrpg.network",
  1056. "https://tucson.social",
  1057. "https://twisti.ca",
  1058. "https://unreachable.cloud",
  1059. "https://upvote.au",
  1060. "https://usenet.lol",
  1061. "https://va11halla.bar",
  1062. "https://vastactive.online",
  1063. "https://victoriagaming.ca",
  1064. "https://voltage.vn",
  1065. "https://voxpop.social",
  1066. "https://voyager.lemmy.ml",
  1067. "https://werm.social",
  1068. "https://whemic.xyz",
  1069. "https://whiskers.bim.boats",
  1070. "https://wilbo.tech",
  1071. "https://wired.bluemarch.art",
  1072. "https://withachanceof.com",
  1073. "https://x69.org",
  1074. "https://xbdv.com",
  1075. "https://xffxe4.lol",
  1076. "https://xrs.cx",
  1077. "https://xyz.higurashi.xyz",
  1078. "https://yah.lol",
  1079. "https://yall.theatl.social",
  1080. "https://yamasaur.com",
  1081. "https://yiffit.net",
  1082. "https://ythreektech.com",
  1083. "https://yukistorm.com",
  1084. "https://zemmy.cc",
  1085. "https://zen.lema.cl",
  1086. "https://zerobytes.monster",
  1087. "https://zoo.splitlinux.org",
  1088. "https://zorg.social"
  1089. ]);
  1090. var INSTANCES_KBIN = /* @__PURE__ */ new Set([
  1091. "https://artemis.camp",
  1092. "https://atakbin.spacehost.dev",
  1093. "https://bin.pol.social",
  1094. "https://community.yshi.org",
  1095. "https://dgngrnder.com",
  1096. "https://feddit.online",
  1097. "https://fedinews.net",
  1098. "https://fusionpatrol.social",
  1099. "https://hotaudiofiction.social",
  1100. "https://jlailu.social",
  1101. "https://k.fe.derate.me",
  1102. "https://karab.in",
  1103. "https://kayb.ee",
  1104. "https://kbin-u3.vm.elestio.app",
  1105. "https://kbin.bitgoblin.tech",
  1106. "https://kbin.cafe",
  1107. "https://kbin.chat",
  1108. "https://kbin.ectolab.net",
  1109. "https://kbin.fedi.cr",
  1110. "https://kbin.life",
  1111. "https://kbin.nz",
  1112. "https://kbin.pieho.me",
  1113. "https://kbin.pithyphrase.net",
  1114. "https://kbin.projectsegfau.lt",
  1115. "https://kbin.social",
  1116. "https://kbin.tenkuu.social",
  1117. "https://kbin.thicknahalf.com",
  1118. "https://kbin.wangwood.house",
  1119. "https://kglitch.social",
  1120. "https://kopnij.in",
  1121. "https://kx.pontual.social",
  1122. "https://link.fossdle.org",
  1123. "https://longley.ws",
  1124. "https://nadajnik.org",
  1125. "https://polesie.pol.social",
  1126. "https://rainy.place",
  1127. "https://social.tath.link",
  1128. "https://supermeter.social",
  1129. "https://teacup.social",
  1130. "https://wiku.hu"
  1131. ]);
  1132. trace(`Define instances sets end`);
  1133.  
  1134. // src/our-changes.js
  1135. var OUR_CHANGES = { addedNodes: {} };
  1136. function getAddedNodesSelectors() {
  1137. return Object.values(OUR_CHANGES.addedNodes);
  1138. }
  1139. function registerAddedNode(id, selector) {
  1140. OUR_CHANGES.addedNodes[id] = selector;
  1141. }
  1142.  
  1143. // src/constants.js
  1144. var constants_default = {
  1145. ICON_CLASS: withNS(`icon`),
  1146. ICON_LOADING_CLASS: withNS(`loading`),
  1147. ICON_STYLES_ID: withNS(`icon-styles`),
  1148. ICON_LINK_CLASS: withNS(`icon-link`),
  1149. ICON_LINK_SYMBOL_ID: withNS(`icon-link-symbol`),
  1150. ICON_SPINNER_CLASS: withNS(`icon-spinner`),
  1151. ICON_SPINNER_SYMBOL_ID: withNS(`icon-spinner-symbol`),
  1152. ORIGINAL_LINK_CLASS: withNS(`original-link`),
  1153. SHOW_AT_HOME_BUTTON_CLASS: withNS(`show-at-home`),
  1154. ICON_SVG_TEMPLATE_ID: withNS(`icon-template`),
  1155. AUTH_WRONG: `AUTH_WRONG`,
  1156. AUTH_MISSING: `AUTH_MISSING`,
  1157. REWRITE_STATUS: withNSCamelCase(`localUrlStatus`),
  1158. REWRITE_STATUS_PENDING: `pending`,
  1159. REWRITE_STATUS_SUCCESS: `success`,
  1160. REWRITE_STATUS_ERROR: `error`,
  1161. REWRITE_STATUS_UNRESOLVED: `unresolved`,
  1162. SETUP_AUTH_MESSAGE: `Lemmy Universal Link Switcher: Visit your home instance once to set up post/comment rewriting`,
  1163. SETTINGS_BUTTON_ID: withNS(`open-settings-button`),
  1164. SETTINGS_MENU_ID: withNS(`settings`),
  1165. SETTINGS_STYLES_ID: withNS(`settings-styles`)
  1166. };
  1167. function withNS(identifier) {
  1168. return `lemmy-rewrite-urls-` + identifier;
  1169. }
  1170. function withNSCamelCase(identifier) {
  1171. return `lemmyRewriteUrls` + identifier.charAt(0).toUpperCase() + identifier.slice(1);
  1172. }
  1173.  
  1174. // src/rewriting/helpers.js
  1175. function isHashLink(link) {
  1176. return link.hash && link.origin + link.pathname + link.search === location.origin + location.pathname + location.search;
  1177. }
  1178. function isSamePage(url1, url2) {
  1179. return url1.host === url2.host && url1.pathname === url2.pathname;
  1180. }
  1181. function isV17() {
  1182. return window.isoData?.site_res?.version.startsWith(`0.17`);
  1183. }
  1184. var stopEventHandler = (event) => {
  1185. event.preventDefault();
  1186. event.stopPropagation();
  1187. };
  1188.  
  1189. // src/gm.js
  1190. async function setValue(key, value) {
  1191. trace(`GM.setValue key ${key}, value ${value}`);
  1192. await GM.setValue(key, value);
  1193. }
  1194. async function getValue(key) {
  1195. return await GM.getValue(key);
  1196. }
  1197. function parseResponse(response) {
  1198. try {
  1199. return JSON.parse(response.response);
  1200. } catch (e) {
  1201. debug(`Error parsing response JSON`, e);
  1202. return response.response;
  1203. }
  1204. }
  1205. function logRequest(response, data) {
  1206. trace(
  1207. `FinalUrl`,
  1208. response.finalUrl,
  1209. `status`,
  1210. response.status,
  1211. `text`,
  1212. response.statusText,
  1213. `response`,
  1214. data || response.response
  1215. );
  1216. trace(`responseHeaders`, response.responseHeaders);
  1217. }
  1218. function performXmlHttpRequest(url, doneCallback) {
  1219. GM.xmlHttpRequest({
  1220. url,
  1221. onloadend: (response) => {
  1222. const data = parseResponse(response);
  1223. logRequest(response, data);
  1224. doneCallback(response, data);
  1225. }
  1226. });
  1227. }
  1228. function registerMenuCommand(name, onClick) {
  1229. GM.registerMenuCommand(name, onClick);
  1230. }
  1231.  
  1232. // src/rewriting/auth.js
  1233. var AUTH;
  1234. function getAuthFromCookie() {
  1235. return document.cookie.split("; ").find((row) => row.startsWith("jwt="))?.split("=")[1];
  1236. }
  1237. async function setAuth(auth) {
  1238. AUTH = auth;
  1239. await setValue(`auth`, auth);
  1240. }
  1241. async function initAuth() {
  1242. const curAuth = await getAuth();
  1243. if (curAuth) {
  1244. AUTH = curAuth;
  1245. return;
  1246. }
  1247. if (location.origin === HOME) {
  1248. const newAuth = getAuthFromCookie();
  1249. await setAuth(newAuth);
  1250. if (newAuth && await getValue(`authNoticeShown`)) {
  1251. alert(`Lemmy Universal Link Switcher: Post/comment rewriting has been set up successfully`);
  1252. await setValue(`authNoticeShown`, null);
  1253. }
  1254. } else if (HOME && !await getValue(`authNoticeShown`)) {
  1255. await setValue(`authNoticeShown`, `true`);
  1256. alert(constants_default.SETUP_AUTH_MESSAGE);
  1257. }
  1258. }
  1259. function updateAuthPeriodically() {
  1260. setInterval(async () => {
  1261. const prev = AUTH;
  1262. const newAuth = location.origin === HOME ? getAuthFromCookie() : await getAuth();
  1263. if (prev !== newAuth) {
  1264. debug(`Auth changed`);
  1265. await setAuth(newAuth);
  1266. clearMissingUrlsInCache();
  1267. triggerRewrite();
  1268. }
  1269. }, 1234);
  1270. }
  1271. async function getAuth() {
  1272. return await getValue(`auth`);
  1273. }
  1274.  
  1275. // src/rewriting/url-mapping.js
  1276. function splitPaths(url) {
  1277. return url.pathname.split(`/`).slice(1);
  1278. }
  1279. function isRemoteLemmyUrl(url) {
  1280. return !isHomeInstance(url) && isLemmyInstance(url);
  1281. }
  1282. function isRemoteKbinUrl(url) {
  1283. return !isHomeInstance(url) && isKbinInstance(url);
  1284. }
  1285. function isRemoteUrl(url) {
  1286. return isRemoteLemmyUrl(url) || isRemoteKbinUrl(url);
  1287. }
  1288. function findLocalUrlForStandardAtFormat(url, rootPath) {
  1289. const paths = splitPaths(url);
  1290. const name = paths[1].includes(`@`) ? paths[1] : paths[1] + `@` + url.host;
  1291. return `${HOME}/${rootPath || paths[0]}/${name}` + url.search + url.hash;
  1292. }
  1293. function findLocalUrlForLemmyUrl(url) {
  1294. if (isLemmyUserOrCommunityUrl(url)) {
  1295. return findLocalUrlForStandardAtFormat(url);
  1296. } else {
  1297. return null;
  1298. }
  1299. }
  1300. function findLocalUrlForKbinUserUrl(url) {
  1301. const paths = splitPaths(url);
  1302. const user = paths[1].startsWith(`@`) ? paths[1].substring(1) : paths[1];
  1303. const name = user.includes(`@`) ? user : user + `@` + url.host;
  1304. return `${HOME}/u/${name}` + url.search + url.hash;
  1305. }
  1306. function findLocalUrlForKbinUrl(url) {
  1307. if (isKbinMagazineUrl(url)) {
  1308. return findLocalUrlForStandardAtFormat(url, mappedKbinRootPath(url));
  1309. } else if (isKbinUserUrl(url)) {
  1310. return findLocalUrlForKbinUserUrl(url);
  1311. } else {
  1312. return null;
  1313. }
  1314. }
  1315. function findLocalUrl(url) {
  1316. if (isRemoteLemmyUrl(url))
  1317. return findLocalUrlForLemmyUrl(url);
  1318. if (isRemoteKbinUrl(url))
  1319. return findLocalUrlForKbinUrl(url);
  1320. return null;
  1321. }
  1322. async function fetchApIdFromRemote(url) {
  1323. const endpoint = isLemmyPostUrl(url) ? `post` : `comment`;
  1324. const paths = splitPaths(url);
  1325. const id = paths[1];
  1326. return new Promise((resolve, reject) => {
  1327. performXmlHttpRequest(`${url.origin}/api/v3/${endpoint}?id=${id}`, (response, data) => {
  1328. const apId = data[`${endpoint}_view`]?.[endpoint]?.ap_id;
  1329. if (response.status === 200 && apId) {
  1330. resolve(apId);
  1331. } else {
  1332. handleFailedRequest(`fetching AP ID`, response, reject);
  1333. }
  1334. });
  1335. });
  1336. }
  1337. function handleFailedRequest(requestName, response, reject) {
  1338. if (response.status >= 200 && response.status <= 299) {
  1339. reject(`${requestName}: Unhandled successful response, status: ${response.status}`);
  1340. } else if (response.status >= 400 && response.status <= 599) {
  1341. reject(`${requestName}: Error, status: ${response.status}`);
  1342. } else {
  1343. reject(`${requestName}: Something weird happened, status: ${response.status}`);
  1344. }
  1345. }
  1346. async function resolveObjectFromHome(url) {
  1347. return new Promise(async (resolve, reject) => {
  1348. const auth = await getAuth();
  1349. if (!auth) {
  1350. debug(`No auth token found`);
  1351. reject(constants_default.AUTH_MISSING);
  1352. }
  1353. performXmlHttpRequest(
  1354. `${HOME}/api/v3/resolve_object?auth=${auth}&q=${encodeURIComponent(url.href)}`,
  1355. (response, data) => {
  1356. if (response.status === 200 && data.post?.post?.id) {
  1357. resolve(`${HOME}/post/${data.post.post.id}${url.search}${url.hash}`);
  1358. } else if (response.status === 200 && data.comment?.comment?.id) {
  1359. resolve(`${HOME}/comment/${data.comment.comment.id}${url.search}${url.hash}`);
  1360. } else if (response.status === 400 && data?.error === `couldnt_find_object`) {
  1361. resolve(null);
  1362. } else if (response.status === 400 && data?.error === `not_logged_in`) {
  1363. reject(constants_default.AUTH_WRONG);
  1364. } else {
  1365. handleFailedRequest(`resolving object`, response, reject);
  1366. }
  1367. }
  1368. );
  1369. });
  1370. }
  1371. var urlCache = {};
  1372. function clearMissingUrlsInCache() {
  1373. for (const value of Object.values(urlCache)) {
  1374. if (value.error)
  1375. delete value.error;
  1376. if (value.localUrl === null)
  1377. delete value.localUrl;
  1378. }
  1379. }
  1380. function getCacheKey(url) {
  1381. return url.host + url.pathname + url.search;
  1382. }
  1383. function cacheResult(url, localUrl) {
  1384. const key = getCacheKey(url);
  1385. if (!urlCache[key]) {
  1386. urlCache[key] = {};
  1387. }
  1388. if (urlCache[key].error)
  1389. delete urlCache[key].error;
  1390. urlCache[key].localUrl = localUrl;
  1391. return localUrl;
  1392. }
  1393. function cacheErrorResult(url, error) {
  1394. const key = getCacheKey(url);
  1395. if (!urlCache[key]) {
  1396. urlCache[key] = {};
  1397. }
  1398. urlCache[key].error = error;
  1399. }
  1400. function getLocalUrlfromCache(url) {
  1401. const key = getCacheKey(url);
  1402. if (urlCache[key]?.error) {
  1403. throw urlCache[key]?.error;
  1404. } else {
  1405. return urlCache[key]?.localUrl;
  1406. }
  1407. }
  1408. async function fetchLocalUrl(url, loadFromCache = true) {
  1409. if (loadFromCache) {
  1410. const cached = getLocalUrlfromCache(url);
  1411. if (cached !== void 0) {
  1412. trace(`Found URL ${url} in cache: ${cached}`);
  1413. return cached;
  1414. }
  1415. }
  1416. try {
  1417. return cacheResult(url, await fetchLocalUrlNoCache(url));
  1418. } catch (e) {
  1419. debug(`fetchLocalUrl error`, e);
  1420. cacheErrorResult(url, e);
  1421. throw e;
  1422. }
  1423. }
  1424. async function fetchLocalUrlNoCache(url) {
  1425. trace(`Trying to resolve URL ${url} directly`);
  1426. const localUrl = await resolveObjectFromHome(url);
  1427. if (localUrl !== null) {
  1428. return localUrl;
  1429. } else {
  1430. trace(`Did not find URL ${url} directly`);
  1431. }
  1432. const apId = new URL(await fetchApIdFromRemote(url));
  1433. trace(`Found AP ID for URL ${url}: ${apId}`);
  1434. if (!apId.search) {
  1435. apId.search = url.search;
  1436. }
  1437. if (!apId.hash) {
  1438. apId.hash = url.hash;
  1439. }
  1440. if (isSamePage(url, apId)) {
  1441. trace(`Previous URL was AP ID already, URL not federated for some reason`);
  1442. return null;
  1443. } else {
  1444. return await resolveObjectFromHome(apId);
  1445. }
  1446. }
  1447. function isInstantlyRewritable(url) {
  1448. return isRemoteLemmyUrl(url) && isLemmyUserOrCommunityUrl(url) || isRemoteKbinUrl(url) && (isKbinMagazineUrl(url) || isKbinUserUrl(url));
  1449. }
  1450. function isRewritableAfterResolving(url) {
  1451. return isRemoteLemmyUrl(url) && (isLemmyPostUrl(url) || isLemmyCommentUrl(url));
  1452. }
  1453. function isLemmyPostUrl(url) {
  1454. const paths = splitPaths(url);
  1455. return paths[0] === `post`;
  1456. }
  1457. function isLemmyCommentUrl(url) {
  1458. const paths = splitPaths(url);
  1459. return paths[0] === `comment`;
  1460. }
  1461. function isLemmyUserOrCommunityUrl(url) {
  1462. const paths = splitPaths(url);
  1463. return paths[0] === `c` || paths[0] === `u`;
  1464. }
  1465. function isKbinPostUrl(url) {
  1466. const paths = splitPaths(url);
  1467. return paths[0] === `m` && paths.length > 2 && paths[2] === `t`;
  1468. }
  1469. function isKbinMicroblogUrl(url) {
  1470. const paths = splitPaths(url);
  1471. return paths[0] === `m` && paths.length > 2 && paths[2] === `p`;
  1472. }
  1473. function isKbinMicroblogOverviewUrl(url) {
  1474. const paths = splitPaths(url);
  1475. return paths[0] === `m` && paths.length > 2 && paths[2] === `microblog`;
  1476. }
  1477. function isKbinMagazinePeopleUrl(url) {
  1478. const paths = splitPaths(url);
  1479. return paths[0] === `m` && paths.length > 2 && paths[2] === `people`;
  1480. }
  1481. function isKbinMagazineUrl(url) {
  1482. const paths = splitPaths(url);
  1483. return paths[0] === `m` && !isKbinPostUrl(url) && !isKbinMagazinePeopleUrl(url) && !isKbinMicroblogUrl(url) && !isKbinMicroblogOverviewUrl(url);
  1484. }
  1485. function mappedKbinRootPath(url) {
  1486. const paths = splitPaths(url);
  1487. if (paths[0] === `m`) {
  1488. return `c`;
  1489. } else {
  1490. return null;
  1491. }
  1492. }
  1493. function isKbinUserUrl(url) {
  1494. const paths = splitPaths(url);
  1495. return paths.length === 2 && paths[0] === `u`;
  1496. }
  1497.  
  1498. // src/rewriting/links/icon.js
  1499. function getIcon(link) {
  1500. return link.querySelector(`.` + constants_default.ICON_CLASS);
  1501. }
  1502. function createIcon(link) {
  1503. ensureTemplateAvailable();
  1504. ensureIconStylesAdded();
  1505. const wrapper = document.createElement(`span`);
  1506. registerAddedNode(constants_default.ICON_CLASS, `.` + constants_default.ICON_CLASS);
  1507. wrapper.classList.add(constants_default.ICON_CLASS);
  1508. if (link.children.length === 0 || getComputedStyle(link.children[link.children.length - 1]).marginRight === `0px`) {
  1509. wrapper.style.marginLeft = `0.5em`;
  1510. }
  1511. const linkIcon = createSVG();
  1512. linkIcon.classList.add(constants_default.ICON_LINK_CLASS);
  1513. linkIcon.innerHTML = `<use href=#${constants_default.ICON_LINK_SYMBOL_ID} />`;
  1514. wrapper.append(linkIcon);
  1515. const spinnerIcon = createSVG();
  1516. spinnerIcon.classList.add(constants_default.ICON_SPINNER_CLASS);
  1517. spinnerIcon.innerHTML = `<use href=#${constants_default.ICON_SPINNER_SYMBOL_ID} />`;
  1518. wrapper.append(spinnerIcon);
  1519. return wrapper;
  1520. }
  1521. function createSVG() {
  1522. return document.createElementNS(`http://www.w3.org/2000/svg`, `svg`);
  1523. }
  1524. function ensureTemplateAvailable() {
  1525. if (document.querySelector(`#` + constants_default.ICON_SVG_TEMPLATE_ID))
  1526. return;
  1527. const template = createSVG();
  1528. template.id = constants_default.ICON_SVG_TEMPLATE_ID;
  1529. template.innerHTML = `<defs>
  1530. <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>
  1531. <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>
  1532. </defs>`;
  1533. registerAddedNode(constants_default.ICON_SVG_TEMPLATE_ID, `#` + constants_default.ICON_SVG_TEMPLATE_ID);
  1534. document.head.append(template);
  1535. }
  1536. function ensureIconStylesAdded() {
  1537. if (document.querySelector(`#` + constants_default.ICON_STYLES_ID))
  1538. return;
  1539. const style = document.createElement(`style`);
  1540. style.id = constants_default.ICON_STYLES_ID;
  1541. style.innerHTML = `
  1542. .${constants_default.ICON_SPINNER_CLASS} {
  1543. display: none;
  1544. animation: spins 2s linear infinite;
  1545. }
  1546. .${constants_default.ICON_LINK_CLASS} {
  1547. display: inline-block;
  1548. }
  1549. .${constants_default.ICON_LOADING_CLASS} > .${constants_default.ICON_LINK_CLASS} {
  1550. display: none;
  1551. }
  1552. .${constants_default.ICON_LOADING_CLASS} > .${constants_default.ICON_SPINNER_CLASS} {
  1553. display: inline-block;
  1554. }
  1555. .${constants_default.ICON_CLASS} > svg {
  1556. vertical-align: sub;
  1557. height: 1em; width: 1em;
  1558. stroke: currentColor;
  1559. fill: currentColor;
  1560. }`;
  1561. registerAddedNode(constants_default.ICON_STYLES_ID, `#` + constants_default.ICON_STYLES_ID);
  1562. document.head.append(style);
  1563. }
  1564.  
  1565. // src/tippy.js
  1566. var tippy_default = window.tippy;
  1567.  
  1568. // src/rewriting/links/tooltip.js
  1569. function getOriginalLinkHtml(originalHref) {
  1570. registerAddedNode(constants_default.ORIGINAL_LINK_CLASS, `.` + constants_default.ORIGINAL_LINK_CLASS);
  1571. return `Original link: <a class="${constants_default.ORIGINAL_LINK_CLASS}" href="${originalHref}">${originalHref}</a>`;
  1572. }
  1573. function defaultOptions(link) {
  1574. return {
  1575. appendTo: () => link.parentNode,
  1576. allowHTML: true,
  1577. interactive: true,
  1578. animation: false,
  1579. placement: "bottom",
  1580. hideOnClick: false
  1581. };
  1582. }
  1583. function createOriginalLinkTooltip(link, originalHref) {
  1584. trace(`Create original link tooltip`, link, originalHref);
  1585. getIcon(link).addEventListener(`click`, stopEventHandler);
  1586. return createLinkTooltip(link, getOriginalLinkHtml(originalHref));
  1587. }
  1588. function createLinkTooltip(link, content) {
  1589. return tippy_default(getIcon(link), {
  1590. ...defaultOptions(link),
  1591. content
  1592. });
  1593. }
  1594. function createLinkLoadTooltip(link) {
  1595. trace(`Create link load tooltip`, link);
  1596. getIcon(link).classList.add(constants_default.ICON_LOADING_CLASS);
  1597. return createLinkTooltip(link, `Loading home URL...<br />Don't want to wait? ${getOriginalLinkHtml(link.href)}`);
  1598. }
  1599. function linkLoadTooltipSuccess(tooltip, originalHref) {
  1600. linkLoadResult(tooltip, `\u2714\uFE0F Changed link to home instance`, getOriginalLinkHtml(originalHref));
  1601. }
  1602. function linkLoadTooltipError(tooltip, error) {
  1603. linkLoadResult(tooltip, `\u274C ` + error);
  1604. }
  1605. function linkLoadResult(tooltip, result, finalContent = result) {
  1606. const icon = tooltip.reference;
  1607. icon.classList.remove(constants_default.ICON_LOADING_CLASS);
  1608. icon.addEventListener(`click`, stopEventHandler);
  1609. if (tooltip.state.isVisible) {
  1610. tooltip.setContent(result);
  1611. setTimeout(() => {
  1612. tooltip.hide();
  1613. tooltip.setContent(finalContent);
  1614. }, 2e3);
  1615. } else {
  1616. tooltip.setContent(finalContent);
  1617. }
  1618. }
  1619.  
  1620. // src/rewriting/links/links.js
  1621. function changeLinkHref(link, localUrl) {
  1622. const treeWalker = document.createTreeWalker(link, NodeFilter.SHOW_TEXT, (node) => {
  1623. if (node.textContent.toLowerCase().trim() === link.href.toLowerCase().trim()) {
  1624. return NodeFilter.FILTER_ACCEPT;
  1625. } else {
  1626. return NodeFilter.FILTER_SKIP;
  1627. }
  1628. });
  1629. let textNode;
  1630. while ((textNode = treeWalker.nextNode()) !== null) {
  1631. textNode.textContent = localUrl;
  1632. }
  1633. link.href = localUrl;
  1634. link.addEventListener(`click`, (event) => {
  1635. if (event.button === 0 && !event.ctrlKey && !event.metaKey && link.target !== `_blank`) {
  1636. location.href = localUrl;
  1637. }
  1638. });
  1639. }
  1640. function appendIconTo(elem, icon) {
  1641. if (elem.children.length === 0 || getComputedStyle(elem.children[elem.children.length - 1]).display !== `inline-block`) {
  1642. elem.append(icon);
  1643. } else {
  1644. appendIconTo(elem.children[elem.children.length - 1], icon);
  1645. }
  1646. }
  1647. function addFetchLocalUrlHandler(link) {
  1648. let tooltip;
  1649. const handler = async (event) => {
  1650. if (event.type === `click`) {
  1651. stopEventHandler(event);
  1652. if (tooltip)
  1653. tooltip.show();
  1654. return;
  1655. }
  1656. link.removeEventListener(`focus`, handler);
  1657. link.removeEventListener(`mouseenter`, handler);
  1658. if (link.dataset[constants_default.REWRITE_STATUS] === constants_default.REWRITE_STATUS_PENDING)
  1659. return;
  1660. link.dataset[constants_default.REWRITE_STATUS] = constants_default.REWRITE_STATUS_PENDING;
  1661. tooltip = createLinkLoadTooltip(link);
  1662. try {
  1663. const localUrl = await fetchLocalUrl(link);
  1664. if (!localUrl) {
  1665. debug(`Local URL for ${link.href} could not be found`);
  1666. linkLoadTooltipError(tooltip, `Home URL could not be found`);
  1667. return;
  1668. }
  1669. trace(`Local URL for ${link.href} found: ${localUrl}`);
  1670. const oldHref = link.href;
  1671. changeLinkHref(link, localUrl);
  1672. linkLoadTooltipSuccess(tooltip, oldHref);
  1673. link.dataset[constants_default.REWRITE_STATUS] = constants_default.REWRITE_STATUS_SUCCESS;
  1674. } catch (e) {
  1675. debug(`Error while trying to resolve local URL`, e);
  1676. let msg;
  1677. if (e === constants_default.AUTH_WRONG) {
  1678. msg = `Saved login expired. Return to your home instance and log in again.`;
  1679. } else if (e === constants_default.AUTH_MISSING) {
  1680. msg = constants_default.SETUP_AUTH_MESSAGE;
  1681. } else {
  1682. msg = `Error while trying to find home URL`;
  1683. }
  1684. linkLoadTooltipError(tooltip, msg);
  1685. link.dataset[constants_default.REWRITE_STATUS] = constants_default.REWRITE_STATUS_ERROR;
  1686. } finally {
  1687. link.removeEventListener(`click`, handler);
  1688. }
  1689. };
  1690. link.addEventListener(`click`, handler);
  1691. link.addEventListener(`focus`, handler);
  1692. link.addEventListener(`mouseenter`, handler);
  1693. }
  1694. function isFediverseLink(link) {
  1695. const svg = link.querySelector(`svg`);
  1696. if (!svg)
  1697. return false;
  1698. if (svg.children.length === 0)
  1699. return false;
  1700. return svg.children[0].getAttribute(`xlink:href`)?.includes(`#icon-fedilink`);
  1701. }
  1702. function rewriteToLocal(link) {
  1703. if (!link.parentNode)
  1704. return false;
  1705. if (link.classList.contains(constants_default.ORIGINAL_LINK_CLASS))
  1706. return false;
  1707. if (link.dataset[constants_default.REWRITE_STATUS] === constants_default.REWRITE_STATUS_SUCCESS)
  1708. return false;
  1709. if (isHashLink(link))
  1710. return false;
  1711. if (!isRemoteUrl(link))
  1712. return false;
  1713. if (isFediverseLink(link))
  1714. return false;
  1715. if (isInstantlyRewritable(link)) {
  1716. const localUrl = findLocalUrl(link);
  1717. if (!localUrl)
  1718. return false;
  1719. if (isSamePage(new URL(localUrl), location))
  1720. return false;
  1721. const oldHref = link.href;
  1722. changeLinkHref(link, localUrl);
  1723. const icon = createIcon(link);
  1724. appendIconTo(link, icon);
  1725. createOriginalLinkTooltip(link, oldHref);
  1726. link.dataset[constants_default.REWRITE_STATUS] = constants_default.REWRITE_STATUS_SUCCESS;
  1727. trace(`Rewrite link`, link, ` from`, oldHref, `to`, localUrl);
  1728. return true;
  1729. } else if (isRewritableAfterResolving(link)) {
  1730. if (!getIcon(link)) {
  1731. appendIconTo(link, createIcon(link));
  1732. }
  1733. if (!link.dataset[constants_default.REWRITE_STATUS]) {
  1734. link.dataset[constants_default.REWRITE_STATUS] = constants_default.REWRITE_STATUS_UNRESOLVED;
  1735. addFetchLocalUrlHandler(link);
  1736. }
  1737. }
  1738. }
  1739. function findLinksInChange(change) {
  1740. if (change.type === `childList`) {
  1741. const links = Array.from(change.addedNodes).flatMap((addedNode) => {
  1742. if (addedNode.tagName?.toLowerCase() === `a`) {
  1743. return addedNode;
  1744. } else if (addedNode.querySelectorAll) {
  1745. return Array.from(addedNode.querySelectorAll(`a`));
  1746. } else {
  1747. return [];
  1748. }
  1749. });
  1750. if (links.length > 0)
  1751. trace(`Change`, change, `contained the links`, links);
  1752. return links;
  1753. } else if (change.type === `attributes`) {
  1754. return change.target.matches?.(`a`) ? change.target : [];
  1755. } else {
  1756. return [];
  1757. }
  1758. }
  1759. function findLinksToRewrite(changes) {
  1760. if (!changes) {
  1761. return document.querySelectorAll(`a`);
  1762. }
  1763. return changes.flatMap(findLinksInChange);
  1764. }
  1765. async function rewriteLinksToLocal(changes) {
  1766. const links = findLinksToRewrite(changes);
  1767. const chunkSize = 50;
  1768. return await async function processChunk(currentChunk) {
  1769. const startIdx = currentChunk * chunkSize;
  1770. const endChunkIdx = (currentChunk + 1) * chunkSize;
  1771. const endIdx = Math.min(links.length, endChunkIdx);
  1772. debug(
  1773. `Processing ${links.length} links, current chunk `,
  1774. currentChunk,
  1775. `processing links ${startIdx} to ${endIdx}`
  1776. );
  1777. let anyRewritten = false;
  1778. for (let i = startIdx; i < endIdx; ++i) {
  1779. const rewritten = rewriteToLocal(links[i]);
  1780. anyRewritten = anyRewritten || rewritten;
  1781. }
  1782. debug(`Processed links ${startIdx} to ${endIdx}`);
  1783. if (endChunkIdx >= links.length) {
  1784. return anyRewritten;
  1785. }
  1786. const chunkResult = await new Promise((resolve) => setTimeout(async () => {
  1787. resolve(await processChunk(currentChunk + 1));
  1788. }, 0));
  1789. return anyRewritten || chunkResult;
  1790. }(0);
  1791. }
  1792.  
  1793. // src/settings.js
  1794. var refreshMethods = [];
  1795. async function refresh() {
  1796. for (const refreshMethod of refreshMethods) {
  1797. await refreshMethod();
  1798. }
  1799. }
  1800. function closeSettings() {
  1801. document.querySelector(`#` + constants_default.SETTINGS_MENU_ID)?.remove();
  1802. refreshMethods = [];
  1803. }
  1804. function initSettings() {
  1805. registerMenuCommand(`Open Settings`, showSettings);
  1806. }
  1807. function styles(elem, style) {
  1808. for (const [prop, val] of Object.entries(style)) {
  1809. elem.style[prop] = val;
  1810. }
  1811. }
  1812. var defaultBorder = `1px solid #AAA`;
  1813. function createButton(content, options = {}) {
  1814. const button = document.createElement(`div`);
  1815. button.innerHTML = content;
  1816. styles(button, {
  1817. display: options.inline ? `inline-block` : `block`,
  1818. backgroundColor: `#666`,
  1819. textAlign: `center`,
  1820. border: defaultBorder,
  1821. cursor: `pointer`,
  1822. borderRadius: `8px 8px`,
  1823. padding: `5px`
  1824. });
  1825. return button;
  1826. }
  1827. function ensureMenuStylesAdded() {
  1828. if (document.querySelector(`#` + constants_default.SETTINGS_STYLES_ID))
  1829. return;
  1830. const style = document.createElement(`style`);
  1831. style.id = constants_default.SETTINGS_STYLES_ID;
  1832. style.innerHTML = `
  1833. #${constants_default.SETTINGS_MENU_ID} * {
  1834. box-sizing: border-box;
  1835. }
  1836. `;
  1837. document.head.append(style);
  1838. }
  1839. function showSettings() {
  1840. if (document.querySelector(`#` + constants_default.SETTINGS_MENU_ID))
  1841. return;
  1842. if (window !== window.top)
  1843. return;
  1844. ensureMenuStylesAdded();
  1845. const background = document.createElement(`div`);
  1846. background.id = constants_default.SETTINGS_MENU_ID;
  1847. registerAddedNode(constants_default.SETTINGS_MENU_ID, `#` + constants_default.SETTINGS_MENU_ID);
  1848. styles(background, {
  1849. position: `fixed`,
  1850. top: `0`,
  1851. left: `0`,
  1852. zIndex: 9999,
  1853. width: `100vw`,
  1854. height: `100vh`,
  1855. backgroundColor: `#00000099`,
  1856. backdropFilter: `blur(6px)`,
  1857. display: `grid`,
  1858. grid: `grid`,
  1859. alignItems: `center`,
  1860. justifyContent: `space-around`
  1861. });
  1862. background.addEventListener(`click`, (event) => {
  1863. if (event.target === background) {
  1864. closeSettings();
  1865. }
  1866. });
  1867. const menu = document.createElement(`div`);
  1868. styles(menu, {
  1869. position: `relative`,
  1870. color: `#ddd`,
  1871. maxWidth: `90vw`,
  1872. maxHeight: `90vh`,
  1873. backgroundColor: `#555`,
  1874. padding: `20px`,
  1875. border: defaultBorder,
  1876. borderRadius: `8px`,
  1877. overflow: `hidden auto`
  1878. });
  1879. background.append(menu);
  1880. const closeButton = createButton(`\u{1F5D9}`);
  1881. styles(closeButton, {
  1882. position: `absolute`,
  1883. top: `-1px`,
  1884. right: `-1px`,
  1885. width: `30px`,
  1886. height: `30px`,
  1887. lineHeight: `25px`,
  1888. fontSize: `25px`,
  1889. borderRadius: `0 8px`,
  1890. padding: 0
  1891. });
  1892. closeButton.addEventListener(`click`, () => closeSettings());
  1893. menu.append(closeButton);
  1894. const menuHeader = createSettingHeader(`Lemmy Universal Link Switcher Settings`, 1.5);
  1895. styles(menuHeader, {
  1896. textDecoration: `underline`,
  1897. marginTop: 0
  1898. });
  1899. menu.append(menuHeader);
  1900. addMainHomeInstanceSetting(menu);
  1901. addMoreHomeInstancesSettings(menu);
  1902. document.body.append(background);
  1903. }
  1904. function createSettingHeader(text, sizeMultiplicator = 1) {
  1905. const header = document.createElement(`div`);
  1906. header.innerHTML = text;
  1907. styles(header, {
  1908. marginRight: `10px`,
  1909. fontSize: `${sizeMultiplicator * 1.4}em`,
  1910. lineHeight: `${sizeMultiplicator * 1.4}em`,
  1911. fontWeight: `bold`,
  1912. marginTop: `${sizeMultiplicator * 25}px`,
  1913. marginBottom: `${sizeMultiplicator * 10}px`
  1914. });
  1915. return header;
  1916. }
  1917. function addInfo(elem, content) {
  1918. const info = document.createElement(`span`);
  1919. info.innerHTML = ` \u{1F6C8}`;
  1920. tippy_default(info, {
  1921. content,
  1922. placement: "bottom",
  1923. triggerTarget: elem
  1924. });
  1925. elem.append(info);
  1926. }
  1927. function createInput(options) {
  1928. const inputElement = document.createElement(`input`);
  1929. styles(inputElement, {
  1930. width: `100%`,
  1931. margin: 0
  1932. });
  1933. if (options.placeholder) {
  1934. inputElement.placeholder = options.placeholder;
  1935. }
  1936. if (options.getter) {
  1937. Promise.resolve(options.getter()).then((result) => inputElement.value = result);
  1938. }
  1939. if (!inputElement.value) {
  1940. inputElement.setCustomValidity(`empty`);
  1941. }
  1942. styles(inputElement, { border: defaultBorder });
  1943. if (!options.validator && !options.setter) {
  1944. return;
  1945. }
  1946. let validator = options.validator || (() => true);
  1947. let setter = options.setter || (() => {
  1948. });
  1949. inputElement.addEventListener(`input`, async () => {
  1950. const validated = await validator(inputElement.value);
  1951. if (!inputElement.value || validated) {
  1952. inputElement.setCustomValidity(`empty`);
  1953. styles(inputElement, {
  1954. border: defaultBorder
  1955. });
  1956. }
  1957. if (inputElement.value) {
  1958. if (validated) {
  1959. inputElement.setCustomValidity(``);
  1960. await setter(inputElement.value);
  1961. } else if (!validated) {
  1962. inputElement.setCustomValidity(`fail`);
  1963. styles(inputElement, {
  1964. border: `1px solid red`
  1965. });
  1966. }
  1967. }
  1968. });
  1969. return inputElement;
  1970. }
  1971. var urlValidator = (value) => {
  1972. try {
  1973. new URL(value);
  1974. return true;
  1975. } catch (e) {
  1976. return false;
  1977. }
  1978. };
  1979. var instancePlaceholder = `The full link (including http(s)://) to the instance`;
  1980. function addMainHomeInstanceSetting(addTo) {
  1981. const header = createSettingHeader(`Main Home Instance`);
  1982. addInfo(header, `All links will be rewritten to this instance, except for links to your secondary home instances.`);
  1983. addTo.append(header);
  1984. const mainInstance = createInput({
  1985. getter: () => HOME,
  1986. setter: (value) => {
  1987. const url = new URL(value);
  1988. if (HOME !== url.origin) {
  1989. setHome(url.origin);
  1990. refresh();
  1991. }
  1992. },
  1993. validator: urlValidator,
  1994. placeholder: instancePlaceholder
  1995. });
  1996. addTo.append(mainInstance);
  1997. const homeButtonWrapper = document.createElement(`div`);
  1998. const refreshMakeHomeButton = () => {
  1999. homeButtonWrapper.replaceChildren();
  2000. if (location.origin === HOME)
  2001. return;
  2002. const makeHomeButton = createButton(`Use current page as home instance`);
  2003. styles(makeHomeButton, {
  2004. borderRadius: `0 0 8px 8px`
  2005. });
  2006. makeHomeButton.addEventListener(`click`, () => {
  2007. mainInstance.value = location.origin;
  2008. mainInstance.dispatchEvent(new Event(`input`));
  2009. });
  2010. homeButtonWrapper.append(makeHomeButton);
  2011. };
  2012. refreshMakeHomeButton();
  2013. refreshMethods.push(refreshMakeHomeButton);
  2014. addTo.append(homeButtonWrapper);
  2015. }
  2016. async function addMoreHomeInstancesSettings(addTo) {
  2017. const header = createSettingHeader(`Secondary Home Instances`);
  2018. addInfo(header, `All links pointing to these instances will not be changed.`);
  2019. addTo.append(header);
  2020. addTo.append(createListInput(
  2021. async () => await getSecondaryHomeInstances(),
  2022. async (value) => {
  2023. const homeInstances = await getSecondaryHomeInstances();
  2024. homeInstances.push(new URL(value).origin);
  2025. await setSecondaryHomeInstances(homeInstances);
  2026. },
  2027. async (value) => {
  2028. const homeInstances = await getSecondaryHomeInstances();
  2029. var index = homeInstances.indexOf(new URL(value).origin);
  2030. if (index > -1) {
  2031. homeInstances.splice(index, 1);
  2032. }
  2033. await setSecondaryHomeInstances(homeInstances);
  2034. }
  2035. ));
  2036. }
  2037. function createListItem(item, onClick) {
  2038. const listItem = createButton(item + ` \u{1F5D9}`, { inline: true });
  2039. styles(listItem, {
  2040. margin: `0px 5px 5px 0`
  2041. });
  2042. listItem.addEventListener(`click`, onClick);
  2043. return listItem;
  2044. }
  2045. function createListInput(getter, add, remove) {
  2046. const wrapper = document.createElement(`div`);
  2047. const list = document.createElement(`div`);
  2048. styles(list, {
  2049. marginBottom: `8px`
  2050. });
  2051. const refreshList = async () => {
  2052. const items = (await getter()).sort().map((item) => createListItem(item, async () => {
  2053. await remove(item);
  2054. refresh();
  2055. }));
  2056. if (items.length) {
  2057. list.replaceChildren(...items);
  2058. } else {
  2059. list.replaceChildren(`<None>`);
  2060. }
  2061. };
  2062. refreshList();
  2063. refreshMethods.push(refreshList);
  2064. wrapper.append(list);
  2065. const addInput = createInput({
  2066. validator: async (value) => {
  2067. return urlValidator(value) && !(await getter()).includes(value);
  2068. },
  2069. placeholder: instancePlaceholder
  2070. });
  2071. wrapper.append(addInput);
  2072. const buttonWrapper = document.createElement(`div`);
  2073. const refreshButtons = async () => {
  2074. buttonWrapper.replaceChildren();
  2075. const isCurrentPageHome = HOME === location.origin || (await getter()).includes(location.origin);
  2076. const addButton2 = createButton(`Add`);
  2077. styles(addButton2, {
  2078. borderRadius: isCurrentPageHome ? `0 0 8px 8px` : `0`
  2079. });
  2080. addButton2.addEventListener(`click`, async () => {
  2081. if (addInput.validity.valid) {
  2082. await add(addInput.value);
  2083. addInput.value = ``;
  2084. refresh();
  2085. }
  2086. });
  2087. buttonWrapper.append(addButton2);
  2088. if (!isCurrentPageHome) {
  2089. const addCurrentButton = createButton(`Add current page`);
  2090. styles(addCurrentButton, {
  2091. borderRadius: `0 0 8px 8px`
  2092. });
  2093. addCurrentButton.addEventListener(`click`, async () => {
  2094. await add(location.origin);
  2095. addInput.value = ``;
  2096. refresh();
  2097. });
  2098. buttonWrapper.append(addCurrentButton);
  2099. }
  2100. };
  2101. refreshButtons();
  2102. refreshMethods.push(refreshButtons);
  2103. wrapper.append(buttonWrapper);
  2104. return wrapper;
  2105. }
  2106.  
  2107. // src/rewriting/settings-buttons.js
  2108. function addSettingsButton() {
  2109. if (location.pathname !== `/settings`)
  2110. return false;
  2111. if (!document.querySelector(`[name="Description"][content="Lemmy"]`))
  2112. return false;
  2113. if (document.querySelector(`#` + constants_default.SETTINGS_BUTTON_ID))
  2114. return false;
  2115. const insertAfter = document.querySelector(`#user-password`)?.closest(`.card`);
  2116. if (!insertAfter)
  2117. return;
  2118. const button = document.createElement(`button`);
  2119. button.id = constants_default.SETTINGS_BUTTON_ID;
  2120. button.setAttribute(`class`, `btn btn-block btn-primary mr-4 w-100`);
  2121. button.innerHTML = `Lemmy Universal Link Switcher Settings`;
  2122. button.addEventListener(`click`, showSettings);
  2123. registerAddedNode(constants_default.SETTINGS_BUTTON_ID, `#` + constants_default.SETTINGS_BUTTON_ID);
  2124. insertAfter.insertAdjacentElement("afterend", button);
  2125. return true;
  2126. }
  2127.  
  2128. // src/rewriting/show-at-home.js
  2129. function showAtHomeButtonText() {
  2130. const host = new URL(HOME).hostname;
  2131. return `Show on ${host}`;
  2132. }
  2133. function createShowAtHomeAnchor(localUrl) {
  2134. const showAtHome = document.createElement(`a`);
  2135. showAtHome.dataset.creationHref = location.href;
  2136. showAtHome.classList.add(constants_default.SHOW_AT_HOME_BUTTON_CLASS);
  2137. showAtHome.innerHTML = showAtHomeButtonText();
  2138. showAtHome.href = localUrl;
  2139. registerAddedNode(constants_default.SHOW_AT_HOME_BUTTON_CLASS, `.` + constants_default.SHOW_AT_HOME_BUTTON_CLASS);
  2140. return showAtHome;
  2141. }
  2142. function addLemmyShowAtHomeButton(localUrl) {
  2143. const logo = document.querySelector(`a.navbar-brand`);
  2144. const navbarIcons = isV17() ? document.querySelector(`[title="Search"]`)?.closest(`.navbar-nav`) : document.querySelector(`#navbarIcons`);
  2145. if (!logo || !navbarIcons) {
  2146. debug(`Could not find position to insert ShowAtHomeButton at`);
  2147. return false;
  2148. }
  2149. const mobile = createShowAtHomeAnchor(localUrl);
  2150. mobile.classList.add(`d-md-none`);
  2151. mobile.style[`margin-right`] = `8px`;
  2152. mobile.style[`margin-left`] = `auto`;
  2153. if (isV17()) {
  2154. document.querySelector(`.navbar-nav.ml-auto`)?.classList.remove(`ml-auto`);
  2155. }
  2156. logo.insertAdjacentElement("afterend", mobile);
  2157. const desktop = createShowAtHomeAnchor(localUrl);
  2158. desktop.classList.add(`d-md-inline`);
  2159. desktop.style[`margin-right`] = `8px`;
  2160. navbarIcons.insertAdjacentElement("beforebegin", desktop);
  2161. return true;
  2162. }
  2163. function addKbinShowAtHomeButton(localUrl) {
  2164. const prependTo = document.querySelector(`#header menu:not(.head-nav__menu)`);
  2165. if (!prependTo) {
  2166. debug(`Could not find position to insert ShowAtHomeButton at`);
  2167. return false;
  2168. }
  2169. const item = document.createElement(`li`);
  2170. item.append(createShowAtHomeAnchor(localUrl));
  2171. prependTo.prepend(item);
  2172. return true;
  2173. }
  2174. function addButton(localUrl) {
  2175. const oldButton = document.querySelectorAll(`.` + constants_default.SHOW_AT_HOME_BUTTON_CLASS);
  2176. if (oldButton.length > 0 && oldButton[0].dataset.creationHref !== location.href) {
  2177. debug(`Removing old show at home button`);
  2178. oldButton.forEach((btn) => btn.remove());
  2179. } else if (oldButton.length > 0) {
  2180. debug(`Old show at home button still exists`);
  2181. return false;
  2182. }
  2183. if (!localUrl) {
  2184. debug(`No local URL for show at home button found`);
  2185. return false;
  2186. } else if (isRemoteLemmyUrl(location)) {
  2187. return addLemmyShowAtHomeButton(localUrl);
  2188. } else if (isRemoteKbinUrl(location)) {
  2189. return addKbinShowAtHomeButton(localUrl);
  2190. } else {
  2191. return false;
  2192. }
  2193. }
  2194. async function addShowAtHomeButton() {
  2195. if (isInstantlyRewritable(location)) {
  2196. return addButton(findLocalUrl(location));
  2197. } else if (isRewritableAfterResolving(location)) {
  2198. try {
  2199. return addButton(await fetchLocalUrl(location));
  2200. } catch (e) {
  2201. debug(`Error while trying to add "show at home" button`, e);
  2202. }
  2203. }
  2204. }
  2205.  
  2206. // src/rewriting/rewrite.js
  2207. function triggerRewrite() {
  2208. doAllDomChanges();
  2209. }
  2210. function isOrHasOurAddedNode(node) {
  2211. return getAddedNodesSelectors().some((selector) => node.matches?.(selector) || node.querySelector?.(selector));
  2212. }
  2213. function isIrrelevantChange(change) {
  2214. if (change.type === `childList`) {
  2215. if (Array.from(change.removedNodes).some(isOrHasOurAddedNode)) {
  2216. trace(`Change`, change, `is relevant because a removed node is/has ours`);
  2217. return false;
  2218. }
  2219. if (!Array.from(change.addedNodes).every(isOrHasOurAddedNode)) {
  2220. trace(`Change`, change, `is relevant because not every added node is/has ours`);
  2221. return false;
  2222. }
  2223. } else if (change.type === `attributes` && isRemoteUrl(new URL(change.target.href, location.origin))) {
  2224. trace(`Change`, change, `is relevant because href changed to a remote URL`);
  2225. return false;
  2226. }
  2227. trace(`Change`, change, `is irrelevant`);
  2228. return true;
  2229. }
  2230. async function startRewriting() {
  2231. new MutationObserver((changes, observer) => {
  2232. debug(`MutationObserver triggered`, changes);
  2233. if (changes.every(isIrrelevantChange)) {
  2234. debug(`All observed changes are irrelevant`);
  2235. return;
  2236. }
  2237. doAllDomChanges(changes);
  2238. }).observe(document.body, {
  2239. subtree: true,
  2240. childList: true,
  2241. attributeFilter: [`href`]
  2242. });
  2243. await doAllDomChanges();
  2244. }
  2245. async function doAllDomChanges(changes) {
  2246. debug(`doAllDomChanges start`);
  2247. const addedSettingsButtons = addSettingsButton();
  2248. if (addedSettingsButtons)
  2249. debug(`Added Settings Buttons`);
  2250. const addedShowAtHomeButton = HOME ? addShowAtHomeButton() : false;
  2251. if (addedShowAtHomeButton)
  2252. debug(`Added Show At Home Button`);
  2253. const rewrittenLinks = HOME ? await rewriteLinksToLocal(changes) : false;
  2254. if (rewrittenLinks)
  2255. debug(`Rewritten some links`);
  2256. debug(`doAllDomChanges end`);
  2257. }
  2258.  
  2259. // src/home.js
  2260. var HOME;
  2261. var secondaryHomes = [];
  2262. async function initHome() {
  2263. HOME = await getValue(`home`);
  2264. secondaryHomes = await getSecondaryHomeInstances();
  2265. if (!HOME && isLemmyInstance(location) && confirm(`Lemmy Universal Link Switcher: Set this instance to be your home instance to which all URLs get rewritten to?`)) {
  2266. setHome(location.origin);
  2267. }
  2268. }
  2269. async function setHome(newHome) {
  2270. trace(`Set HOME from ${HOME} to ${newHome}`);
  2271. if (typeof newHome !== `string`) {
  2272. newHome = null;
  2273. }
  2274. HOME = newHome;
  2275. await setValue(`home`, newHome);
  2276. }
  2277. async function getHome() {
  2278. return await getValue(`home`);
  2279. }
  2280. function updateHomePeriodically() {
  2281. trace(`Current HOME`, HOME);
  2282. setInterval(async () => {
  2283. const prevHome = HOME;
  2284. const prevSecondaries = secondaryHomes;
  2285. HOME = await getHome();
  2286. secondaryHomes = await getSecondaryHomeInstances();
  2287. if (prevHome !== HOME) {
  2288. debug(`HOME changed externally from`, prevHome, `to`, HOME);
  2289. triggerRewrite();
  2290. } else if (prevSecondaries.length !== secondaryHomes.length || !prevSecondaries.every((v) => secondaryHomes.includes(v))) {
  2291. debug(`secondaryHomes changed externally from`, prevSecondaries, `to`, secondaryHomes);
  2292. triggerRewrite();
  2293. }
  2294. }, 1337);
  2295. }
  2296. function isHomeInstance(url) {
  2297. return secondaryHomes.concat(HOME).includes(url.origin);
  2298. }
  2299. async function getSecondaryHomeInstances() {
  2300. const homeInstancesStr = await getValue(`secondaryHomes`);
  2301. return homeInstancesStr ? JSON.parse(homeInstancesStr) : [];
  2302. }
  2303. async function setSecondaryHomeInstances(homeInstances) {
  2304. await setValue(`secondaryHomes`, JSON.stringify(homeInstances));
  2305. }
  2306.  
  2307. // src/main.js
  2308. (async () => {
  2309. await initHome();
  2310. updateHomePeriodically();
  2311. await initAuth();
  2312. updateAuthPeriodically();
  2313. initSettings();
  2314. startRewriting();
  2315. })();
  2316. })();