URL Modifier for Search Engines

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

当前为 2024-01-28 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name URL Modifier for Search Engines
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.3.2
  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.  
  10. // @match *://yandex.com/search/?*
  11. // @match *://yandex.ru/search/?*
  12.  
  13. // @match *://search.disroot.org/search*
  14. // @match *://searx.tiekoetter.com/search*
  15. // @match *://search.bus-hit.me/search*
  16. // @match *://search.inetol.net/search*
  17. // @match *://priv.au/search*
  18. // @match *://searx.be/search*
  19. // @match *://searxng.site/search*
  20. // @match *://search.hbubli.cc/search*
  21. // @match *://search.im-in.space/search*
  22. // @match *://opnxng.com/search*
  23. // @match *://search.upinmars.com/search*
  24. // @match *://search.sapti.me/search*
  25. // @match *://freesearch.club/search*
  26. // @match *://xo.wtf/search*
  27. // @match *://www.gruble.de/search*
  28. // @match *://searx.tuxcloud.net/search*
  29. // @match *://baresearch.org/search*
  30. // @match *://searx.daetalytica.io/search*
  31. // @match *://etsi.me/search*
  32. // @match *://search.leptons.xyz/search*
  33. // @match *://search.rowie.at/search*
  34. // @match *://search.mdosch.de/search*
  35. // @match *://searx.catfluori.de/search*
  36. // @match *://searx.si/search*
  37. // @match *://searx.namejeff.xyz/search*
  38. // @match *://search.itstechtime.com/search*
  39. // @match *://s.mble.dk/search*
  40. // @match *://searx.kutay.dev/search*
  41. // @match *://ooglester.com/search*
  42. // @match *://searx.ox2.fr/search*
  43. // @match *://searx.techsaviours.org/search*
  44. // @match *://searx.perennialte.ch/search*
  45. // @match *://s.trung.fun/search*
  46. // @match *://search.in.projectsegfau.lt/search*
  47. // @match *://search.projectsegfau.lt/search*
  48. // @match *://darmarit.org/searx/search*
  49. // @match *://searx.lunar.icu/search*
  50. // @match *://nyc1.sx.ggtyler.dev/search*
  51. // @match *://search.rhscz.eu/search*
  52. // @match *://paulgo.io/search*
  53. // @match *://northboot.xyz/search*
  54. // @match *://searx.zhenyapav.com/search*
  55. // @match *://searxng.ch/search*
  56. // @match *://copp.gg/search*
  57. // @match *://searx.sev.monster/search*
  58. // @match *://searx.oakleycord.dev/search*
  59. // @match *://searx.juancord.xyz/search*
  60. // @match *://searx.work/search*
  61. // @match *://search.ononoki.org/search*
  62. // @match *://search.demoniak.ch/search*
  63. // @match *://searx.cthd.icu/search*
  64. // @match *://searx.fmhy.net/search*
  65. // @match *://searx.headpat.exchange/search*
  66. // @match *://sex.finaltek.net/search*
  67. // @match *://search.gcomm.ch/search*
  68. // @match *://search.smnz.de/search*
  69. // @match *://searx.ankha.ac/search*
  70. // @match *://search.lvkaszus.pl/search*
  71. // @match *://searx.nobulart.com/search*
  72. // @match *://sx.t-1.org/search*
  73. // @match *://www.jabber-germany.de/searx/search*
  74. // @match *://sx.catgirl.cloud/search*
  75. // @match *://sx.vern.cc/searxng/search*
  76.  
  77. // @match *://www.startpage.com/search*
  78. // @match *://www.startpage.com/sp/search*
  79.  
  80. // @match *://search.brave.com/search*
  81.  
  82. // @match *://duckduckgo.com
  83. // @match *://duckduckgo.com/?*
  84.  
  85. // @match *://www.qwant.com/?*
  86. // @match *://www.ecosia.org/search?*
  87. // @match *://presearch.com/search?*
  88. // @match *://swisscows.com/*/web?*
  89.  
  90. // @match *://metager.org/meta/*
  91. // @match *://metager.de/meta/*
  92.  
  93. // @match *://4get.ca/web?*
  94. // @match *://4get.silly.computer/web?*
  95. // @match *://4get.plunked.party/web?*
  96. // @match *://4get.konakona.moe/web?*
  97. // @match *://4get.sijh.net/web?*
  98. // @match *://4get.hbubli.cc/web?*
  99. // @match *://4get.perennialte.ch/web?*
  100. // @match *://4get.zzls.xyz/web?*
  101. // @match *://4getus.zzls.xyz/web?*
  102. // @match *://4get.seitan-ayoub.lol/web?*
  103. // @match *://4get.dcs0.hu/web?*
  104. // @match *://4get.psily.garden/web?*
  105. // @match *://4get.lvkaszus.pl/web?*
  106. // @match *://4get.kizuki.lol/web?*
  107.  
  108. // @match *://search.ahwx.org/search.php?*
  109. // @match *://search2.ahwx.org/search.php?*
  110. // @match *://search3.ahwx.org/search.php?*
  111. // @match *://ly.owo.si/search.php?*
  112. // @match *://librey.franklyflawless.org/search.php?*
  113. // @match *://librey.org/search.php?*
  114. // @match *://search.davidovski.xyz/search.php?*
  115. // @match *://search.milivojevic.in.rs/search.php?*
  116. // @match *://glass.prpl.wtf/search.php?*
  117. // @match *://librex.uk.to/search.php?*
  118. // @match *://librey.ix.tc/search.php?*
  119. // @match *://search.funami.tech/search.php?*
  120. // @match *://librex.retro-hax.net/search.php?*
  121. // @match *://librex.nohost.network/search.php?*
  122. // @match *://search.pabloferreiro.es/search.php?*
  123. // @match *://librey.baczek.me/search.php?*
  124. // @match *://lx.benike.me/search.php?*
  125. // @match *://search.seitan-ayoub.lol/search.php?*
  126. // @match *://librey.myroware.net/search.php?*
  127. // @match *://librey.nezumi.party/search.php?*
  128. // @match *://search.zeroish.xyz/search.php?*
  129. // @match *://search.zeroish.xyz/search.php?*
  130.  
  131. // @match *://stract.com/search?*
  132.  
  133. // @match *://www.etools.ch/searchSubmit.do*
  134. // @match *://www.etools.ch/mobileSearch.do*
  135.  
  136. // @match *://search.lilo.org/?*
  137. // @match *://search.entireweb.com/search?*
  138. // @match *://www.mojeek.com/search?*
  139. // @match *://yep.com/web?*
  140. // @match *://www.torry.io/search*
  141.  
  142. // @grant none
  143. // @run-at document-end
  144. // @license GPL-2.0-only
  145. // ==/UserScript==
  146.  
  147. (function() {
  148. 'use strict';
  149.  
  150. // Define URL modification rules with precompiled regex
  151. const urlModificationRules = [
  152. {
  153. matchRegex: new RegExp(/^https?:\/\/(?:old|www)\.reddit\.com\/((?:r|u)\/.*)/),
  154. replaceWith: 'https://safereddit.com/$1'
  155. },
  156. {
  157. matchRegex: new RegExp(/^https?:\/\/www\.quora\.com\/((?=.*-)[\w-]+$|profile\/.*)/),
  158. replaceWith: 'https://quetre.iket.me/$1'
  159. },
  160. {
  161. matchRegex: new RegExp(/^https?:\/\/twitter\.com\/([A-Za-z_][\w]+)(\/status\/(\d+))?.*/),
  162. replaceWith: 'https://nitter.net/$1$2'
  163. },
  164. {
  165. matchRegex: new RegExp(/^https?:\/\/stackoverflow\.com(\/questions\/\d+\/[\w-]+)/),
  166. replaceWith: 'https://ao.vern.cc$1'
  167. },
  168. {
  169. matchRegex: new RegExp(/^https?:\/\/((?!test)[a-z]+)\.?m?\.wikipedia\.org\/(?:[a-z]+|wiki)\/(?!Special:Search)(.*)/),
  170. replaceWith: 'https://www.wikiwand.com/$1/$2'
  171. },
  172. {
  173. matchRegex: new RegExp(/^https?:\/\/zh\.?m?\.wikipedia\.org\/(?:zh-hans|wiki)\/(.*)/),
  174. replaceWith: 'https://www.wikiwand.com/zh-hans/$1'
  175. },
  176. {
  177. matchRegex: new RegExp(/^https?:\/\/wikipedia\.org\/(?:[a-z]+|wiki)\/(?!Special:Search)(.*)/),
  178. replaceWith: 'https://www.wikiwand.com/en/$1'
  179. },
  180. {
  181. matchRegex: new RegExp(/^https?:\/\/((?:(?:.*?)?medium|towardsdatascience|betterprogramming|.*?plainenglish|.*?gitconnected|aninjusticemag|betterhumans|uxdesign|uxplanet)\.\w+\/(?=.*-)(?:[\w\/-]+|[\w@.]+\/[\w-]+))(?:\?source=.*)?/),
  182. replaceWith: 'https://freedium.cfd/https://$1'
  183. },
  184. {
  185. matchRegex: new RegExp(/^https?:\/\/(?:www\.|m\.)?youtube\.com\/((?:@|watch\?|playlist\?|channel\/|user\/|shorts\/).*)/),
  186. replaceWith: 'https://vid.puffyan.us/$1'
  187. },
  188. {
  189. matchRegex: new RegExp(/^https?:\/\/music\.youtube\.com\/((?:playlist\?|watch\?|channel\/|browse\/).*)/),
  190. replaceWith: 'https://hyperpipe.surge.sh/$1'
  191. },
  192. {
  193. matchRegex: new RegExp(/^https?:\/\/www\.twitch\.tv\/(\w+)$/),
  194. replaceWith: 'https://ttv.vern.cc/$1'
  195. },
  196. {
  197. matchRegex: new RegExp(/^https?:\/\/(?:m|www)\.imdb\.com(.*)/),
  198. replaceWith: 'https://ld.vern.cc$1'
  199. },
  200. {
  201. matchRegex: new RegExp(/^https?:\/\/www\.goodreads\.com\/((?:(?:[a-z]+\/)?book\/show|work\/quotes|series|author\/show)\/[\w.-]+)/),
  202. replaceWith: 'https://bl.vern.cc/$1'
  203. },
  204. {
  205. // only support English Fandom sites
  206. matchRegex: new RegExp(/^https?:\/\/((?!www|community).*?)\.fandom\.com\/wiki\/(.*)/),
  207. replaceWith: 'https://antifandom.com/$1/wiki/$2'
  208. },
  209. {
  210. matchRegex: new RegExp(/^https?:\/\/www\.urbandictionary\.com\/(define\.php\?term=.*)/),
  211. replaceWith: 'https://rd.vern.cc/$1'
  212. },
  213. {
  214. matchRegex: new RegExp(/^https?:\/\/github\.ink(.*)/),
  215. replaceWith: 'https://github.com$1'
  216. },
  217. {
  218. matchRegex: new RegExp(/^https?:\/\/www\.reuters\.com\/((?=.*\/)(?=.*-).*)/),
  219. replaceWith: 'https://nu.vern.cc/$1'
  220. },
  221. {
  222. matchRegex: new RegExp(/^https?:\/\/(www\.ft\.com\/content\/[\w-]+)/),
  223. replaceWith: 'https://archive.today/https://$1'
  224. },
  225. {
  226. matchRegex: new RegExp(/^https?:\/\/(www\.bloomberg\.com\/(?:(?:[a-z]+\/)?news|opinion)\/[\w\/-]+).*/),
  227. replaceWith: 'https://archive.today/https://$1'
  228. },
  229. {
  230. matchRegex: new RegExp(/^https?:\/\/www\.npr\.org\/(?:\d{4}\/\d{2}\/\d{2}|sections)\/(?:[A-Za-z-]+\/\d{4}\/\d{2}\/\d{2}\/)?(\d+)\/.*/),
  231. replaceWith: 'https://text.npr.org/$1'
  232. },
  233. {
  234. matchRegex: new RegExp(/^https?:\/\/news\.ycombinator\.com\/item\?id=(\d+)/),
  235. replaceWith: 'https://www.hckrnws.com/stories/$1'
  236. },
  237. {
  238. matchRegex: new RegExp(/^https?:\/\/(?:[a-z]+)\.slashdot\.org(.*)/),
  239. replaceWith: 'https://slashdot.org$1'
  240. },
  241. {
  242. matchRegex: new RegExp(/^https?:\/\/(?:(?:.*)arxiv\.org\/pdf|arxiv-export-lb\.library\.cornell\.edu\/(?:pdf|abs))\/(\d{4}\.\d{4,5}(v\d)?)(?:.*)/),
  243. replaceWith: 'https://arxiv.org/abs/$1'
  244. },
  245. {
  246. matchRegex: new RegExp(/^https?:\/\/(ieeexplore\.ieee\.org\/document\/\d+)\//),
  247. replaceWith: 'https://$1'
  248. },
  249. {
  250. matchRegex: new RegExp(/^https?:\/\/www\.snopes\.com(.*)/),
  251. replaceWith: 'https://sd.vern.cc$1'
  252. },
  253. {
  254. matchRegex: new RegExp(/^https?:\/\/www\.instructables\.com\/(.*)/),
  255. replaceWith: 'https://ds.vern.cc/$1'
  256. },
  257. {
  258. matchRegex: new RegExp(/^https?:\/\/genius\.com\/((?=[\w-]+lyrics|search\?q=).*)/),
  259. replaceWith: 'https://dm.vern.cc/$1'
  260. },
  261. {
  262. matchRegex: new RegExp(/^https?:\/\/(.*?)\.bandcamp\.com\//),
  263. replaceWith: 'https://tn.vern.cc/artist.php?name=$1'
  264. },
  265. {
  266. matchRegex: new RegExp(/^https?:\/\/(.*?)\.bandcamp\.com\/(.*?)\/(.*)/),
  267. replaceWith: 'https://tn.vern.cc/release.php?artist=$1&type=$2&name=$3'
  268. },
  269. {
  270. matchRegex: new RegExp(/^https?:\/\/bandcamp\.com\/search\?q=(.*)/),
  271. replaceWith: 'https://tn.vern.cc/search.php?query=$1'
  272. },
  273. {
  274. matchRegex: new RegExp(/^https?:\/\/f4\.bcbits\.com\/img\/(.*)/),
  275. replaceWith: 'https://tn.vern.cc/image.php?file=$1'
  276. },
  277. {
  278. matchRegex: new RegExp(/^https?:\/\/t4\.bcbits\.com\/stream\/(.*?)\/(.*?)\/(.*?)\?token=(.*)/),
  279. replaceWith: 'https://tn.vern.cc/audio.php?directory=$1&format=$2&file=$3&token=$4'
  280. },
  281. {
  282. matchRegex: new RegExp(/^https?:\/\/(?:\w+\.)?imgur.com\/((?:a\/)?(?!gallery)[\w.]+)/),
  283. replaceWith: 'https://rimgo.totaldarkness.net/$1'
  284. },
  285. {
  286. matchRegex: new RegExp(/^https?:\/\/www\.pixiv\.net\/(?:[a-z]+\/)?(artworks\/\d+|tags\/\w+|users\/\d+).*/),
  287. replaceWith: 'https://pixivfe.exozy.me/$1'
  288. },
  289. {
  290. matchRegex: new RegExp(/^https?:\/\/knowyourmeme\.com\/(.*)/),
  291. replaceWith: 'https://mm.vern.cc/$1'
  292. },
  293. {
  294. matchRegex: new RegExp(/^https?:\/\/tenor\.com\/((?:view|search)\/.*)/),
  295. replaceWith: 'https://sp.vern.cc/$1'
  296. },
  297. {
  298. matchRegex: new RegExp(/^https?:\/\/(?:\w+\.)?ifunny\.co\/(picture\/.*)/),
  299. replaceWith: 'https://uf.vern.cc/$1'
  300. },
  301. // Add more rules here as needed
  302. ];
  303.  
  304. // Define enhanced selector rules for each search engine
  305. const selectorRules = {
  306. 'google': [
  307. {
  308. selector: 'div.MjjYud div.yuRUbf div span a',
  309. childSelector: 'div.byrV5b cite',
  310. updateChildText: true,
  311. containProtocol: true,
  312. displayMethod: 1
  313. },
  314. {
  315. // selector for sub-results
  316. selector: 'div.MjjYud div.HiHjCd a'
  317. },
  318. {
  319. // selector for sidebar links
  320. selector: 'div.TQc1id#rhs a'
  321. }
  322. ],
  323. 'yandex': [
  324. {
  325. selector: 'ul#search-result li div.Organic-Subtitle div a',
  326. updateChildText: true,
  327. containProtocol: false,
  328. displayMethod: 1,
  329. },
  330. {
  331. selector: 'ul#search-result li div.Organic div a',
  332. }
  333. ],
  334. 'searx': [
  335. {
  336. selector: 'article.result a.url_wrapper',
  337. childSelector: 'span span',
  338. updateChildText: true,
  339. containProtocol: true,
  340. displayMethod: 1,
  341. multiElementsForUrlDisplay: true
  342. },
  343. {
  344. selector: 'article.result h3 a'
  345. },
  346. {
  347. selector: 'aside.infobox div.urls ul li a'
  348. }
  349. ],
  350. 'startpage': [
  351. {
  352. selector: 'a.w-gl__result-url.result-link',
  353. updateText: true,
  354. displayMethod: 2
  355. },
  356. {
  357. selector: 'a.w-gl__result-title.result-link'
  358. },
  359. {
  360. selector: 'div.sx-kp-main a'
  361. }
  362. ],
  363. 'brave': [
  364. {
  365. selector: 'a.h.svelte-1dihpoi',
  366. childSelector: 'cite.snippet-url.svelte-1ygzem6 span',
  367. updateChildText: true,
  368. containProtocol: false,
  369. displayMethod: 1,
  370. multiElementsForUrlDisplay: true
  371. },
  372. {
  373. selector: 'div.snippet a'
  374. }
  375. ],
  376. 'duckduckgo': [
  377. {
  378. selector: 'a.eVNpHGjtxRBq_gLOfGDr.LQNqh2U1kzYxREs65IJu'
  379. },
  380. {
  381. selector: 'a.Rn_JXVtoPVAFyGkcaXyK',
  382. childSelector: 'span',
  383. updateChildText: true,
  384. containProtocol: true,
  385. displayMethod: 1,
  386. multiElementsForUrlDisplay: true
  387. },
  388. {
  389. // Selector for sub-results
  390. selector: 'ul.b269SZlC2oyR13Fcc4Iy li a.f3uDrYrWF3Exrfp1m3Og'
  391. },
  392. {
  393. selector: 'div.react-module div section div a'
  394. }
  395. ],
  396. 'qwant': [
  397. {
  398. selector: 'div._35zId._3A7p7 a.external'
  399. },
  400. {
  401. selector: 'div._35zId._3WA-c a.external',
  402. childSelector: 'span',
  403. updateChildText: true,
  404. containProtocol: false,
  405. displayMethod: 1,
  406. multiElementsForUrlDisplay: true
  407. },
  408. {
  409. // Selector for sub-results
  410. selector: 'div._12BMd div._2-LMx._2E8gc._16lFV.Ks7KS.tCpbb.m_hqb a.external'
  411. },
  412. {
  413. selector: 'div._3McWE.is-sidebar a.external'
  414. }
  415. ],
  416. 'ecosia': [
  417. {
  418. selector: 'div.mainline__result-wrapper div.result__header div.result__info a',
  419. childSelector: 'span span',
  420. updateChildText: true,
  421. containProtocol: true,
  422. displayMethod: 1,
  423. multiElementsForUrlDisplay: true
  424. },
  425. {
  426. selector: 'div.mainline__result-wrapper div.result__header div.result__title a'
  427. },
  428. {
  429. selector: 'div.mainline__result-wrapper div ul li a'
  430. },
  431. {
  432. selector: 'aside.sidebar article div.entity-links ul li a'
  433. },
  434. {
  435. selector: 'aside.sidebar article div.entity__content p a'
  436. }
  437. ],
  438. 'presearch': [
  439. {
  440. selector: 'div.relative div.w-auto a',
  441. childSelector: 'div',
  442. updateChildText: true,
  443. displayMethod: 3,
  444. },
  445. {
  446. selector: 'div.relative div.inline-block a'
  447. }
  448. ],
  449. 'swisscows': [
  450. {
  451. selector: 'article.item-web a',
  452. updateText: true,
  453. containProtocol: false,
  454. displayMethod: 1
  455. }
  456. ],
  457. 'metager': [
  458. {
  459. selector: 'h2.result-title a'
  460. },
  461. {
  462. selector: 'div.result-subheadline a',
  463. updateText: true,
  464. displayMethod: 3
  465. },
  466. {
  467. selector: 'div.quicktip div.quicktip-headline h1 a'
  468. },
  469. {
  470. selector: 'div.quicktip div.quicktip-detail h2 a'
  471. }
  472. ],
  473. '4get': [
  474. {
  475. selector: 'div.text-result a.hover'
  476. },
  477. {
  478. selector: 'div.text-result div.sublinks a'
  479. },
  480. {
  481. selector: 'div.right-wrapper div.answer-wrapper div.answer div.answer-title a.answer-title'
  482. }
  483. ],
  484. 'librey': [
  485. {
  486. selector: 'div.text-result-wrapper a',
  487. updateText: true,
  488. useTopLevelDomain: true,
  489. displayMethod: 2
  490. },
  491. {
  492. selector: 'p.special-result-container a',
  493. updateText: true,
  494. displayMethod: 2
  495. },
  496. ],
  497. 'stract': [
  498. {
  499. selector: 'div.grid div div.flex div div div a',
  500. updateText: true,
  501. displayMethod: 2
  502. },
  503. {
  504. selector: 'div.grid div div.flex div div a'
  505. },
  506. {
  507. selector: 'div.mb-5.text-xl a'
  508. },
  509. {
  510. selector: 'div.text-sm a.text-link'
  511. }
  512. ],
  513. 'etools': [
  514. {
  515. // searchSubmit.do
  516. selector: 'td.record a.title'
  517. },
  518. {
  519. // mobileSearch.do
  520. selector: 'p a.title'
  521. }
  522. ],
  523. 'lilo': [
  524. {
  525. selector: 'div.lilo-text-result div a'
  526. },
  527. {
  528. selector: 'div.column a'
  529. }
  530. ],
  531. 'entireweb': [
  532. {
  533. selector: 'div.gsc-webResult.gsc-result a'
  534. },
  535. {
  536. selector: 'div.web-result a'
  537. },
  538. {
  539. selector: 'div#infobox-list div.card-body a'
  540. }
  541. ],
  542. 'mojeek': [
  543. {
  544. selector: 'ul.results-standard li h2 a.title'
  545. },
  546. {
  547. selector: 'ul.results-standard li a.ob',
  548. childSelector: 'span.url',
  549. updateChildText: true,
  550. containProtocol: true,
  551. displayMethod: 1
  552. },
  553. {
  554. selector: 'div.infobox p a'
  555. },
  556. {
  557. selector: 'div.results.news-results li a'
  558. },
  559. {
  560. selector: 'div.right-col div.results ul li a'
  561. }
  562. ],
  563. 'yep': [
  564. {
  565. selector: 'div.css-102xgmn-card div div a',
  566. childSelector: 'div span',
  567. updateChildText: true,
  568. containProtocol: false,
  569. displayMethod: 1
  570. }
  571. ],
  572. 'torry': [
  573. {
  574. selector: 'div.searpList p a.toranclick',
  575. updateText: true,
  576. displayMethod: 2
  577. },
  578. {
  579. selector: 'div.searpList div h2 a.toranclick',
  580. },
  581. {
  582. selector: 'div.searpList ul li a',
  583. }
  584. ]
  585. // Additional search engines can be defined here...
  586. };
  587.  
  588. // User-defined list of search engine instance URLs
  589. const searchEngines = {
  590. 'google': {
  591. hosts: ['google.com'],
  592. // search results container
  593. // you can ignore this parameter if you don't want to set it, just delete it
  594. // defult value is 'body'
  595. resultContainerSelectors: ['div.GyAeWb#rcnt']
  596. },
  597. 'yandex': {
  598. hosts: [
  599. 'yandex.com',
  600. 'yandex.ru'
  601. ],
  602. resultContainerSelectors: ['div.main__container']
  603. },
  604. 'searx': {
  605. hosts: [
  606. 'search.disroot.org',
  607. 'searx.tiekoetter.com',
  608. 'search.bus-hit.me',
  609. 'search.inetol.net',
  610. 'priv.au',
  611. 'searx.be',
  612. 'searxng.site',
  613. 'search.hbubli.cc',
  614. 'search.im-in.space',
  615. 'opnxng.com',
  616. 'search.upinmars.com',
  617. 'search.sapti.me',
  618. 'freesearch.club',
  619. 'xo.wtf',
  620. 'www.gruble.de',
  621. 'searx.tuxcloud.net',
  622. 'baresearch.org',
  623. 'searx.daetalytica.io',
  624. 'etsi.me',
  625. 'search.leptons.xyz',
  626. 'search.rowie.at',
  627. 'search.mdosch.de',
  628. 'searx.catfluori.de',
  629. 'searx.si',
  630. 'searx.namejeff.xyz',
  631. 'search.itstechtime.com',
  632. 's.mble.dk',
  633. 'searx.kutay.dev',
  634. 'ooglester.com',
  635. 'searx.ox2.fr',
  636. 'searx.techsaviours.org',
  637. 'searx.perennialte.ch',
  638. 's.trung.fun',
  639. 'search.in.projectsegfau.lt',
  640. 'search.projectsegfau.lt',
  641. 'darmarit.org',
  642. 'searx.lunar.icu',
  643. 'nyc1.sx.ggtyler.dev',
  644. 'search.rhscz.eu',
  645. 'paulgo.io',
  646. 'northboot.xyz',
  647. 'searx.zhenyapav.com',
  648. 'searxng.ch',
  649. 'copp.gg',
  650. 'searx.sev.monster',
  651. 'searx.oakleycord.dev',
  652. 'searx.juancord.xyz',
  653. 'searx.work',
  654. 'search.ononoki.org',
  655. 'search.demoniak.ch',
  656. 'searx.cthd.icu',
  657. 'searx.fmhy.net',
  658. 'searx.headpat.exchange',
  659. 'sex.finaltek.net',
  660. 'search.gcomm.ch',
  661. 'search.smnz.de',
  662. 'searx.ankha.ac',
  663. 'search.lvkaszus.pl',
  664. 'searx.nobulart.com',
  665. 'sx.t-1.org',
  666. 'www.jabber-germany.de',
  667. 'sx.catgirl.cloud'
  668. ],
  669. resultContainerSelectors: [
  670. 'main#main_results'
  671. // 'maindiv#main_results div#urls'
  672. // 'div#sidebar div#infoboxes'
  673. ]
  674. },
  675. 'startpage': {
  676. hosts: ['startpage.com'],
  677. resultContainerSelectors: [
  678. 'div.show-results',
  679. ]
  680. },
  681. 'brave': {
  682. hosts: ['search.brave.com'],
  683. resultContainerSelectors: [
  684. 'main.main-column',
  685. 'aside.sidebar'
  686. ]
  687. },
  688. 'duckduckgo': {
  689. hosts: ['duckduckgo.com'],
  690. resultContainerSelectors: [
  691. 'section[data-testid="mainline"][data-area="mainline"]',
  692. 'section[data-testid="sidebar"][data-area="sidebar"]'
  693. ]
  694. },
  695. 'qwant': {
  696. hosts: ['qwant.com'],
  697. resultContainerSelectors: ['div._35zId']
  698. },
  699. 'ecosia': {
  700. hosts: ['ecosia.org'],
  701. resultContainerSelectors: [
  702. 'section.mainline.web__mainline',
  703. 'aside.sidebar.web__sidebar'
  704. ]
  705. },
  706. 'presearch': {
  707. hosts: ['presearch.com'],
  708. resultContainerSelectors: ['div.w-full']
  709. },
  710. 'swisscows': {
  711. hosts: ['swisscows.com'],
  712. resultContainerSelectors: ['section.container.page-results']
  713. },
  714. 'metager': {
  715. hosts: [
  716. 'metager.org',
  717. 'metager.de'
  718. ],
  719. resultContainerSelectors: [
  720. 'div#results',
  721. 'div#additions-container'
  722. ]
  723. },
  724. '4get': {
  725. hosts: [
  726. '4get.ca',
  727. '4get.silly.computer',
  728. '4get.plunked.party',
  729. '4get.konakona.moe',
  730. '4get.sijh.net',
  731. '4get.hbubli.cc',
  732. '4get.perennialte.ch',
  733. '4get.zzls.xyz',
  734. '4getus.zzls.xyz',
  735. '4get.seitan-ayoub.lol',
  736. '4get.dcs0.hu',
  737. '4get.psily.garden',
  738. '4get.lvkaszus.pl',
  739. '4get.kizuki.lol'
  740. ],
  741. resultContainerSelectors: ['div#overflow']
  742. },
  743. 'librey': {
  744. hosts: [
  745. 'search.ahwx.org',
  746. 'search2.ahwx.org',
  747. 'search3.ahwx.org',
  748. 'ly.owo.si',
  749. 'librey.franklyflawless.org',
  750. 'librey.org',
  751. 'search.davidovski.xyz',
  752. 'search.milivojevic.in.rs',
  753. 'glass.prpl.wtf',
  754. 'librex.uk.to',
  755. 'librey.ix.tc',
  756. 'search.funami.tech',
  757. 'librex.retro-hax.net',
  758. 'librex.nohost.network',
  759. 'search.pabloferreiro.es',
  760. 'librey.baczek.me',
  761. 'lx.benike.me',
  762. 'search.seitan-ayoub.lol',
  763. 'librey.myroware.net',
  764. 'librey.nezumi.party',
  765. 'search.zeroish.xyz',
  766. 'search.zeroish.xyz'
  767. ],
  768. resultContainerSelectors: [
  769. 'div.text-result-container',
  770. 'p.special-result-container'
  771. ]
  772. },
  773. 'stract': {
  774. hosts: ['stract.com'],
  775. resultContainerSelectors: [
  776. 'div.col-start-1',
  777. 'div.row-start-2'
  778. ]
  779. },
  780. 'etools': {
  781. hosts: ['etools.ch'],
  782. // resultContainerSelectors: ['table.result']
  783. },
  784. 'lilo': {
  785. hosts: ['search.lilo.org'],
  786. resultContainerSelectors: ['div.container#content']
  787. },
  788. 'entireweb': {
  789. hosts: ['search.entireweb.com'],
  790. resultContainerSelectors: ['div.container.search-container']
  791. },
  792. 'mojeek': {
  793. hosts: ['mojeek.com'],
  794. resultContainerSelectors: ['div.container.serp-results']
  795. },
  796. 'yep': {
  797. hosts: ['yep.com']
  798. },
  799. 'torry': {
  800. hosts: ['torry.io'],
  801. resultContainerSelectors: ['div.searpListouterappend'],
  802. attribute: 'data-target'
  803. }
  804. // ... more search engines
  805. };
  806.  
  807. // Function to modify URLs and optionally text
  808. const modifyUrls = (engine, observer, resultContainer, engineInfo) => {
  809. try {
  810. const selectors = selectorRules[engine];
  811. if (selectors) {
  812. // Disconnect the observer to prevent recursive triggering
  813. observer.disconnect();
  814.  
  815. // Modify results
  816. selectors.forEach(rule => {
  817. processElements(rule.selector, rule, engineInfo);
  818. });
  819.  
  820. // Reconnect the observer after DOM modifications are done
  821. observer.observe(resultContainer, { childList: true, subtree: true });
  822. }
  823. } catch (error) {
  824. console.error("URL Modification Error: ", error);
  825. }
  826. };
  827.  
  828. // Function to process elements based on selector and rule
  829. const processElements = (selector, rule, engineInfo) => {
  830. const elements = document.querySelectorAll(selector);
  831. const additionalAttribute = engineInfo.attribute; // Get the additional attribute if specified
  832. if (elements.length > 0) {
  833. elements.forEach(element => {
  834. for (let i = 0; i < urlModificationRules.length; i++) {
  835. try {
  836. const urlRule = urlModificationRules[i];
  837. let urlToModify = element.href || (additionalAttribute && element.getAttribute(additionalAttribute));
  838. // update attribute
  839. if (urlToModify && urlRule.matchRegex.test(urlToModify)) {
  840. // Generate redirected URL
  841. let newUrl = urlToModify.replace(urlRule.matchRegex, urlRule.replaceWith);
  842. newUrl = rule.useTopLevelDomain ? extractTopLevelDomain(newUrl) : newUrl;
  843. if (element.href) {
  844. element.href = newUrl;
  845. } else if (additionalAttribute) {
  846. element.setAttribute(additionalAttribute, newUrl);
  847. }
  848. updateTextContent(element, rule, newUrl);
  849. break;
  850. }
  851. } catch (error) {
  852. console.error("Update Link/Text Error: ", error);
  853. }
  854. }
  855. });
  856. }
  857. };
  858.  
  859. // Function to update text content (displayed url)
  860. const updateTextContent = (element, rule, newUrl) => {
  861. if (rule.updateText || rule.updateChildText) {
  862. try {
  863. if (rule.multiElementsForUrlDisplay) {
  864. updateMultiElementContent(element, rule, newUrl);
  865. } else {
  866. let targetElement = element;
  867. if (rule.childSelector) {
  868. targetElement = element.querySelector(rule.childSelector)
  869. }
  870. updateSingleElementText(targetElement, rule, newUrl);
  871. }
  872. } catch (error) {
  873. console.error("Update Displayed URL Error: ", error);
  874. }
  875. }
  876. };
  877.  
  878. // Function to update text for multi elements (i.e. DuckDuckGo, Brave)
  879. const updateMultiElementContent = (element, rule, newUrl) => {
  880. // Remove the "https://" protocol if containProtocol is false
  881. newUrl = rule.containProtocol ? newUrl : removeProtocol(newUrl);
  882.  
  883. let formattedUrl = formatMethod1(newUrl, rule.containProtocol); // Assume max length 70 for splitting
  884. let urlParts = formattedUrl.split(' › ');
  885.  
  886. // Correctly select the first and second <span> elements
  887. let spans = element.querySelectorAll(rule.childSelector);
  888.  
  889. if (spans && spans.length >= 2) {
  890. spans.forEach(clearElementContent);
  891. spans[0].textContent = urlParts[0]; // Update the first part
  892. spans[1].textContent = ' › ' + urlParts.slice(1).join(' › '); // Update the second part
  893. } else {
  894. console.error("Script: Expected structure not found for Double Element URL update!");
  895. }
  896. };
  897.  
  898. // Function to update text for a single element
  899. const updateSingleElementText = (targetElement, rule, newUrl) => {
  900. if (!targetElement) {
  901. console.error("Target DOM Element not found for Single-Element Text update!");
  902. return;
  903. }
  904. if (targetElement) {
  905. let formattedUrl = '';
  906. switch (rule.displayMethod) {
  907. case 1:
  908. formattedUrl = formatMethod1(newUrl, rule.containProtocol);
  909. break;
  910. case 2:
  911. formattedUrl = newUrl; // Full URL with protocol
  912. break;
  913. case 3:
  914. formattedUrl = decodeURIComponent(removeProtocol(newUrl)); // Full URL without protocol
  915. break;
  916. }
  917. if (rule.updateText) {
  918. updateTextWithoutOverwriteChildNodes(targetElement, formattedUrl);
  919. } else {
  920. targetElement.textContent = formattedUrl;
  921. }
  922. } else {
  923. console.error("Script: Expected element not found for Single Element URL update!");
  924. }
  925. };
  926.  
  927. // Function for Method 1 (Breadcrumb style URLs), leaving 'https://' intact
  928. const formatMethod1 = (url, containProtocol) => {
  929. if (!containProtocol) {
  930. url = removeProtocol(url);
  931. }
  932. // Split the URL while keeping 'https://' intact; Replace the second occurrence of 'https://' with 'https', if exists
  933. // Replace the first occurrence of 'https://' with a placeholder
  934. url = url.replace('https://', 'https›');
  935. // Deal with the second 'https://'
  936. let secondHttpsIndex = url.indexOf('https://');
  937. if (secondHttpsIndex !== -1) {
  938. url = url.substring(0, secondHttpsIndex) + 'https/' + url.substring(secondHttpsIndex + 8);
  939. }
  940. // Split the URL with '/'
  941. let parts = url.split('/');
  942. // Restore the first 'https://' in the URL
  943. parts[0] = parts[0].replace('https›', 'https://');
  944.  
  945. // Join the URL parts with ' › '
  946. let joinedUrl = parts.join(' › ');
  947.  
  948. // Decode the URL to convert encoded characters to their original form
  949. return decodeURIComponent(joinedUrl);
  950. };
  951.  
  952. // Function to update only the text node within an element, leave the child elements, if exist, intact
  953. const updateTextWithoutOverwriteChildNodes = (element, newContent) => {
  954. let foundTextNode = false;
  955. // Iterate through child nodes
  956. for (const node of element.childNodes) {
  957. // Identify and update the first text node
  958. if (node.nodeType === Node.TEXT_NODE) {
  959. node.nodeValue = newContent;
  960. foundTextNode = true;
  961. break; // Stop after updating the first text node
  962. }
  963. }
  964. };
  965.  
  966. // Remove 'https://' from the URL link
  967. const removeProtocol = (url) => {
  968. return url.replace(/^https?:\/\//, '');
  969. };
  970.  
  971. // Extract the top level domain from URL link
  972. const extractTopLevelDomain = (url) => {
  973. const parsedUrl = new URL(url);
  974. return `${parsedUrl.protocol}//${parsedUrl.hostname}/`;
  975. };
  976.  
  977. // Function to clear existing content of an element
  978. const clearElementContent = (element) => {
  979. if (element) {
  980. element.textContent = '';
  981. } else {
  982.  
  983. }
  984. };
  985.  
  986. // Improved function to determine the search engine
  987. const getSearchEngineInfo = () => {
  988. try {
  989. const host = window.location.host;
  990. for (const engine in searchEngines) {
  991. if (searchEngines[engine].hosts.some(instanceHost => host.includes(instanceHost))) {
  992. const selectors = searchEngines[engine].resultContainerSelectors || ['body']; // Default to 'body' if not specified
  993. const attribute = searchEngines[engine].attribute; // Get the attribute if specified
  994. return {
  995. engine,
  996. selectors: selectors,
  997. attribute: attribute
  998. };
  999. }
  1000. }
  1001. } catch (error) {
  1002. console.error("Error determining search engine: ", error);
  1003. }
  1004. };
  1005.  
  1006. const observeToExecute = (engine, selector, engineInfo) => {
  1007. const resultContainers = document.querySelectorAll(selector);
  1008. if (resultContainers) {
  1009. resultContainers.forEach(resultContainer => {
  1010. // Observe changes in each result container
  1011. const observer = new MutationObserver(() => modifyUrls(engine, observer, resultContainer, engineInfo));
  1012. observer.observe(resultContainer, { childList: true, subtree: true });
  1013. modifyUrls(engine, observer, resultContainer, engineInfo);
  1014. });
  1015. }
  1016. };
  1017.  
  1018. // Run the script for the current search engine
  1019. try {
  1020. const engineInfo = getSearchEngineInfo();
  1021. if (engineInfo) {
  1022. engineInfo.selectors.forEach(containerSelector => {
  1023. observeToExecute(engineInfo.engine, containerSelector, engineInfo);
  1024. });
  1025. }
  1026. } catch (error) {
  1027. console.error("Error executing URL Modifier Script: ", error);
  1028. }
  1029. })();