URL Modifier for Search Engines

Modify (Redirect) URL links in search engines results to alternative frontends or for other purposes

当前为 2024-02-11 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name URL Modifier for Search Engines
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.5
  5. // @description Modify (Redirect) URL links in search engines results to alternative frontends or for other purposes
  6. // @author Domenic
  7.  
  8. // @match *://www.google.com/search?*
  9. // @match *://www.google.ad/search?*
  10. // @match *://www.google.ae/search?*
  11. // @match *://www.google.com.af/search?*
  12. // @match *://www.google.com.ag/search?*
  13. // @match *://www.google.al/search?*
  14. // @match *://www.google.am/search?*
  15. // @match *://www.google.co.ao/search?*
  16. // @match *://www.google.com.ar/search?*
  17. // @match *://www.google.as/search?*
  18. // @match *://www.google.at/search?*
  19. // @match *://www.google.com.au/search?*
  20. // @match *://www.google.az/search?*
  21. // @match *://www.google.ba/search?*
  22. // @match *://www.google.com.bd/search?*
  23. // @match *://www.google.be/search?*
  24. // @match *://www.google.bf/search?*
  25. // @match *://www.google.bg/search?*
  26. // @match *://www.google.com.bh/search?*
  27. // @match *://www.google.bi/search?*
  28. // @match *://www.google.bj/search?*
  29. // @match *://www.google.com.bn/search?*
  30. // @match *://www.google.com.bo/search?*
  31. // @match *://www.google.com.br/search?*
  32. // @match *://www.google.bs/search?*
  33. // @match *://www.google.bt/search?*
  34. // @match *://www.google.co.bw/search?*
  35. // @match *://www.google.by/search?*
  36. // @match *://www.google.com.bz/search?*
  37. // @match *://www.google.ca/search?*
  38. // @match *://www.google.cd/search?*
  39. // @match *://www.google.cf/search?*
  40. // @match *://www.google.cg/search?*
  41. // @match *://www.google.ch/search?*
  42. // @match *://www.google.ci/search?*
  43. // @match *://www.google.co.ck/search?*
  44. // @match *://www.google.cl/search?*
  45. // @match *://www.google.cm/search?*
  46. // @match *://www.google.cn/search?*
  47. // @match *://www.google.com.co/search?*
  48. // @match *://www.google.co.cr/search?*
  49. // @match *://www.google.com.cu/search?*
  50. // @match *://www.google.cv/search?*
  51. // @match *://www.google.com.cy/search?*
  52. // @match *://www.google.cz/search?*
  53. // @match *://www.google.de/search?*
  54. // @match *://www.google.dj/search?*
  55. // @match *://www.google.dk/search?*
  56. // @match *://www.google.dm/search?*
  57. // @match *://www.google.com.do/search?*
  58. // @match *://www.google.dz/search?*
  59. // @match *://www.google.com.ec/search?*
  60. // @match *://www.google.ee/search?*
  61. // @match *://www.google.com.eg/search?*
  62. // @match *://www.google.es/search?*
  63. // @match *://www.google.com.et/search?*
  64. // @match *://www.google.fi/search?*
  65. // @match *://www.google.com.fj/search?*
  66. // @match *://www.google.fm/search?*
  67. // @match *://www.google.fr/search?*
  68. // @match *://www.google.ga/search?*
  69. // @match *://www.google.ge/search?*
  70. // @match *://www.google.gg/search?*
  71. // @match *://www.google.com.gh/search?*
  72. // @match *://www.google.com.gi/search?*
  73. // @match *://www.google.gl/search?*
  74. // @match *://www.google.gm/search?*
  75. // @match *://www.google.gr/search?*
  76. // @match *://www.google.com.gt/search?*
  77. // @match *://www.google.gy/search?*
  78. // @match *://www.google.com.hk/search?*
  79. // @match *://www.google.hn/search?*
  80. // @match *://www.google.hr/search?*
  81. // @match *://www.google.ht/search?*
  82. // @match *://www.google.hu/search?*
  83. // @match *://www.google.co.id/search?*
  84. // @match *://www.google.ie/search?*
  85. // @match *://www.google.co.il/search?*
  86. // @match *://www.google.im/search?*
  87. // @match *://www.google.co.in/search?*
  88. // @match *://www.google.iq/search?*
  89. // @match *://www.google.is/search?*
  90. // @match *://www.google.it/search?*
  91. // @match *://www.google.je/search?*
  92. // @match *://www.google.com.jm/search?*
  93. // @match *://www.google.jo/search?*
  94. // @match *://www.google.co.jp/search?*
  95. // @match *://www.google.co.ke/search?*
  96. // @match *://www.google.com.kh/search?*
  97. // @match *://www.google.ki/search?*
  98. // @match *://www.google.kg/search?*
  99. // @match *://www.google.co.kr/search?*
  100. // @match *://www.google.com.kw/search?*
  101. // @match *://www.google.kz/search?*
  102. // @match *://www.google.la/search?*
  103. // @match *://www.google.com.lb/search?*
  104. // @match *://www.google.li/search?*
  105. // @match *://www.google.lk/search?*
  106. // @match *://www.google.co.ls/search?*
  107. // @match *://www.google.lt/search?*
  108. // @match *://www.google.lu/search?*
  109. // @match *://www.google.lv/search?*
  110. // @match *://www.google.com.ly/search?*
  111. // @match *://www.google.co.ma/search?*
  112. // @match *://www.google.md/search?*
  113. // @match *://www.google.me/search?*
  114. // @match *://www.google.mg/search?*
  115. // @match *://www.google.mk/search?*
  116. // @match *://www.google.ml/search?*
  117. // @match *://www.google.com.mm/search?*
  118. // @match *://www.google.mn/search?*
  119. // @match *://www.google.com.mt/search?*
  120. // @match *://www.google.mu/search?*
  121. // @match *://www.google.mv/search?*
  122. // @match *://www.google.mw/search?*
  123. // @match *://www.google.com.mx/search?*
  124. // @match *://www.google.com.my/search?*
  125. // @match *://www.google.co.mz/search?*
  126. // @match *://www.google.com.na/search?*
  127. // @match *://www.google.com.ng/search?*
  128. // @match *://www.google.com.ni/search?*
  129. // @match *://www.google.ne/search?*
  130. // @match *://www.google.nl/search?*
  131. // @match *://www.google.no/search?*
  132. // @match *://www.google.com.np/search?*
  133. // @match *://www.google.nr/search?*
  134. // @match *://www.google.nu/search?*
  135. // @match *://www.google.co.nz/search?*
  136. // @match *://www.google.com.om/search?*
  137. // @match *://www.google.com.pa/search?*
  138. // @match *://www.google.com.pe/search?*
  139. // @match *://www.google.com.pg/search?*
  140. // @match *://www.google.com.ph/search?*
  141. // @match *://www.google.com.pk/search?*
  142. // @match *://www.google.pl/search?*
  143. // @match *://www.google.pn/search?*
  144. // @match *://www.google.com.pr/search?*
  145. // @match *://www.google.ps/search?*
  146. // @match *://www.google.pt/search?*
  147. // @match *://www.google.com.py/search?*
  148. // @match *://www.google.com.qa/search?*
  149. // @match *://www.google.ro/search?*
  150. // @match *://www.google.ru/search?*
  151. // @match *://www.google.rw/search?*
  152. // @match *://www.google.com.sa/search?*
  153. // @match *://www.google.com.sb/search?*
  154. // @match *://www.google.sc/search?*
  155. // @match *://www.google.se/search?*
  156. // @match *://www.google.com.sg/search?*
  157. // @match *://www.google.sh/search?*
  158. // @match *://www.google.si/search?*
  159. // @match *://www.google.sk/search?*
  160. // @match *://www.google.com.sl/search?*
  161. // @match *://www.google.sn/search?*
  162. // @match *://www.google.so/search?*
  163. // @match *://www.google.sm/search?*
  164. // @match *://www.google.sr/search?*
  165. // @match *://www.google.st/search?*
  166. // @match *://www.google.com.sv/search?*
  167. // @match *://www.google.td/search?*
  168. // @match *://www.google.tg/search?*
  169. // @match *://www.google.co.th/search?*
  170. // @match *://www.google.com.tj/search?*
  171. // @match *://www.google.tl/search?*
  172. // @match *://www.google.tm/search?*
  173. // @match *://www.google.tn/search?*
  174. // @match *://www.google.to/search?*
  175. // @match *://www.google.com.tr/search?*
  176. // @match *://www.google.tt/search?*
  177. // @match *://www.google.com.tw/search?*
  178. // @match *://www.google.co.tz/search?*
  179. // @match *://www.google.com.ua/search?*
  180. // @match *://www.google.co.ug/search?*
  181. // @match *://www.google.co.uk/search?*
  182. // @match *://www.google.com.uy/search?*
  183. // @match *://www.google.co.uz/search?*
  184. // @match *://www.google.com.vc/search?*
  185. // @match *://www.google.co.ve/search?*
  186. // @match *://www.google.co.vi/search?*
  187. // @match *://www.google.com.vn/search?*
  188. // @match *://www.google.vu/search?*
  189. // @match *://www.google.ws/search?*
  190. // @match *://www.google.rs/search?*
  191. // @match *://www.google.co.za/search?*
  192. // @match *://www.google.co.zm/search?*
  193. // @match *://www.google.co.zw/search?*
  194. // @match *://www.google.cat/search?*
  195.  
  196. // @match *://search.yahoo.com/search*
  197. // @match *://search.yahoo.co.jp/search?*
  198. // @match *://*.search.yahoo.com/search*
  199.  
  200. // @match *://yandex.com/search/?*
  201. // @match *://yandex.ru/search/?*
  202.  
  203. // @match *://search.disroot.org/search*
  204. // @match *://searx.tiekoetter.com/search*
  205. // @match *://search.bus-hit.me/search*
  206. // @match *://search.inetol.net/search*
  207. // @match *://priv.au/search*
  208. // @match *://searx.be/search*
  209. // @match *://searxng.site/search*
  210. // @match *://search.hbubli.cc/search*
  211. // @match *://search.im-in.space/search*
  212. // @match *://opnxng.com/search*
  213. // @match *://search.upinmars.com/search*
  214. // @match *://search.sapti.me/search*
  215. // @match *://freesearch.club/search*
  216. // @match *://xo.wtf/search*
  217. // @match *://www.gruble.de/search*
  218. // @match *://searx.tuxcloud.net/search*
  219. // @match *://baresearch.org/search*
  220. // @match *://searx.daetalytica.io/search*
  221. // @match *://etsi.me/search*
  222. // @match *://search.leptons.xyz/search*
  223. // @match *://search.rowie.at/search*
  224. // @match *://search.mdosch.de/search*
  225. // @match *://searx.catfluori.de/search*
  226. // @match *://searx.si/search*
  227. // @match *://searx.namejeff.xyz/search*
  228. // @match *://search.itstechtime.com/search*
  229. // @match *://s.mble.dk/search*
  230. // @match *://searx.kutay.dev/search*
  231. // @match *://ooglester.com/search*
  232. // @match *://searx.ox2.fr/search*
  233. // @match *://searx.techsaviours.org/search*
  234. // @match *://searx.perennialte.ch/search*
  235. // @match *://s.trung.fun/searxng/search*
  236. // @match *://search.in.projectsegfau.lt/search*
  237. // @match *://search.projectsegfau.lt/search*
  238. // @match *://darmarit.org/searx/search*
  239. // @match *://searx.lunar.icu/search*
  240. // @match *://nyc1.sx.ggtyler.dev/search*
  241. // @match *://search.rhscz.eu/search*
  242. // @match *://paulgo.io/search*
  243. // @match *://northboot.xyz/search*
  244. // @match *://searx.zhenyapav.com/search*
  245. // @match *://searxng.ch/search*
  246. // @match *://copp.gg/search*
  247. // @match *://searx.sev.monster/search*
  248. // @match *://searx.oakleycord.dev/search*
  249. // @match *://searx.juancord.xyz/search*
  250. // @match *://searx.work/search*
  251. // @match *://search.ononoki.org/search*
  252. // @match *://search.demoniak.ch/search*
  253. // @match *://searx.cthd.icu/search*
  254. // @match *://searx.fmhy.net/search*
  255. // @match *://searx.headpat.exchange/search*
  256. // @match *://sex.finaltek.net/search*
  257. // @match *://search.gcomm.ch/search*
  258. // @match *://search.smnz.de/search*
  259. // @match *://searx.ankha.ac/search*
  260. // @match *://search.lvkaszus.pl/search*
  261. // @match *://searx.nobulart.com/search*
  262. // @match *://sx.t-1.org/search*
  263. // @match *://www.jabber-germany.de/searx/search*
  264. // @match *://sx.catgirl.cloud/search*
  265. // @match *://sx.vern.cc/searxng/search*
  266.  
  267. // @match *://www.startpage.com/search*
  268. // @match *://www.startpage.com/sp/search*
  269.  
  270. // @match *://search.brave.com/search*
  271.  
  272. // @match *://duckduckgo.com
  273. // @match *://duckduckgo.com/?*
  274.  
  275. // @match *://ghosterysearch.com/search?*
  276. // @match *://presearch.com/search?*
  277.  
  278. // @match *://metager.org/*meta/meta.ger3*
  279. // @match *://metager.de/*meta/meta.ger3*
  280.  
  281. // @match *://4get.ca/web?*
  282. // @match *://4get.silly.computer/web?*
  283. // @match *://4get.plunked.party/web?*
  284. // @match *://4get.konakona.moe/web?*
  285. // @match *://4get.sijh.net/web?*
  286. // @match *://4get.hbubli.cc/web?*
  287. // @match *://4get.perennialte.ch/web?*
  288. // @match *://4get.zzls.xyz/web?*
  289. // @match *://4getus.zzls.xyz/web?*
  290. // @match *://4get.seitan-ayoub.lol/web?*
  291. // @match *://4get.dcs0.hu/web?*
  292. // @match *://4get.psily.garden/web?*
  293. // @match *://4get.lvkaszus.pl/web?*
  294. // @match *://4get.kizuki.lol/web?*
  295.  
  296. // @match *://search.ahwx.org/search.php?*
  297. // @match *://search2.ahwx.org/search.php?*
  298. // @match *://search3.ahwx.org/search.php?*
  299. // @match *://ly.owo.si/search.php?*
  300. // @match *://librey.franklyflawless.org/search.php?*
  301. // @match *://librey.org/search.php?*
  302. // @match *://search.davidovski.xyz/search.php?*
  303. // @match *://search.milivojevic.in.rs/search.php?*
  304. // @match *://glass.prpl.wtf/search.php?*
  305. // @match *://librex.uk.to/search.php?*
  306. // @match *://librey.ix.tc/search.php?*
  307. // @match *://search.funami.tech/search.php?*
  308. // @match *://librex.retro-hax.net/search.php?*
  309. // @match *://librex.nohost.network/search.php?*
  310. // @match *://search.pabloferreiro.es/search.php?*
  311. // @match *://librey.baczek.me/search.php?*
  312. // @match *://lx.benike.me/search.php?*
  313. // @match *://search.seitan-ayoub.lol/search.php?*
  314. // @match *://librey.myroware.net/search.php?*
  315. // @match *://librey.nezumi.party/search.php?*
  316. // @match *://search.zeroish.xyz/search.php?*
  317.  
  318. // @match *://stract.com/search?*
  319.  
  320. // @match *://www.etools.ch/searchSubmit.do*
  321. // @match *://www.etools.ch/mobileSearch.do*
  322.  
  323. // @match *://www.mojeek.com/search?*
  324. // @match *://yep.com/web?*
  325. // @match *://www.torry.io/search*
  326. // @match *://www.qwant.com/?*
  327. // @match *://www.ecosia.org/search?*
  328. // @match *://search.becovi.com/serp.php?*
  329. // @match *://good-search.org/*search/?*
  330. // @match *://www.alltheinternet.com/?*
  331. // @match *://search.aol.com/*search*
  332. // @match *://www.onesearch.com/*search*
  333. // @match *://www.info.com/serp?*
  334. // @match *://oceanhero.today/web?*
  335.  
  336. // @match *://swisscows.com/*/web?*
  337.  
  338. // @match *://search.lilo.org/?*
  339. // @match *://search.entireweb.com/search?*
  340. // @match *://search.gmx.com/web/result?*
  341. // @match *://search.gmx.com/web?*
  342. // @match *://youcare.world/all?*
  343. // @match *://search.lycos.com/web/?*
  344. // @match *://alohafind.com/search/?*
  345. // @match *://spot.ecloud.global/search*
  346. // @match *://www.nona.de/?*
  347. // @match *://www.exalead.com/search/web/results/?*
  348. // @match *://search.seznam.cz/?*
  349. // @match *://gibiru.com/results.html?*
  350. // @match *://www.lukol.com/s.php?*
  351. // @match *://search.givewater.com/serp?*
  352. // @match *://results.excite.com/serp?*
  353. // @match *://www.webcrawler.com/serp?*
  354. // @match *://www.metacrawler.com/serp?*
  355. // @match *://www.dogpile.com/serp?*
  356.  
  357. // @grant none
  358. // @run-at document-end
  359. // @license GPL-2.0-only
  360. // ==/UserScript==
  361.  
  362. (function () {
  363. 'use strict';
  364.  
  365. // Define URL modification rules with precompiled regex
  366. const urlModificationRules = [
  367. // {
  368. // matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?(?:https?:\/\/)(?:[\w-]+\.|)((?:imdb|imgur|instagram|medium|odysee|quora|reddit|tiktok|twitter|wikipedia|youtube)\.(?:[a-z]+).*?)(?:$|\/RK=.*|&sa=.*)/),
  369. // replaceWith: 'https://farside.link/$1'
  370. // },
  371. {
  372. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/((?!test)[a-z]+)\.?m?\.wikipedia\.org\/(?:[a-z]+|wiki)\/(?!Special:Search)(.*?)(?:$|\/RK=.*|&sa=.*)/),
  373. replaceWith: 'https://www.wikiwand.com/$1/$2'
  374. },
  375. {
  376. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/zh\.?m?\.wikipedia\.org\/(?:zh-hans|wiki)\/(.*?)(?:$|\/RK=.*|&sa=.*)/),
  377. replaceWith: 'https://www.wikiwand.com/zh-hans/$1'
  378. },
  379. {
  380. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/wikipedia\.org\/(?:[a-z]+|wiki)\/(?!Special:Search)(.*?)(?:$|\/RK=.*|&sa=.*)/),
  381. replaceWith: 'https://www.wikiwand.com/en/$1'
  382. },
  383. {
  384. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(?:old|www)\.reddit\.com\/((?:r|u)\/.*?)(?:$|\/RK=.*|&sa=.*)/),
  385. replaceWith: 'https://safereddit.com/$1'
  386. // replaceWith: 'https://lr.vern.cc/$1'
  387. },
  388. {
  389. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/www\.quora\.com\/((?=.*-)[\w-]+|profile\/.*?)(?:$|\/RK=.*|&sa=.*)/),
  390. replaceWith: 'https://quetre.iket.me/$1'
  391. },
  392. {
  393. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/twitter\.com\/([A-Za-z_][\w]+)(\/status\/(?:\d+))?(?:.*?)(?:$|\/RK=.*|&sa=.*)/),
  394. replaceWith: 'https://nitter.catsarch.com/$1$2'
  395. },
  396. {
  397. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/stackoverflow\.com(\/questions\/\d+\/[\w-]+)(?:.*?)(?:$|\/RK=.*|&sa=.*)/),
  398. replaceWith: 'https://ao.vern.cc$1'
  399. },
  400. {
  401. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(.*?(?:medium.*?|towardsdatascience|betterprogramming|plainenglish|gitconnected|aninjusticemag|betterhumans|uxdesign|uxplanet)\.\w+\/(?!tag)(?=.*-)(?:.*?|[\w@.]+\/[\w-]+))(?:\?source=.*)?(?:$|\/RK=.*|&sa=.*)/),
  402. replaceWith: 'https://freedium.cfd/https://$1'
  403. },
  404. {
  405. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(?:www\.|m\.)?youtube\.com\/((?:@|watch\?|playlist\?|channel\/|user\/|shorts\/).*?)(?:$|\/RK=.*|&sa=.*)/),
  406. // replaceWith: 'https://vid.puffyan.us/$1'
  407. replaceWith: 'https://piped.video/$1'
  408. },
  409. {
  410. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/music\.youtube\.com\/((?:playlist\?|watch\?|channel\/|browse\/).*?)(?:$|\/RK=.*|&sa=.*)/),
  411. replaceWith: 'https://hyperpipe.surge.sh/$1'
  412. },
  413. {
  414. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/www\.twitch\.tv\/(\w+)(?:.*?)(?:$|\/RK=.*|&sa=.*)/),
  415. replaceWith: 'https://ttv.vern.cc/$1'
  416. },
  417. {
  418. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(?:m|www)\.imdb\.com\/((?:title|name)\/\w+)(?:.*?)(?:$|\/RK=.*|&sa=.*)/),
  419. replaceWith: 'https://ld.vern.cc/$1'
  420. },
  421. {
  422. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/www\.goodreads\.com\/((?:(?:[a-z]+\/)?book\/show|work\/quotes|series|author\/show)\/[\w.-]+)(?:.*?)(?:$|\/RK=.*|&sa=.*)/),
  423. replaceWith: 'https://bl.vern.cc/$1'
  424. },
  425. {
  426. // only support English Fandom sites
  427. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/((?!www|community).*?)\.fandom\.com\/wiki\/(.*?)(?:$|\/RK=.*|&sa=.*)/),
  428. replaceWith: 'https://antifandom.com/$1/wiki/$2'
  429. },
  430. {
  431. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/www\.urbandictionary\.com\/(define\.php\?term=.*?)(?:$|\/RK=.*|&sa=.*)/),
  432. replaceWith: 'https://rd.vern.cc/$1'
  433. },
  434. {
  435. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/www\.reuters\.com\/((?=.*\/)(?=.*-).*?)(?:$|\/RK=.*|&sa=.*)/),
  436. replaceWith: 'https://nu.vern.cc/$1'
  437. },
  438. {
  439. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(www\.ft\.com\/content\/[\w-]+)(?:.*?)(?:$|\/RK=.*|&sa=.*)/),
  440. replaceWith: 'https://archive.today/https://$1'
  441. },
  442. {
  443. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(www\.bloomberg\.com\/(?:(?:[a-z]+\/)?news|opinion)\/.*?)(?:$|\/RK=.*|&sa=.*)/),
  444. replaceWith: 'https://archive.today/https://$1'
  445. },
  446. {
  447. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/www\.npr\.org\/(?:\d{4}\/\d{2}\/\d{2}|sections)\/(?:[A-Za-z-]+\/\d{4}\/\d{2}\/\d{2}\/)?(\d+)\/(?:.*?)(?:$|\/RK=.*|&sa=.*)/),
  448. replaceWith: 'https://text.npr.org/$1'
  449. },
  450. {
  451. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/news\.ycombinator\.com\/item\?id=(\d+)(?:.*?)(?:$|\/RK=.*|&sa=.*)/),
  452. replaceWith: 'https://www.hckrnws.com/stories/$1'
  453. },
  454. {
  455. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(?:[a-z]+)\.slashdot\.org(.*?)(?:$|\/RK=.*|&sa=.*)/),
  456. replaceWith: 'https://slashdot.org$1'
  457. },
  458. {
  459. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(?:(?:.*)(?:arxiv\.org\/pdf|arxiv-export-lb\.library\.cornell\.edu\/(?:pdf|abs)))\/(\d{4}\.\d{4,5}(v\d)?)(?:.*)/),
  460. replaceWith: 'https://arxiv.org/abs/$1'
  461. },
  462. {
  463. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(ieeexplore\.ieee\.org\/document\/\d+)\/(?:.*?)(?:$|\/RK=.*|&sa=.*)/),
  464. replaceWith: 'https://$1'
  465. },
  466. {
  467. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/github\.ink(.*?)(?:$|\/RK=.*|&sa=.*)/),
  468. replaceWith: 'https://github.com$1'
  469. },
  470. {
  471. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/www\.snopes\.com(.*?)(?:$|\/RK=.*|&sa=.*)/),
  472. replaceWith: 'https://sd.vern.cc$1'
  473. },
  474. {
  475. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/www\.instructables\.com\/(.*?)(?:$|\/RK=.*|&sa=.*)/),
  476. replaceWith: 'https://ds.vern.cc/$1'
  477. // replaceWith: 'https://structables.private.coffee/$1'
  478. },
  479. {
  480. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/genius\.com\/((?=[\w-]+lyrics|search\?q=).*?)(?:$|\/RK=.*|&sa=.*)/),
  481. replaceWith: 'https://dm.vern.cc/$1'
  482. },
  483. {
  484. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(.*?)\.bandcamp\.com\/(?:$|\/RK=.*|&sa=.*)/),
  485. replaceWith: 'https://tn.vern.cc/artist.php?name=$1'
  486. },
  487. {
  488. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(.*?)\.bandcamp\.com\/(.*?)\/(.*?)(?:$|\/RK=.*|&sa=.*)/),
  489. replaceWith: 'https://tn.vern.cc/release.php?artist=$1&type=$2&name=$3'
  490. },
  491. {
  492. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/bandcamp\.com\/search\?q=(.*?)(?:$|\/RK=.*|&sa=.*)/),
  493. replaceWith: 'https://tn.vern.cc/search.php?query=$1'
  494. },
  495. {
  496. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/f4\.bcbits\.com\/img\/(.*?)(?:$|\/RK=.*|&sa=.*)/),
  497. replaceWith: 'https://tn.vern.cc/image.php?file=$1'
  498. },
  499. {
  500. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/t4\.bcbits\.com\/stream\/(.*?)\/(.*?)\/(.*?)\?token=(.*?)(?:$|\/RK=.*|&sa=.*)/),
  501. replaceWith: 'https://tn.vern.cc/audio.php?directory=$1&format=$2&file=$3&token=$4'
  502. },
  503. {
  504. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(?:[\w.]+)?imgur.com\/((?:a\/)?(?!gallery)[\w.]+)(?:.*?)(?:$|\/RK=.*|&sa=.*)/),
  505. replaceWith: 'https://rimgo.totaldarkness.net/$1'
  506. },
  507. {
  508. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/www\.pixiv\.net\/(?:[a-z]+\/)?(artworks\/\d+|tags\/\w+|users\/\d+).*/),
  509. replaceWith: 'https://pixivfe.exozy.me/$1'
  510. },
  511. {
  512. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/knowyourmeme\.com\/(.*?)(?:$|\/RK=.*|&sa=.*)/),
  513. replaceWith: 'https://mm.vern.cc/$1'
  514. },
  515. {
  516. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/tenor\.com\/((?:view|search)\/.*?)(?:$|\/RK=.*|&sa=.*)/),
  517. replaceWith: 'https://sp.vern.cc/$1'
  518. },
  519. {
  520. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))?https?:\/\/(?:\w+\.)?ifunny\.co\/(picture\/.*?)(?:$|\/RK=.*|&sa=.*)/),
  521. replaceWith: 'https://uf.vern.cc/$1'
  522. },
  523. {
  524. matchRegex: new RegExp(/^(?:.*?(?:\/RU=|&q=|&as=))https?:\/\/(.*?)(?:$|\/RK=.*|&sa=.*)/),
  525. replaceWith: 'https://$1'
  526. },
  527. // Add more rules here as needed
  528. ];
  529.  
  530. // Define enhanced selector rules for each search engine
  531. const selectorRules = {
  532. 'google': [
  533. {
  534. selector: 'div.MjjYud div.yuRUbf div span a',
  535. childSelector: 'div.byrV5b cite',
  536. updateChildText: true,
  537. containProtocol: true,
  538. urlDisplayMethod: 1
  539. },
  540. {
  541. // selector for sub-results
  542. selector: 'div.MjjYud div.HiHjCd a'
  543. },
  544. {
  545. // selector for sidebar links
  546. selector: 'div.TQc1id#rhs a'
  547. }
  548. ],
  549. 'yahoo': [
  550. {
  551. parentSelector: 'div#left div#web ol li div div.compTitle h3.title',
  552. linkNodeSelector: 'a',
  553. textNodeSelector: 'span.p-abs',
  554. childSelector: 'span',
  555. updateChildText: true,
  556. containProtocol: false,
  557. multiElementsForUrlDisplay: 2
  558. },
  559. {
  560. selector: 'div#left div#web ol li div ul.compArticleList a'
  561. },
  562. {
  563. selector: 'div#left div#web ol li div div.compGenericCardList a'
  564. },
  565. {
  566. selector: 'div#right ol.cardReg.searchRightTop a'
  567. }
  568. ],
  569. 'yahoojp': [
  570. {
  571. parentSelector: 'div.sw-CardBase div.sw-Card__title',
  572. linkNodeSelector: 'a.sw-Card__titleInner',
  573. textNodeSelector: 'div.sw-Card__titleCiteWrapper cite ol',
  574. childSelector: 'li',
  575. updateChildText: true,
  576. containProtocol: true,
  577. multiElementsForUrlDisplay: 1
  578. },
  579. {
  580. selector: 'div.sw-CardBase p.Algo__osl a'
  581. },
  582. {
  583. selector: 'div.sw-CardBase div.sw-Card.AnswerKnowledgePanel__title div.sw-Tooltip__balloonInner a',
  584. updateTextByOverwrite: true,
  585. urlDisplayMethod: 2
  586. },
  587. {
  588. selector: 'div.sw-CardBase div.sw-Card.AnswerKnowledgePanel__title a'
  589. },
  590. {
  591. selector: 'div.sw-CardBase div.sw-Card.AnswerKnowledgePanel__info a'
  592. }
  593. ],
  594. 'yandex': [
  595. {
  596. selector: 'ul#search-result li div.Organic-Subtitle div a',
  597. updateChildText: true,
  598. containProtocol: false,
  599. urlDisplayMethod: 1,
  600. },
  601. {
  602. selector: 'ul#search-result li div.Organic div a',
  603. }
  604. ],
  605. 'searx': [
  606. {
  607. selector: 'article.result a.url_wrapper',
  608. childSelector: 'span span',
  609. updateChildText: true,
  610. containProtocol: true,
  611. multiElementsForUrlDisplay: 1
  612. },
  613. {
  614. selector: 'article.result h3 a'
  615. },
  616. {
  617. selector: 'aside.infobox div.urls ul li a'
  618. }
  619. ],
  620. 'startpage': [
  621. {
  622. selector: 'a.w-gl__result-url.result-link',
  623. updateTextByOverwrite: true,
  624. urlDisplayMethod: 2
  625. },
  626. {
  627. selector: 'a.w-gl__result-title.result-link'
  628. },
  629. {
  630. selector: 'div.sx-kp-main a'
  631. }
  632. ],
  633. 'brave': [
  634. {
  635. selector: 'div.snippet a.h',
  636. childSelector: 'div.site cite.snippet-url span',
  637. updateChildText: true,
  638. containProtocol: false,
  639. multiElementsForUrlDisplay: 1
  640. },
  641. {
  642. selector: 'div#discussions.snippet a',
  643. },
  644. {
  645. selector: 'div#infobox-snippet.snippet a'
  646. }
  647. ],
  648. 'duckduckgo': [
  649. {
  650. selector: 'a.eVNpHGjtxRBq_gLOfGDr.LQNqh2U1kzYxREs65IJu'
  651. },
  652. {
  653. selector: 'a.Rn_JXVtoPVAFyGkcaXyK',
  654. childSelector: 'span',
  655. updateChildText: true,
  656. containProtocol: true,
  657. multiElementsForUrlDisplay: 1
  658. },
  659. {
  660. // Selector for sub-results
  661. selector: 'ul.b269SZlC2oyR13Fcc4Iy li a.f3uDrYrWF3Exrfp1m3Og'
  662. },
  663. {
  664. selector: 'div.react-module div section div a'
  665. }
  666. ],
  667. 'ghostery': [
  668. {
  669. selector: 'li.result h2 a'
  670. },
  671. {
  672. selector: 'li.result div.snippet div.address a.url',
  673. updateTextByOverwrite: true,
  674. urlDisplayMethod: 2
  675. }
  676. ],
  677. 'presearch': [
  678. {
  679. selector: 'div.relative div.w-auto a',
  680. childSelector: 'div',
  681. updateChildText: true,
  682. urlDisplayMethod: 3,
  683. },
  684. {
  685. selector: 'div.relative div.inline-block a'
  686. }
  687. ],
  688. 'metager': [
  689. {
  690. selector: 'h2.result-title a'
  691. },
  692. {
  693. selector: 'div.result-subheadline a',
  694. updateTextByOverwrite: true,
  695. urlDisplayMethod: 3
  696. },
  697. {
  698. selector: 'div.quicktip div.quicktip-headline h1 a'
  699. },
  700. {
  701. selector: 'div.quicktip div.quicktip-detail h2 a'
  702. }
  703. ],
  704. '4get': [
  705. {
  706. parentSelector: 'div.text-result',
  707. linkNodeSelector: 'a.hover',
  708. textNodeSelector: 'div.url',
  709. childSelector: 'a.part',
  710. updateChildText: true,
  711. containProtocol: true,
  712. multiElementsForUrlDisplay: 4
  713. },
  714. {
  715. selector: 'div.text-result div.sublinks a'
  716. },
  717. {
  718. parentSelector: 'div.right-wrapper div.answer',
  719. linkNodeSelector: 'a.answer-title',
  720. textNodeSelector: 'div.url',
  721. childSelector: 'a.part',
  722. updateChildText: true,
  723. containProtocol: true,
  724. multiElementsForUrlDisplay: 4
  725. }
  726. ],
  727. 'librey': [
  728. {
  729. selector: 'div.text-result-wrapper a',
  730. updateTextWithoutOverwrite: true,
  731. useTopLevelDomain: true,
  732. urlDisplayMethod: 2
  733. },
  734. {
  735. selector: 'p.special-result-container a',
  736. updateTextWithoutOverwrite: true,
  737. urlDisplayMethod: 2
  738. },
  739. ],
  740. 'stract': [
  741. {
  742. selector: 'div.grid div div.flex div div a.text-link'
  743. },
  744. {
  745. selector: 'div.grid div div.flex div div div a',
  746. updateTextByOverwrite: true,
  747. urlDisplayMethod: 2
  748. },
  749. {
  750. selector: 'div.mb-5.text-xl a'
  751. },
  752. {
  753. selector: 'div.text-sm a.text-link'
  754. }
  755. ],
  756. 'etools': [
  757. {
  758. // searchSubmit.do
  759. selector: 'td.record a.title'
  760. },
  761. {
  762. // mobileSearch.do
  763. selector: 'p a.title'
  764. }
  765. ],
  766. 'mojeek': [
  767. {
  768. selector: 'ul.results-standard li h2 a.title'
  769. },
  770. {
  771. selector: 'ul.results-standard li a.ob',
  772. childSelector: 'span.url',
  773. updateChildText: true,
  774. containProtocol: true,
  775. urlDisplayMethod: 1
  776. },
  777. {
  778. selector: 'div.infobox p a'
  779. },
  780. {
  781. selector: 'div.results.news-results li a'
  782. },
  783. {
  784. selector: 'div.right-col div.results ul li a'
  785. }
  786. ],
  787. 'yep': [
  788. {
  789. selector: 'div.css-102xgmn-card div div a',
  790. childSelector: 'div span',
  791. updateChildText: true,
  792. containProtocol: false,
  793. urlDisplayMethod: 1
  794. }
  795. ],
  796. 'torry': [
  797. {
  798. selector: 'div.searpList p a.toranclick',
  799. updateTextByOverwrite: true,
  800. urlDisplayMethod: 2
  801. },
  802. {
  803. selector: 'div.searpList div h2 a.toranclick',
  804. },
  805. {
  806. selector: 'div.searpList ul li a',
  807. }
  808. ],
  809. 'qwant': [
  810. {
  811. selector: 'div._35zId._3A7p7 a.external'
  812. },
  813. {
  814. selector: 'div._35zId._3WA-c a.external',
  815. childSelector: 'span',
  816. updateChildText: true,
  817. containProtocol: false,
  818. multiElementsForUrlDisplay: 1
  819. },
  820. {
  821. // Selector for sub-results
  822. selector: 'div._12BMd div._2-LMx._2E8gc._16lFV.Ks7KS.tCpbb.m_hqb a.external'
  823. },
  824. {
  825. selector: 'div._3McWE.is-sidebar a.external'
  826. }
  827. ],
  828. 'ecosia': [
  829. {
  830. selector: 'div.mainline__result-wrapper div.result__header div.result__info a',
  831. childSelector: 'span span',
  832. updateChildText: true,
  833. containProtocol: true,
  834. multiElementsForUrlDisplay: 1
  835. },
  836. {
  837. selector: 'div.mainline__result-wrapper div.result__header div.result__title a'
  838. },
  839. {
  840. selector: 'div.mainline__result-wrapper div ul li a'
  841. },
  842. {
  843. selector: 'aside.sidebar article div.entity-links ul li a'
  844. },
  845. {
  846. selector: 'aside.sidebar article div.entity__content p a'
  847. }
  848. ],
  849. 'oscobo': [
  850. {
  851. selector: 'div.result a',
  852. childSelector: 'span.siteTitleWrap span.favicons',
  853. updateTextWithoutOverwrite: true,
  854. containProtocol: true,
  855. urlDisplayMethod: 1
  856. }
  857. ],
  858. 'good': [
  859. {
  860. selector: 'div.content div.margin-bottom--small.box a.link--search',
  861. childSelector: 'p.url',
  862. updateTextByOverwrite: true,
  863. urlDisplayMethod: 2
  864. },
  865. {
  866. selector: 'div.sx-kp-top a'
  867. },
  868. {
  869. selector: 'div.sx-kp-tab-content-wrap section ul.sx-kp-social-links a'
  870. },
  871. {
  872. selector: 'div.sx-kp-tab-content-wrap section div.sx-kp-attributions a'
  873. }
  874. ],
  875. 'alltheinternet': [
  876. {
  877. parentSelector: 'div.gs-webResult.gs-result',
  878. linkNodeSelector: 'a.gs-title',
  879. textNodeSelector: 'div.gsc-url-top div.gs-visibleUrl-long',
  880. updateTextByOverwrite: true,
  881. urlDisplayMethod: 2
  882. }
  883. ],
  884. 'aol': [
  885. {
  886. parentSelector: 'div#left div#web ol li div div.compTitle',
  887. linkNodeSelector: 'h3.title a',
  888. textNodeSelector: 'div span',
  889. updateTextByOverwrite: true,
  890. urlDisplayMethod: 3
  891. },
  892. {
  893. selector: 'div#left div#web ol li div div.compList a'
  894. },
  895. {
  896. selector: 'div#right ol.cardReg.searchRightTop a'
  897. }
  898. ],
  899. 'onesearch': [
  900. {
  901. parentSelector: 'div#left div#web ol li div div.compTitle',
  902. linkNodeSelector: 'h3.title a',
  903. textNodeSelector: 'div span',
  904. updateTextByOverwrite: true,
  905. urlDisplayMethod: 3
  906. },
  907. {
  908. selector: 'div#left div#web ol li div div.compList a'
  909. },
  910. {
  911. selector: 'div#right ol.cardReg.searchRightTop a'
  912. }
  913. ],
  914. 'info': [
  915. {
  916. parentSelector: 'div.web-yahoo__result',
  917. linkNodeSelector: 'a.web-yahoo__title',
  918. textNodeSelector: 'span.web-yahoo__url',
  919. updateTextWithoutOverwrite: true,
  920. urlDisplayMethod: 2
  921. },
  922. {
  923. selector: 'div.sidebar-results a'
  924. }
  925. ],
  926. 'oceanhero': [
  927. {
  928. selector: 'div div div a',
  929. childSelector: 'span cite',
  930. updateChildText: true,
  931. containProtocol: false,
  932. urlDisplayMethod: 1
  933. },
  934. {
  935. selector: 'section div ul li a'
  936. },
  937. {
  938. selector: 'div div div p a'
  939. }
  940. ],
  941. 'swisscows': [
  942. {
  943. selector: 'article.item-web a.site',
  944. updateTextWithoutOverwrite: true,
  945. containProtocol: false,
  946. urlDisplayMethod: 1
  947. },
  948. {
  949. selector: 'article.item-web a'
  950. }
  951. ],
  952. 'lilo': [
  953. {
  954. selector: 'div.lilo-text-result div p a.has-text-grey-darker',
  955. childSelector: 'span',
  956. updateChildText: true,
  957. containProtocol: true,
  958. multiElementsForUrlDisplay: 2
  959. },
  960. {
  961. selector: 'div.lilo-text-result div a.has-text-primary'
  962. },
  963. {
  964. selector: 'div.column.is-two-fifths a'
  965. }
  966. ],
  967. 'entireweb': [
  968. {
  969. parentSelector: 'div.web-result',
  970. linkNodeSelector: 'a.web-result-title',
  971. textNodeSelector: 'div.web-result-domain',
  972. updateTextWithoutOverwrite: true,
  973. urlDisplayMethod: 3
  974. },
  975. {
  976. selector: 'div#infobox-list.card div.card-body a'
  977. },
  978. {
  979. parentSelector: 'div.gsc-webResult.gsc-result',
  980. linkNodeSelector: 'a.gs-title',
  981. textNodeSelector: 'div.gsc-url-top',
  982. updateTextByOverwrite: true,
  983. containProtocol: false,
  984. urlDisplayMethod: 1
  985. }
  986. ],
  987. 'gmx': [
  988. {
  989. selector: 'div.eMd a.eMdhl'
  990. },
  991. {
  992. selector: 'div.eMd a.eMdu',
  993. childSelector: 'span',
  994. updateChildText: true,
  995. containProtocol: true,
  996. multiElementsForUrlDisplay: 2
  997. },
  998. ],
  999. 'youcare': [
  1000. {
  1001. selector: 'div.search-result-item-text a.search-result-item-text__title'
  1002. },
  1003. {
  1004. selector: 'div.search-result-item-text div div a.search-result-item-text-sitename'
  1005. },
  1006. {
  1007. selector: 'div.search-result-item-text div div a.search-result-item-text__header-url',
  1008. updateTextWithoutOverwrite: true,
  1009. containProtocol: true,
  1010. urlDisplayMethod: 1
  1011. },
  1012. {
  1013. selector: "div.search-results-view__side a"
  1014. }
  1015. ],
  1016. 'lycos': [
  1017. {
  1018. parentSelector: 'div.results li.result-item',
  1019. linkNodeSelector: 'a.result-link',
  1020. textNodeSelector: 'span.result-url',
  1021. updateTextByOverwrite: true,
  1022. urlDisplayMethod: 3
  1023. },
  1024. {
  1025. selector: 'div.col-aside a'
  1026. }
  1027. ],
  1028. 'alohafind': [
  1029. {
  1030. parentSelector: 'div.gsc-webResult.gsc-result',
  1031. linkNodeSelector: 'a.gs-title',
  1032. textNodeSelector: 'div.gs-visibleUrl-short',
  1033. updateTextByOverwrite: true,
  1034. urlDisplayMethod: 3
  1035. }
  1036. ],
  1037. 'spot': [
  1038. {
  1039. selector: 'div.result h4 a'
  1040. },
  1041. {
  1042. selector: 'div.result a.external-link',
  1043. updateTextByOverwrite: true,
  1044. urlDisplayMethod: 2
  1045. },
  1046. {
  1047. selector: 'div.infobox div.footer div.links a'
  1048. }
  1049. ],
  1050. 'nona': [
  1051. {
  1052. selector: 'section.result-section article.teaser div.teaser__container a.teaser__topline',
  1053. updateTextByOverwrite: true,
  1054. urlDisplayMethod: 2
  1055. },
  1056. {
  1057. selector: 'section.result-section article.teaser div.teaser__container a.teaser__link'
  1058. },
  1059. {
  1060. selector: 'section.result-section article.entity-teaser div.entity-teaser__wrapper a'
  1061. }
  1062. ],
  1063. 'exalead': [
  1064. {
  1065. selector: 'li.media div.media-body a.ellipsis',
  1066. updateTextByOverwrite: true,
  1067. urlDisplayMethod: 3
  1068. },
  1069. {
  1070. selector: 'li.media div.media-body a'
  1071. }
  1072. ],
  1073. 'seznam': [
  1074. {
  1075. selector: 'div.f2c528 h3 a'
  1076. },
  1077. {
  1078. selector: 'div.f2c528 a.d5e75c',
  1079. updateTextByOverwrite: true,
  1080. urlDisplayMethod: 3
  1081. },
  1082. ],
  1083. 'gibiru': [
  1084. {
  1085. parentSelector: 'div.gs-webResult.gs-result',
  1086. linkNodeSelector: 'a.gs-title',
  1087. textNodeSelector: 'div.gsc-url-top div.gs-visibleUrl-breadcrumb',
  1088. childSelector: 'span',
  1089. updateChildText: true,
  1090. containProtocol: false,
  1091. multiElementsForUrlDisplay: 1
  1092. }
  1093. ],
  1094. 'lukol': [
  1095. {
  1096. parentSelector: 'div.gsc-webResult.gsc-result',
  1097. linkNodeSelector: 'a.gs-title',
  1098. textNodeSelector: 'div.gsc-url-bottom div.gs-visibleUrl-long',
  1099. updateTextByOverwrite: true,
  1100. urlDisplayMethod: 2
  1101. }
  1102. ],
  1103. 'givewater': [
  1104. {
  1105. parentSelector: 'div.web-bing__result',
  1106. linkNodeSelector: 'a.web-bing__title',
  1107. textNodeSelector: 'span.web-bing__url',
  1108. updateTextByOverwrite: true,
  1109. urlDisplayMethod: 2
  1110. }
  1111. ],
  1112. 'excite': [
  1113. {
  1114. parentSelector: 'div.web-bing__result',
  1115. linkNodeSelector: 'a.web-bing__title',
  1116. textNodeSelector: 'span.web-bing__url',
  1117. updateTextByOverwrite: true,
  1118. urlDisplayMethod: 2
  1119. }
  1120. ],
  1121. 'webcrawler': [
  1122. {
  1123. parentSelector: 'div.web-bing__result',
  1124. linkNodeSelector: 'a.web-bing__title',
  1125. textNodeSelector: 'span.web-bing__url',
  1126. updateTextByOverwrite: true,
  1127. urlDisplayMethod: 2
  1128. }
  1129. ],
  1130. 'metacrawler': [
  1131. {
  1132. parentSelector: 'div.web-bing__result',
  1133. linkNodeSelector: 'a.web-bing__title',
  1134. textNodeSelector: 'span.web-bing__url',
  1135. updateTextByOverwrite: true,
  1136. urlDisplayMethod: 2
  1137. }
  1138. ],
  1139. 'dogpile': [
  1140. {
  1141. parentSelector: 'div.web-bing__result',
  1142. linkNodeSelector: 'a.web-bing__title',
  1143. textNodeSelector: 'span.web-bing__url',
  1144. updateTextByOverwrite: true,
  1145. urlDisplayMethod: 2
  1146. }
  1147. ]
  1148. // Additional search engines can be defined here...
  1149. };
  1150.  
  1151. // User-defined list of search engine instance URLs
  1152. const searchEngines = {
  1153. 'google': {
  1154. hosts: ['google.com'],
  1155. // search results container
  1156. // you can ignore this parameter if you don't want to set it, just delete it
  1157. // defult value is 'body'
  1158. resultContainerSelectors: ['div.GyAeWb#rcnt']
  1159. },
  1160. 'yahoo': {
  1161. hosts: ['search.yahoo.com'],
  1162. resultContainerSelectors: ['div#results div#cols']
  1163. },
  1164. 'yahoojp': {
  1165. hosts: ['search.yahoo.co.jp'],
  1166. resultContainerSelectors: ['div#contents div#contents__wrap']
  1167. },
  1168. 'yandex': {
  1169. hosts: [
  1170. 'yandex.com',
  1171. 'yandex.ru'
  1172. ],
  1173. resultContainerSelectors: ['div.main__container']
  1174. },
  1175. 'searx': {
  1176. hosts: [
  1177. 'search.disroot.org',
  1178. 'searx.tiekoetter.com',
  1179. 'search.bus-hit.me',
  1180. 'search.inetol.net',
  1181. 'priv.au',
  1182. 'searx.be',
  1183. 'searxng.site',
  1184. 'search.hbubli.cc',
  1185. 'search.im-in.space',
  1186. 'opnxng.com',
  1187. 'search.upinmars.com',
  1188. 'search.sapti.me',
  1189. 'freesearch.club',
  1190. 'xo.wtf',
  1191. 'www.gruble.de',
  1192. 'searx.tuxcloud.net',
  1193. 'baresearch.org',
  1194. 'searx.daetalytica.io',
  1195. 'etsi.me',
  1196. 'search.leptons.xyz',
  1197. 'search.rowie.at',
  1198. 'search.mdosch.de',
  1199. 'searx.catfluori.de',
  1200. 'searx.si',
  1201. 'searx.namejeff.xyz',
  1202. 'search.itstechtime.com',
  1203. 's.mble.dk',
  1204. 'searx.kutay.dev',
  1205. 'ooglester.com',
  1206. 'searx.ox2.fr',
  1207. 'searx.techsaviours.org',
  1208. 'searx.perennialte.ch',
  1209. 's.trung.fun',
  1210. 'search.in.projectsegfau.lt',
  1211. 'search.projectsegfau.lt',
  1212. 'darmarit.org',
  1213. 'searx.lunar.icu',
  1214. 'nyc1.sx.ggtyler.dev',
  1215. 'search.rhscz.eu',
  1216. 'paulgo.io',
  1217. 'northboot.xyz',
  1218. 'searx.zhenyapav.com',
  1219. 'searxng.ch',
  1220. 'copp.gg',
  1221. 'searx.sev.monster',
  1222. 'searx.oakleycord.dev',
  1223. 'searx.juancord.xyz',
  1224. 'searx.work',
  1225. 'search.ononoki.org',
  1226. 'search.demoniak.ch',
  1227. 'searx.cthd.icu',
  1228. 'searx.fmhy.net',
  1229. 'searx.headpat.exchange',
  1230. 'sex.finaltek.net',
  1231. 'search.gcomm.ch',
  1232. 'search.smnz.de',
  1233. 'searx.ankha.ac',
  1234. 'search.lvkaszus.pl',
  1235. 'searx.nobulart.com',
  1236. 'sx.t-1.org',
  1237. 'www.jabber-germany.de',
  1238. 'sx.catgirl.cloud'
  1239. ],
  1240. resultContainerSelectors: [
  1241. 'main#main_results'
  1242. // 'maindiv#main_results div#urls'
  1243. // 'div#sidebar div#infoboxes'
  1244. ]
  1245. },
  1246. 'startpage': {
  1247. hosts: ['startpage.com'],
  1248. resultContainerSelectors: [
  1249. 'div.show-results',
  1250. ]
  1251. },
  1252. 'brave': {
  1253. hosts: ['search.brave.com'],
  1254. resultContainerSelectors: [
  1255. 'main.main-column',
  1256. 'aside.sidebar'
  1257. ]
  1258. },
  1259. 'duckduckgo': {
  1260. hosts: ['duckduckgo.com'],
  1261. resultContainerSelectors: [
  1262. 'section[data-testid="mainline"][data-area="mainline"]',
  1263. 'section[data-testid="sidebar"][data-area="sidebar"]'
  1264. ]
  1265. },
  1266. 'ghostery': {
  1267. hosts: ['ghosterysearch.com'],
  1268. resultContainerSelectors: ['section.results']
  1269. },
  1270. 'presearch': {
  1271. hosts: ['presearch.com'],
  1272. resultContainerSelectors: ['div.w-full']
  1273. },
  1274. 'metager': {
  1275. hosts: [
  1276. 'metager.org',
  1277. 'metager.de'
  1278. ],
  1279. resultContainerSelectors: [
  1280. 'div#results',
  1281. 'div#additions-container'
  1282. ]
  1283. },
  1284. '4get': {
  1285. hosts: [
  1286. '4get.ca',
  1287. '4get.silly.computer',
  1288. '4get.plunked.party',
  1289. '4get.konakona.moe',
  1290. '4get.sijh.net',
  1291. '4get.hbubli.cc',
  1292. '4get.perennialte.ch',
  1293. '4get.zzls.xyz',
  1294. '4getus.zzls.xyz',
  1295. '4get.seitan-ayoub.lol',
  1296. '4get.dcs0.hu',
  1297. '4get.psily.garden',
  1298. '4get.lvkaszus.pl',
  1299. '4get.kizuki.lol'
  1300. ],
  1301. resultContainerSelectors: ['div#overflow']
  1302. },
  1303. 'librey': {
  1304. hosts: [
  1305. 'search.ahwx.org',
  1306. 'search2.ahwx.org',
  1307. 'search3.ahwx.org',
  1308. 'ly.owo.si',
  1309. 'librey.franklyflawless.org',
  1310. 'librey.org',
  1311. 'search.davidovski.xyz',
  1312. 'search.milivojevic.in.rs',
  1313. 'glass.prpl.wtf',
  1314. 'librex.uk.to',
  1315. 'librey.ix.tc',
  1316. 'search.funami.tech',
  1317. 'librex.retro-hax.net',
  1318. 'librex.nohost.network',
  1319. 'search.pabloferreiro.es',
  1320. 'librey.baczek.me',
  1321. 'lx.benike.me',
  1322. 'search.seitan-ayoub.lol',
  1323. 'librey.myroware.net',
  1324. 'librey.nezumi.party',
  1325. 'search.zeroish.xyz',
  1326. 'search.zeroish.xyz'
  1327. ],
  1328. resultContainerSelectors: [
  1329. 'div.text-result-container',
  1330. 'p.special-result-container'
  1331. ]
  1332. },
  1333. 'stract': {
  1334. hosts: ['stract.com'],
  1335. resultContainerSelectors: [
  1336. 'div.col-start-1',
  1337. 'div.row-start-2'
  1338. ]
  1339. },
  1340. 'etools': {
  1341. hosts: ['etools.ch'],
  1342. // resultContainerSelectors: ['table.result']
  1343. },
  1344. 'mojeek': {
  1345. hosts: ['mojeek.com'],
  1346. resultContainerSelectors: ['div.container.serp-results']
  1347. },
  1348. 'yep': {
  1349. hosts: ['yep.com']
  1350. },
  1351. 'torry': {
  1352. hosts: ['torry.io'],
  1353. resultContainerSelectors: ['div.searpListouterappend'],
  1354. attribute: 'data-target'
  1355. },
  1356. 'qwant': {
  1357. hosts: ['qwant.com'],
  1358. resultContainerSelectors: ['div._35zId']
  1359. },
  1360. 'ecosia': {
  1361. hosts: ['ecosia.org'],
  1362. resultContainerSelectors: [
  1363. 'section.mainline.web__mainline',
  1364. 'aside.sidebar.web__sidebar'
  1365. ]
  1366. },
  1367. 'oscobo': {
  1368. hosts: ['search.becovi.com'],
  1369. resultContainerSelectors: ['div#results.col']
  1370. },
  1371. 'good': {
  1372. hosts: ['good-search.org'],
  1373. resultContainerSelectors: [
  1374. 'div.grid__item.two-thirds',
  1375. 'div.grid__item.one-third'
  1376. ]
  1377. },
  1378. 'alltheinternet': {
  1379. hosts: ['alltheinternet.com']
  1380. },
  1381. 'aol': {
  1382. hosts: ['search.aol.com'],
  1383. resultContainerSelectors: ['div#results div#cols']
  1384. },
  1385. 'onesearch': {
  1386. hosts: ['onesearch.com'],
  1387. resultContainerSelectors: ['div#results div#cols']
  1388. },
  1389. 'info': {
  1390. hosts: ['info.com'],
  1391. resultContainerSelectors: ['div.layout__body']
  1392. },
  1393. 'oceanhero': {
  1394. hosts: ['oceanhero.today']
  1395. },
  1396. 'swisscows': {
  1397. hosts: ['swisscows.com'],
  1398. resultContainerSelectors: ['section.container.page-results']
  1399. },
  1400. 'lilo': {
  1401. hosts: ['search.lilo.org'],
  1402. resultContainerSelectors: ['div.container#content']
  1403. },
  1404. 'entireweb': {
  1405. hosts: ['search.entireweb.com'],
  1406. resultContainerSelectors: ['div.container.search-container']
  1407. },
  1408. 'gmx': {
  1409. hosts: ['search.gmx.com']
  1410. },
  1411. 'youcare': {
  1412. hosts: ['youcare.world']
  1413. },
  1414. 'lycos': {
  1415. hosts: ['search.lycos.com'],
  1416. resultContainerSelectors: ['div.content.con-search']
  1417. },
  1418. 'alohafind': {
  1419. hosts: ['alohafind.com'],
  1420. resultContainerSelectors: ['section.layout']
  1421. },
  1422. 'spot': {
  1423. hosts: ['spot.ecloud.global'],
  1424. resultContainerSelectors: ['div.container.contents']
  1425. },
  1426. 'nona': {
  1427. hosts: ['nona.de'],
  1428. resultContainerSelectors: ['main.search-results div.container']
  1429. },
  1430. 'exalead': {
  1431. hosts: ['exalead.com'],
  1432. resultContainerSelectors: ['ul.media-list']
  1433. },
  1434. 'seznam': {
  1435. hosts: ['search.seznam.cz'],
  1436. resultContainerSelectors: ['div.PageWrapper.SearchPage#searchpage-root'],
  1437. },
  1438. 'gibiru': {
  1439. hosts: ['gibiru.com'],
  1440. resultContainerSelectors: ['div.container#web-results'],
  1441. },
  1442. 'lukol': {
  1443. hosts: ['lukol.com']
  1444. },
  1445. 'givewater': {
  1446. hosts: ['search.givewater.com'],
  1447. resultContainerSelectors: ['div.mainline-results']
  1448. },
  1449. 'excite': {
  1450. hosts: ['results.excite.com'],
  1451. resultContainerSelectors: ['div.mainline-results']
  1452. },
  1453. 'webcrawler': {
  1454. hosts: ['webcrawler.com'],
  1455. resultContainerSelectors: ['div.web-bing'],
  1456. },
  1457. 'metacrawler': {
  1458. hosts: ['metacrawler.com'],
  1459. resultContainerSelectors: ['div.web-bing'],
  1460. },
  1461. 'dogpile': {
  1462. hosts: ['dogpile.com'],
  1463. resultContainerSelectors: ['div.web-bing'],
  1464. }
  1465. // ... more search engines
  1466. };
  1467.  
  1468. // Function to modify URLs and optionally text
  1469. const modifyUrls = (engine, observer, resultContainer, engineInfo) => {
  1470. try {
  1471. const selectors = selectorRules[engine];
  1472. if (selectors) {
  1473. // Disconnect the observer to prevent recursive triggering
  1474. observer.disconnect();
  1475.  
  1476. // Modify results
  1477. selectors.forEach(rule => {
  1478. if (rule.selector) {
  1479. processElements(rule.selector, rule, engineInfo);
  1480. } else if (rule.parentSelector) {
  1481. processParentElements(rule.parentSelector, rule, engineInfo);
  1482. }
  1483. });
  1484.  
  1485. // Reconnect the observer after DOM modifications are done
  1486. observer.observe(resultContainer, { childList: true, subtree: true });
  1487. }
  1488. } catch (error) {
  1489. console.error("URL Modification Error: ", error);
  1490. }
  1491. };
  1492.  
  1493. // Function to process elements specified by `selector`
  1494. const processElements = (selector, rule, engineInfo) => {
  1495. const elements = document.querySelectorAll(selector);
  1496. const additionalAttribute = engineInfo.attribute; // Get the additional attribute if specified
  1497. if (elements.length > 0) {
  1498. elements.forEach(element => {
  1499. for (let i = 0; i < urlModificationRules.length; i++) {
  1500. try {
  1501. const urlRule = urlModificationRules[i];
  1502. let urlToModify = additionalAttribute ? element.getAttribute(additionalAttribute) : element.href;
  1503. urlToModify = decodeURIComponent(urlToModify);
  1504. // update attribute
  1505. if (urlToModify && urlRule.matchRegex.test(urlToModify)) {
  1506. // Generate redirected URL
  1507. let newUrl = urlToModify.replace(urlRule.matchRegex, urlRule.replaceWith);
  1508. newUrl = rule.useTopLevelDomain ? extractTopLevelDomain(newUrl) : newUrl;
  1509. if (element.href) {
  1510. element.href = newUrl;
  1511. } else if (additionalAttribute) {
  1512. element.setAttribute(additionalAttribute, newUrl);
  1513. }
  1514. updateTextContent(element, rule, removeTailingSlash(removeParameters(newUrl)));
  1515. break;
  1516. }
  1517. } catch (error) {
  1518. console.error("Update Link/Text Error: ", error);
  1519. }
  1520. }
  1521. });
  1522. }
  1523. };
  1524.  
  1525. // Function to process elements specified by `parentSelector`
  1526. const processParentElements = (selector, rule, engineInfo) => {
  1527. const elements = document.querySelectorAll(selector);
  1528. const additionalAttribute = engineInfo.attribute; // Get the additional attribute if specified
  1529. if (elements.length > 0) {
  1530. elements.forEach(element => {
  1531. const linkElement = element.querySelector(rule.linkNodeSelector);
  1532. const textElement = element.querySelector(rule.textNodeSelector);
  1533.  
  1534. for (let i = 0; i < urlModificationRules.length; i++) {
  1535. try {
  1536. const urlRule = urlModificationRules[i];
  1537. let urlToModify = additionalAttribute ? element.getAttribute(additionalAttribute) : linkElement.href;
  1538. urlToModify = decodeURIComponent(urlToModify);
  1539. // update attribute
  1540. if (urlToModify && urlRule.matchRegex.test(urlToModify)) {
  1541. // Generate redirected URL
  1542. let newUrl = urlToModify.replace(urlRule.matchRegex, urlRule.replaceWith);
  1543. newUrl = rule.useTopLevelDomain ? extractTopLevelDomain(newUrl) : newUrl;
  1544. if (linkElement.href) {
  1545. linkElement.href = newUrl;
  1546. } else if (additionalAttribute) {
  1547. linkElement.setAttribute(additionalAttribute, newUrl);
  1548. }
  1549. updateTextContent(textElement, rule, removeTailingSlash(removeParameters(newUrl)));
  1550. break;
  1551. }
  1552. } catch (error) {
  1553. console.error("Update Link/Text Error: ", error);
  1554. }
  1555. }
  1556. });
  1557. }
  1558. };
  1559.  
  1560. // Function to update text content (displayed URL)
  1561. const updateTextContent = (element, rule, newUrl) => {
  1562. if (rule.updateChildText || rule.updateTextWithoutOverwrite || rule.updateTextByOverwrite) {
  1563. try {
  1564. if (rule.multiElementsForUrlDisplay) {
  1565. updateMultiElementContent(element, rule, newUrl);
  1566. } else {
  1567. const targetElement = rule.childSelector ? element.querySelector(rule.childSelector) : element;
  1568. updateSingleElementText(targetElement, rule, newUrl);
  1569. }
  1570. } catch (error) {
  1571. console.error("Update Displayed URL Error: ", error);
  1572. }
  1573. }
  1574. };
  1575.  
  1576. // Function to update text for multi elements (e.g. DuckDuckGo, Brave)
  1577. const updateMultiElementContent = (targetElement, rule, newUrl) => {
  1578. if (!targetElement) {
  1579. console.error("Target DOM Element not found for Multi-Element Text update!");
  1580. return;
  1581. }
  1582. // Remove the "https://" protocol if containProtocol is false
  1583. newUrl = rule.containProtocol ? newUrl : removeProtocol(newUrl);
  1584. let formattedUrl = breadCumbFormat(newUrl, rule.containProtocol);
  1585. let urlParts = formattedUrl.split(' › ');
  1586.  
  1587. const spans = targetElement.querySelectorAll(rule.childSelector)
  1588.  
  1589. switch (rule.multiElementsForUrlDisplay) {
  1590. case 1:
  1591. parallelElements(urlParts, spans);
  1592. break;
  1593. case 2:
  1594. mixedElements(urlParts, spans, targetElement);
  1595. break;
  1596. case 3:
  1597. mixedElementsWithoutClear(urlParts, spans, targetElement);
  1598. break;
  1599. case 4:
  1600. fourGetSearchElements(newUrl, spans, targetElement);
  1601. }
  1602. };
  1603.  
  1604. // Case where URL parts are scattered into parallel elements
  1605. const parallelElements = (urlParts, elements) => {
  1606. if (elements && elements.length >= 2) {
  1607. elements.forEach(clearElementContent);
  1608. elements[0].textContent = urlParts[0]; // Update the first part
  1609. elements[1].textContent = ' › ' + urlParts.slice(1).join(' › '); // Update the second part
  1610. } else {
  1611. console.error("Script: Expected structure not found for Multi-Element (parallel elements) URL update!");
  1612. }
  1613. };
  1614.  
  1615. // Case where URL parts are scattered into non-parallel elements
  1616. const mixedElements = (urlParts, elements, parent) => {
  1617. if (elements && elements.length >= 1) {
  1618. elements.forEach(clearElementContent);
  1619. updateTextWithoutOverwriteChildNodes(parent, urlParts[0]); // Update the first part
  1620. elements[0].textContent = ' › ' + urlParts.slice(1).join(' › '); // Update the second part
  1621. } else {
  1622. console.error("Script: Expected structure not found for Multi-Element (mixed elements) URL update!");
  1623. }
  1624. };
  1625.  
  1626. // Same as case 2, but update elements without clearing their original contents
  1627. const mixedElementsWithoutClear = (urlParts, elements, parent) => {
  1628. if (elements && elements.length >= 1) {
  1629. updateTextWithoutOverwriteChildNodes(parent, urlParts[0]); // Update the first part
  1630. elements[0].textContent = ' › ' + urlParts.slice(1).join(' › '); // Update the second part
  1631. } else {
  1632. console.error("Script: Expected structure not found for Multi-Element (mixed elements with clear) URL update!");
  1633. }
  1634. };
  1635.  
  1636. // Case where search engine is 4get
  1637. const fourGetSearchElements = (newUrl, elements, parent) => {
  1638. // Convert NodeList to Array if necessary
  1639. const elementsArray = Array.isArray(elements) ? elements : Array.from(elements);
  1640.  
  1641. // Split the new URL into parts
  1642. const urlParts = splitUrlIntoParts(newUrl);
  1643.  
  1644. // Ensure the elements array length matches the number of URL parts
  1645. matchElementNumWithPartsFor4Get(urlParts, elementsArray, parent);
  1646.  
  1647. let prevCombinedLink = '';
  1648. // Update or add new elements based on the urlParts
  1649. urlParts.forEach((part, index) => {
  1650. let element = elementsArray[index];
  1651. if (!element) {
  1652. element = createPartElementFor4Get(parent);
  1653. elementsArray.push(element);
  1654. }
  1655. prevCombinedLink += part + '/';
  1656. element.href = prevCombinedLink;
  1657. element.textContent = part;
  1658. // Handle separators
  1659. if (index < urlParts.length - 1 && (!element.nextSibling || element.nextSibling.className !== 'separator')) {
  1660. let separator = createSeparatorElementFor4Get();
  1661. parent.insertBefore(separator, element.nextSibling);
  1662. }
  1663. });
  1664.  
  1665. // Remove any extra elements if the new URL has fewer parts than existing elements
  1666. while (elementsArray.length > urlParts.length) {
  1667. const elementToRemove = elementsArray.pop();
  1668. const separator = elementToRemove.nextSibling;
  1669. if (separator && separator.className === 'separator') {
  1670. separator.remove();
  1671. }
  1672. elementToRemove.remove();
  1673. }
  1674. };
  1675.  
  1676. // 4get specific fucntion
  1677. // Ensure the elements array length matches the number of URL parts, if not, add new < a > elements
  1678. const matchElementNumWithPartsFor4Get = (urlParts, elements, parent) => {
  1679. while (elements.length < urlParts.length) {
  1680. const newElement = createPartElementFor4Get(parent);
  1681. parent.appendChild(newElement);
  1682. elements.push(newElement);
  1683. }
  1684. };
  1685.  
  1686. // 4get specific fucntion
  1687. // Create a new <a> element that represents a URL component
  1688. const createPartElementFor4Get = () => {
  1689. const element = document.createElement('a');
  1690. element.className = 'part';
  1691. element.setAttribute('rel', 'noreferrer nofollow');
  1692. element.setAttribute('tabindex', '-1');
  1693. return element;
  1694. };
  1695.  
  1696. // 4get specific fucntion
  1697. // Create a new <span> element that represents a URL separator '/'
  1698. const createSeparatorElementFor4Get = () => {
  1699. const separator = document.createElement('span');
  1700. separator.className = 'separator';
  1701. return separator;
  1702. };
  1703.  
  1704. // Function to update text for a single element
  1705. const updateSingleElementText = (targetElement, rule, newUrl) => {
  1706. if (!targetElement) {
  1707. console.error("Target DOM Element not found for Single-Element Text update!");
  1708. return;
  1709. }
  1710. let formattedUrl = '';
  1711. switch (rule.urlDisplayMethod) {
  1712. case 1:
  1713. formattedUrl = breadCumbFormat(newUrl, rule.containProtocol);
  1714. break;
  1715. case 2:
  1716. formattedUrl = newUrl; // Full URL with protocol
  1717. break;
  1718. case 3:
  1719. formattedUrl = decodeURIComponent(removeProtocol(newUrl)); // Full URL without protocol
  1720. break;
  1721. }
  1722. if (rule.updateTextWithoutOverwrite) {
  1723. updateTextWithoutOverwriteChildNodes(targetElement, formattedUrl);
  1724. } else if (rule.updateTextByOverwrite) {
  1725. updateTextByOverwriteEverything(targetElement, formattedUrl);
  1726. } else {
  1727. targetElement.textContent = formattedUrl;
  1728. }
  1729. };
  1730.  
  1731. // Format URLs into Breadcrumb style, while leaving 'https://' intact
  1732. const breadCumbFormat = (url, containProtocol) => {
  1733. if (!containProtocol) {
  1734. url = removeProtocol(url);
  1735. }
  1736.  
  1737. const urlParts = splitUrlIntoParts(url);
  1738.  
  1739. // Join the URL parts with ' › '
  1740. const joinedUrl = urlParts.join(' › ');
  1741.  
  1742. // Decode the URL to convert encoded characters to their original form
  1743. return decodeURIComponent(joinedUrl);
  1744. };
  1745.  
  1746. // Function to update only the text node within an element, leave the child elements, if exist, intact
  1747. const updateTextWithoutOverwriteChildNodes = (element, newContent) => {
  1748. let currentIndex = 0;
  1749. const indexObject = { currentIndex };
  1750. collectNodes(element, node => {
  1751. if (currentIndex >= newContent.length) return; // Stop if we've used all the new content
  1752. replaceTextContent(node, oldText => replaceTextBasedOnIndex(oldText, newContent, indexObject));
  1753. });
  1754. };
  1755.  
  1756. // Function to collect all text nodes within an element
  1757. const collectNodes = (node, callback) => {
  1758. // View these elements as text node
  1759. const elementsToIncludeAsText = ['B'];
  1760. // Do not view these elements as text node
  1761. const elementsToExcludeAsText = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'DIV', 'P'];
  1762.  
  1763. if (node.nodeType === Node.TEXT_NODE) {
  1764. callback(node);
  1765. } else if (node.nodeType === Node.ELEMENT_NODE && elementsToIncludeAsText.includes(node.tagName.toUpperCase())) {
  1766. callback(node);
  1767. } else if (node.nodeType === Node.ELEMENT_NODE && !elementsToExcludeAsText.includes(node.tagName.toUpperCase())) {
  1768. node.childNodes.forEach(child => collectNodes(child, callback));
  1769. }
  1770. };
  1771.  
  1772. // Function to replace the text content of a node
  1773. const replaceTextContent = (node, textUpdater) => {
  1774. if (node.nodeType === Node.TEXT_NODE) {
  1775. node.nodeValue = textUpdater(node.nodeValue);
  1776. } else if (node.innerHTML) { // For elements treated as text, like <b>
  1777. node.innerHTML = textUpdater(node.innerHTML);
  1778. }
  1779. };
  1780.  
  1781. // Function to replace text based on index
  1782. const replaceTextBasedOnIndex = (oldText, newContent, indexObject) => {
  1783. const newText = newContent.slice(indexObject.currentIndex, indexObject.currentIndex + oldText.length);
  1784. indexObject.currentIndex += oldText.length;
  1785. return newText;
  1786. };
  1787.  
  1788. // Function to update the content by overwriting everything
  1789. const updateTextByOverwriteEverything = (element, newContent) => {
  1790. clearElementContent(element);
  1791. element.textContent = newContent;
  1792. };
  1793.  
  1794. // Split URL into components, while preserving 'https://' in all occurrences
  1795. const splitUrlIntoParts = (url) => {
  1796. // Temporary replace 'https://' with a placeholder that is unlikely to be part of any URL
  1797. const placeholder = "HTTPS_PLACEHOLDER";
  1798. let tempUrl = url.replace(/https:\/\//g, placeholder);
  1799.  
  1800. // Split the URL by '/'
  1801. let parts = tempUrl.split('/');
  1802.  
  1803. // Replace the placeholder back with 'https://'
  1804. parts = parts.map(part => part.replace(new RegExp(placeholder, 'g'), 'https://'));
  1805.  
  1806. // Filter out any empty strings that may have resulted from the split
  1807. parts = parts.filter(part => part !== '');
  1808.  
  1809. // Ensure the first part always starts with 'https://', adjusting if necessary
  1810. if (parts.length > 0 && !parts[0].startsWith('https://')) {
  1811. parts[0] = 'https://' + parts[0];
  1812. }
  1813.  
  1814. return parts;
  1815. };
  1816.  
  1817. // Extract the top level domain from URL link
  1818. const extractTopLevelDomain = (url) => {
  1819. const parsedUrl = new URL(url);
  1820. return `${parsedUrl.protocol}//${parsedUrl.hostname}/`;
  1821. };
  1822.  
  1823. // Remove 'https://' from the URL link
  1824. const removeProtocol = (url) => {
  1825. return url.replace(/^https?:\/\//, '');
  1826. };
  1827.  
  1828. // Remove parameters (the part behind ?) in the URL link
  1829. const removeParameters = (url) => {
  1830. return url.split('?')[0];
  1831. };
  1832.  
  1833. // Remove tailing slash '/'
  1834. const removeTailingSlash = (url) => {
  1835. // Check if the URL ends with a slash and remove it if present
  1836. return url.endsWith('/') ? url.slice(0, -1) : url;
  1837. };
  1838.  
  1839. // Function to clear existing content of an element
  1840. const clearElementContent = (element) => {
  1841. element.textContent = '';
  1842. };
  1843.  
  1844. // Improved function to determine the search engine
  1845. const getSearchEngineInfo = () => {
  1846. try {
  1847. const host = window.location.host;
  1848. for (const engine in searchEngines) {
  1849. if (searchEngines[engine].hosts.some(instanceHost => host.includes(instanceHost))) {
  1850. // Default to 'body' if not specified
  1851. const selectors = searchEngines[engine].resultContainerSelectors || ['body'];
  1852. // Get the attribute if specified
  1853. const attribute = searchEngines[engine].attribute;
  1854. return {
  1855. engine,
  1856. selectors: selectors,
  1857. attribute: attribute
  1858. };
  1859. }
  1860. }
  1861. } catch (error) {
  1862. console.error("Error determining search engine: ", error);
  1863. }
  1864. };
  1865.  
  1866. // Function to observe and execute the URL modifier script
  1867. const observeToExecute = (engine, selector, engineInfo) => {
  1868. const resultContainers = document.querySelectorAll(selector);
  1869. if (resultContainers) {
  1870. resultContainers.forEach(resultContainer => {
  1871. // Observe changes in each result container
  1872. const observer = new MutationObserver(() => modifyUrls(engine, observer, resultContainer, engineInfo));
  1873. observer.observe(resultContainer, { childList: true, subtree: true });
  1874. modifyUrls(engine, observer, resultContainer, engineInfo);
  1875. });
  1876. }
  1877. };
  1878.  
  1879. // Run the script for the current search engine
  1880. try {
  1881. const engineInfo = getSearchEngineInfo();
  1882. if (engineInfo) {
  1883. engineInfo.selectors.forEach(containerSelector => {
  1884. observeToExecute(engineInfo.engine, containerSelector, engineInfo);
  1885. });
  1886. }
  1887. } catch (error) {
  1888. console.error("Error executing URL Modifier Script: ", error);
  1889. }
  1890. })();