HTTP Unplugged

Find and display links inside a bar; Type of links: chat, contact, email, geo, magnet, media documents, metalinks, podcasts, syndication feeds, torrents and userscripts and wallets.

  1. // ==UserScript==
  2. // @name HTTP Unplugged
  3. // @author Schimon Jehudah, Adv.
  4. // @namespace i2p.schimon.blackbelt
  5. // @homepageURL https://gf.qytechs.cn/en/scripts/466113-black-belt
  6. // @supportURL https://gf.qytechs.cn/en/scripts/466113-black-belt/feedback
  7. // @copyright 2023 - 2024, Schimon Jehudah (http://schimon.i2p)
  8. // @license MIT; https://opensource.org/licenses/MIT
  9. // @description Find and display links inside a bar; Type of links: chat, contact, email, geo, magnet, media documents, metalinks, podcasts, syndication feeds, torrents and userscripts and wallets.
  10. // @match file:///*
  11. // @match *://*/*
  12. // @version 24.03.20
  13. // @grant GM.registerMenuCommand
  14. // @run-at document-end
  15. // @noframes
  16. // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48dGV4dCB5PSIuOWVtIiBmb250LXNpemU9IjkwIj7wn5SMPC90ZXh0Pjwvc3ZnPgo=
  17. // ==/UserScript==
  18.  
  19. // NOTE
  20. // Robe icons (Sauna pack) created by Freepik
  21. // https://www.flaticon.com/free-icon/robe_2520932
  22. // https://www.flaticon.com/authors/freepik
  23. // https://www.freepik.com/
  24.  
  25. // TODO
  26. //
  27. // 0) Bar like https://www.croxyproxy.com/
  28. //
  29. // 0) Decode string for magnet links
  30. // https://btdig.com/9fe6281eaf39f8bee656f27cacf48713c0608c3e/20-birds-&-animals-books-collection-pack-4
  31. //
  32. // 0) Tooltip
  33. // https://www.w3schools.com/howto/howto_css_tooltip.asp
  34. // or DIV on the middle or center of screen
  35. // https://web.archive.org/web/20050423235409/http://karmatics.com/aardvark/
  36. // https://css-tricks.com/quick-css-trick-how-to-center-an-object-exactly-in-the-center/
  37. //
  38. // 1) Brand: Access Bar, Alt Bar, Black Bar, Black Robe, Distribar, Distributed Bar, Distribution Bar, Easy Access Bar, Free Bar, Freenet Bar, Handler Bar, Harvest Bar, IETF Bar, IETF Black Bar, IETF ToolBar, Instant Media Bar, Media Bar, Power Bar, Power Download Bar, Reaping Bar, Simple Access Bar, Simple Bar, Super Bar
  39. //
  40. // 2) Recognize btih of 32 and convert it to 40
  41. //
  42. // 3) Check cache links for none 200 code
  43. // https://bookshelf.theanarchistlibrary.org/library/librarian-previous-announcements-en
  44. //
  45. // 4) FIXME feedx
  46. // http://freebase.be/db/software.rss.xml
  47. //
  48. // 5) Case insensitive (XPath)
  49. // String.prototype.toLowerCase()
  50. // See 'Magnet:' heperlink at https://ddosecrets.com/wiki/Rosatom
  51. // See getPathTo()
  52. //
  53. // 6) Fetch button (guess on demand) instead of auto-guess
  54. // Find file by Hash or ID (Hint: find duplicate chars/strings)
  55. //
  56. // 7) Display software for IPFS, GPS, Monero, RSS, SIP, Tribler, XMPP
  57. //
  58. // 8) Market diaspora*, Linux, Mastodon, ownCloud, RetroShare
  59. //
  60. // 9) TODO cancel *:after "open in new tab" https://cookidoo.thermomix.com/foundation/en-US
  61.  
  62. // blackberry appworld.blackberry.com
  63. // nokia store.ovi.com
  64. // palm
  65. // weixin://dl/chat?
  66. // VK vkontakt
  67.  
  68. // 'pge,✉️,contact,Contact'
  69. const types = {
  70. "feed" : {
  71. "name" : "📰 Follow", // 🗞️ 🔔 索 參
  72. "description" : "Subscribe to News Feed",
  73. "alternate" : ["atom", "rss", "rdf", "stream", "feed", "feed+json"],
  74. "extension" : [
  75. "atom", "atom.php", "atom.xml",
  76. "rss", "rss.php", "rss.xml",
  77. "rdf", "rdf.php", "rdf.xml",
  78. "feed.xml", "rss.json", "feed.json"],
  79. "path" : ["/feed", "/feed/", "app.php/feed"],
  80. "uri" : ["feed", "news"]
  81. },
  82. "podcast" : {
  83. "name" : "🎙️ Podcast", // 🎧
  84. "description" : "Subscribe to Podcast Channel",
  85. "uri" : ["itpc"]
  86. },
  87. "asc" : {
  88. "name" : "🗝️ Encryption Key", // 🔐
  89. "description" : "Additional Sense Code",
  90. "extension" : ["asc", "asc.txt"]
  91. },
  92. "gpg" : {
  93. "name" : "🗝️ Encryption Key", // 🔐
  94. "description" : "OpenPGP",
  95. "extension" : ["gpg", "gpg.txt"]
  96. },
  97. "pgp" : {
  98. "name" : "🗝️ Encryption Key", // 🔐
  99. "description" : "Pretty Good Privacy",
  100. "extension" : ["pgp", "pgp.txt"]
  101. },
  102. "mail" : {
  103. "name" : "✉️ Email", // 📮
  104. "description" : "Send Email Message",
  105. "uri" : ["mailto"]
  106. },
  107. "card" : {
  108. "name" : "🪪 Card", // 📇
  109. "description" : "Virtual Contact File",
  110. "extension" : ["vcard", "vcf"]
  111. },
  112. "geo" : {
  113. "name" : "📍️ Location", // 🗺️
  114. "description" : "Geographic Coordinations",
  115. "extension" : ["gpx", "geojson", "kml", "kmx"],
  116. "uri" : ["geo", "waze"]
  117. },
  118. "gemini" : {
  119. "name" : "💎️ Gemini", // 🔮
  120. "description" : "The Gemini Realm",
  121. "uri" : ["gemini"]
  122. },
  123. "gopher" : {
  124. "name" : "🦦 Gopher", // 🔮
  125. "description" : "The Gopher Realm",
  126. "uri" : ["gopher"]
  127. },
  128. "telephone" : {
  129. "name" : "☎️ Call", // 📞️
  130. "description" : "Call Telephone Number",
  131. "uri" : ["callto", "tel"]
  132. },
  133. "sms" : {
  134. "name" : "💬️ SMS",
  135. "description" : "Message Telephone Number",
  136. "uri" : ["sms"]
  137. },
  138. "voip" : {
  139. "name" : "📞️ VoIP",
  140. "description" : "<span title='Session Initiation Protocol'>Call via SIP</span>",
  141. "uri" : ["sip"]
  142. },
  143. "chat-cabal" : {
  144. "name" : "🔽 Cabal", // ︾ // 🔽 // ⧩ // ➤
  145. "description" : "Cabal Chat Network",
  146. "uri" : ["cabal"]
  147. },
  148. "chat-xmpp" : {
  149. "name" : "💡️ Jabber/XMPP",
  150. "description" : "<span title='Extensible Messaging and Presence Protocol'>The Private & Decentralized Chat Network</span>",
  151. "web" : [
  152. // /i/#
  153. // #converse/room?jid=
  154. "i.kaidan.im",
  155. "join.jabber.network/#",
  156. "anonymous.cheogram.com",
  157. "magicbroccoli.de/i/",
  158. "webchat.disroot.org/#converse/room?jid=",
  159. "xmpp.org/chat#converse/room?jid=",
  160. "yaxim.org/chat/#converse/room?jid=",
  161. "yax.im/i/"],
  162. // TODO handle ?join and ?message
  163. "uri" : ["xmpp"]
  164. },
  165. "chat-irc" : {
  166. "name" : "🗨️ IRC",
  167. "description" : "Internet Relay Chat",
  168. "uri" : ["irc", "ircs"],
  169. "web" : ["kiwiirc.com/nextclient/"]
  170. },
  171. "chat-briar" : {
  172. "name" : "👁 Briar",
  173. "description" : "Briar Chat Network",
  174. "warning" : "This is a messaging system of which its developers have received grants from the CIA.",
  175. "origin" : "🇺🇸 USA",
  176. "uri" : ["briar"]
  177. },
  178. "chat-di" : {
  179. "name" : "👁 Discord",
  180. "description" : "Centralized Messaging Platform",
  181. "warning" : "This chat service logs your activities and conversations to its records and discloses them to governments legally and illegally.",
  182. "origin" : "🇺🇸 USA",
  183. "web" : [
  184. "discord.com/",
  185. "discord.gg/"]
  186. },
  187. "chat-fa" : {
  188. "name" : "👁 Facebook",
  189. "description" : "Centralized Publishing Platform",
  190. "warning" : "This publishing service logs your activities and conversations to its records and discloses them to governments legally and illegally.",
  191. "origin" : "🇺🇸 USA",
  192. "web" : [
  193. "m.me/"]
  194. },
  195. "chat-matrix" : {
  196. "name" : "👁 matrix", // #️⃣️ // # // ⌗ // 濫 // #️ // 🔯
  197. //"description" : "Matrix Chat Network (<a href='https://lukesmith.xyz/articles/matrix-vs-xmpp/' style='color:#000' title='Metadata Disaster'>Read This Warning</a>)",
  198. "description" : "Non-Private & Pseudo-Decentralized Messaging Platform",
  199. "warning" : "This is a <u><a href='https://lukesmith.xyz/articles/matrix-vs-xmpp/' style='color:#fff'>compromised</a></u> messaging system which exposes private metadata to everyone and is connected to middle eastern intelligence agencies.",
  200. "origin" : "🇮🇱 Israel",
  201. "uri" : ["element", "matrix"],
  202. "web" : ["matrix.to/"]
  203. },
  204. "chat-sk" : {
  205. "name" : "👁 Skype",
  206. "description" : "Centralized Messaging Platform",
  207. "warning" : "This chat service logs your activities and conversations to its records and discloses them to governments legally and illegally.",
  208. "origin" : "🇺🇸 USA",
  209. "uri" : ["skype"]
  210. },
  211. "chat-tg" : {
  212. "name" : "👁 Telegram", // 🇶🇦
  213. "description" : "Centralized Messaging Platform",
  214. "warning" : "This chat service logs your activities and conversations to its records and discloses them to governments legally and illegally.",
  215. "origin" : "🇷🇺 Russia",
  216. "web" : [
  217. "t.me/",
  218. "telegram.me"],
  219. "uri" : ["tg"]
  220. },
  221. "chat-te" : {
  222. "name" : "👁 Tencent",
  223. "description" : "Centralized Messaging Platform",
  224. "warning" : "This chat service logs your activities and conversations to its records and discloses them to governments legally and illegally.",
  225. "origin" : "🇨🇳 China",
  226. "uri" : ["tencent"]
  227. },
  228. "chat-vi" : {
  229. "name" : "👁 Viber",
  230. "description" : "Centralized Messaging Platform",
  231. "warning" : "This chat service logs your activities and conversations to its records and discloses them to governments legally and illegally.",
  232. "origin" : "🇯🇵 Japan",
  233. "uri" : ["viber"]
  234. },
  235. "chat-we" : {
  236. "name" : "👁 WeChat",
  237. "description" : "Centralized Messaging Platform",
  238. "warning" : "This chat service logs your activities and conversations to its records and discloses them to governments legally and illegally.",
  239. "origin" : "🇨🇳 China",
  240. "uri" : ["weixin"]
  241. },
  242. "chat-wh" : {
  243. "name" : "👁 Whatsapp",
  244. "description" : "Centralized Messaging Platform",
  245. "warning" : "This chat service logs your activities and conversations to its records and discloses them to governments legally and illegally.",
  246. "origin" : "🇺🇸 USA",
  247. "web" : [
  248. "chat.whatsapp.com",
  249. "wa.me",
  250. "api.whatsapp.com/send?phone=",
  251. "web.whatsapp.com/send?phone="],
  252. "uri" : ["whatsapp"]
  253. },
  254. "tracker" : {
  255. "name" : "📶 Tracker",
  256. "description" : "🧲 BitTorrent Tracker",
  257. "uri" : ["udp"]
  258. },
  259. "ftl-adc" : {
  260. "name" : "🫐️ DC", // Advanced Direct Connect
  261. "description" : "File Transfer / Magnet Link",
  262. "urn" : ['tree:tiger'],
  263. "uri" : ["adc", "adcs", "dchub"]
  264. },
  265. "ftl-bitprint" : {
  266. "name" : "🪩 Gnutella2",
  267. "description" : "File Transfer / Magnet Link",
  268. "urn" : ["bitprint"]
  269. },
  270. "ftl-bittorrent" : {
  271. "name" : "🌊️ BitTorrent", //💧️ ⛲️
  272. "description" : "File Transfer / Magnet Link",
  273. "urn" : ["btih", "btmh"]
  274. },
  275. "ftl-torrent" : {
  276. "name" : "📦️ Torrent", // ⛲️ 🧧️
  277. "description" : "BitTorrent Metadata File",
  278. "extension" : ["torrent"]
  279. },
  280. "ftl-torrent-cache" : {
  281. "name" : "🎁️ Torrent", // ⛲️
  282. "description" : "BitTorrent Metadata File"
  283. },
  284. "ftl-ed2k" : {
  285. "name" : "♈ eDonkey",
  286. "description" : "File Transfer / Magnet Link", // eDonkey2000
  287. "uri" : ["ed2k"],
  288. "urn" : [
  289. "aich",
  290. "ed2k",
  291. "ed2khash"]
  292. },
  293. "ftl-frostwire" : {
  294. "name" : "❄️ Frostwire", // 🍋️ LimeWire
  295. "description" : "File Transfer / Magnet Link",
  296. "urn" : ["sha1"]
  297. },
  298. "ftl-kazzaa" : {
  299. "name" : "⏭️ Fasttrack",
  300. "description" : "File Transfer / Magnet Link",
  301. "urn" : ["kzhash"]
  302. },
  303. "ftl-metalink" : {
  304. "name" : "♾️ Metalink",
  305. "description" : "♾️ Metalink File",
  306. "extension" : ["meta4", "metalink"]
  307. },
  308. "ftl-shareaza" : {
  309. "name" : "❤️‍🔥️ Shareaza",
  310. "description" : "File Transfer / Magnet Link",
  311. "urn" : ["md5"]
  312. },
  313. "wallet-monero" : {
  314. "name" : "🪙️ Monero", // 👛
  315. "description" : "Cryptocurrency Wallet",
  316. "uri" : ["monero"]
  317. },
  318. "wallet-litecoin" : {
  319. "name" : "🪙️ Litecoin",
  320. "description" : "Cryptocurrency Wallet",
  321. "uri" : ["litecoin"]
  322. },
  323. "wallet-ethereum" : {
  324. "name" : "🪙️ Ethereum",
  325. "description" : "Cryptocurrency Wallet",
  326. "uri" : ["ethereum"]
  327. },
  328. "wallet-bitcoin" : {
  329. "name" : "🪙️ Bitcoin",
  330. "description" : "Cryptocurrency Wallet",
  331. "uri" : ["bitcoin"]
  332. },
  333. "pkg-apk" : {
  334. "name" : "💿 Package", // 📦️
  335. "description" : "Android Package",
  336. "condition" : ["android"],
  337. "extension" : ["apk"],
  338. "web" : [
  339. "f-droid.org/packages/",
  340. "apt.izzysoft.de/fdroid/index/apk/",
  341. "mysu.dev/fdroid/",
  342. "acruexirfkgcqhwxyu75v7dtahr3a44hmbfygngsvubmkrbd6axa.b32.i2p/fdroid/",
  343. "cookiejarapps.com/fdroid/repo/",
  344. "fdroid.pabloferreiro.es/repo/",
  345. "appgallery.cloud.huawei.com/ag/n/app/",
  346. "appgallery.huawei.com/app/",
  347. "apkpure.com/",
  348. "play.google.com/store/apps/details?id="]
  349. },
  350. "pkg-appstream" : {
  351. "name" : "💿 Package", // 🛍️
  352. "description" : "AppStream Package",
  353. "condition" : ["linux"],
  354. "uri" : ["appstream"]
  355. },
  356. "pkg-debian" : {
  357. "name" : "💿 Package", // 🐧️
  358. "description" : "Debian Package",
  359. "condition" : ["debian", "ubuntu"],
  360. "extension" : ["deb"]
  361. },
  362. "pkg-fedora" : {
  363. "name" : "💿 Package", // 🐧️
  364. "description" : "Linux Package",
  365. "condition" : ["fedora", "redhat"],
  366. "extension" : ["rpm"]
  367. },
  368. "pkg-flatpak" : {
  369. "name" : "💿 Package", // 🧊
  370. "description" : "Flatpak Package",
  371. "condition" : ["linux"],
  372. "extension" : ["flatpakref"],
  373. "web" : ["flathub.org/apps/details/"]
  374. },
  375. "ios-pkg" : {
  376. "name" : "💿 Package", // 📦️
  377. "description" : "iOS Package",
  378. "condition" : ["ios", "iphone", "ipad"],
  379. "web" : ["apps.apple.com/app/",
  380. "apps.apple.com/us/app/",
  381. "fnd.io"]
  382. },
  383. "pkg-kaios" : {
  384. "name" : "💿 Package", // 💠
  385. "description" : "<a href='https://gerda.tech/' style='color:#000'>Gerda</a> (Kai OS) Package",
  386. "condition" : ["kai"],
  387. "web" : ["store.bananahackers.net/",
  388. "www.kaiostech.com/store/apps/"]
  389. },
  390. "pkg-kde" : {
  391. "name" : "💿 Package", // 🐲️
  392. "description" : "KDE Linux Package",
  393. "condition" : ["linux", "react", "windows"],
  394. "web" : ["store.kde.org/p/"]
  395. },
  396. "pkg-mac" : {
  397. "name" : "💿 Package", // 🍎️
  398. "description" : "Macintosh Package",
  399. "condition" : ["mac"],
  400. "extension" : ["dmg", "pkg"]
  401. },
  402. // NOTE WineHQ
  403. "pkg-reactos" : {
  404. "name" : "💿 Package", // ⚛
  405. "description" : "<a href='https://reactos.org/' style='color:#000'>React OS</a> (Windows) Package",
  406. "condition" : ["windows", "react"],
  407. "extension" : ["exe", "msi"],
  408. "web" : ["apps.microsoft.com/store/detail/",
  409. "www.microsoft.com/store/apps/"]
  410. },
  411. // TODO ask snapcraft for path /app/
  412. // TODO Dismiss path root /
  413. "pkg-snapcraft" : {
  414. "name" : "💿 Package", // 📥️ 🛍️ 🪶️
  415. "description" : "Snapcraft Package",
  416. "web" : ["snapcraft.io/"]
  417. },
  418. "pkg-ubports" : {
  419. "name" : "💿 Package", // 📦️
  420. "description" : "Ubuntu Touch Package",
  421. "condition" : ["ubuntu"],
  422. "uri" : ["openstore"],
  423. "web" : ["open-store.io/app/"]
  424. },
  425. "ext-userjs" : {
  426. "name" : "🐵 Userscript", // 🐒 📜
  427. "description" : "User.JS Script",
  428. "extension" : ["user.js"]
  429. },
  430. "ext-usercss" : {
  431. "name" : "🎨 Userstyle", // 📃
  432. "description" : "User.CSS Stylesheet",
  433. "extension" : ["user.css"]
  434. },
  435. "ext-blink" : {
  436. "name" : "🧩 Extension", // 🧩
  437. "description" : "Web Browser Extension",
  438. "condition" : ["brave", "chrome", "chromium", "crios", "sleipnir", "vivaldi"],
  439. "extension" : ["crx", "chromium.zip", "chrome.zip"],
  440. "web" : ["chrome.google.com/webstore/detail/"]
  441. },
  442. "ext-edge" : {
  443. "name" : "🧩 Extension", // 🧩
  444. "description" : "Web Browser Extension",
  445. "condition" : ["edge"],
  446. "web" : ["microsoftedge.microsoft.com/addons/detail/"]
  447. },
  448. "ext-falkon" : {
  449. "name" : "🧩 Extension", // 🦅️
  450. "description" : "Web Browser Extension",
  451. "condition" : ["falkon"],
  452. "web" : ["store.falkon.org/p/"]
  453. },
  454. "ext-maxthon" : {
  455. "name" : "🧩 Extension",
  456. "description" : "Web Browser Extension",
  457. "condition" : ["maxthon"],
  458. "web" : ["extension.maxthon.com/detail/"]
  459. },
  460. "ext-xpi" : {
  461. "name" : "🧩 Extension", //🐺️ //🦊️ //🦎️
  462. "description" : "Web Browser Extension",
  463. "condition" : ["firefox", "fxios", "librewolf", "waterfox"],
  464. "extension" : ["xpi", "firefox.zip"],
  465. "web" : ["addons.mozilla.org"]
  466. },
  467. "ext-xul" : {
  468. "name" : "🧩 Extension", // 🌕
  469. "description" : "Web Browser Extension",
  470. "condition" : ["basilisk", "goanna", "palemoon"],
  471. "web" : ["addons.palemoon.org",
  472. "realityripple.com/Software/XUL/",
  473. "realityripple.com/Software/Mozilla-Extensions/",
  474. "addons.basilisk-browser.org"]
  475. },
  476. "ipfs" : {
  477. "name" : "💠 IPFS", // 🔮 📁 📂 ⚛️ 🕸️ 🗃️
  478. "description" : "Interplanetary File System",
  479. "uri" : ["ipfs", "ipns", "dweb"],
  480. "web" : ["4everland.io/ipns/",
  481. "cloudflare-ipfs.com/ipfs/",
  482. "cloudflare-ipfs.com/ipns/",
  483. "gateway.pinata.cloud/ipfs/",
  484. "gateway.pinata.cloud/ipns/",
  485. "ipfs.io/ipfs/",
  486. "ipfs.io/ipns/"]
  487. },
  488. "tor" : {
  489. "name" : "🧅️ Tor", // 🔮
  490. "description" : "The Tor Realm",
  491. "url" : ["onion", "onion:"]
  492. },
  493. "i2p" : {
  494. "name" : "㊙️ I2P", //㊣ 🔮
  495. "description" : "The I2P Realm", //⚛️
  496. "url" : ["i2p", "i2p:"]
  497. }
  498. };
  499.  
  500. const namespace = 'i2p-schimon-blackbelt';
  501.  
  502.  
  503. const objectKeys = Object.keys(types);
  504.  
  505. // Check whether HTML; otherwise, exit.
  506. //if (!document.contentType == 'text/html')
  507. // if (!document.doctype) return;
  508. // if (document.doctype == null) return; // Uncaught SyntaxError: Illegal return statement
  509.  
  510. (function() {
  511. let links = [], accept;
  512. for (let i = 0; i < objectKeys.length; i++) {
  513.  
  514. let agent = types[objectKeys[i]].condition, accept = true;
  515. if (agent) {
  516. accept = false;
  517. for (let j = 0; j < agent.length; j++) {
  518. if (navigator.userAgent.toLowerCase().includes(agent[j])) {
  519. accept = true;
  520. }
  521. }
  522. }
  523.  
  524. //if (reject) continue;
  525.  
  526. let
  527. result,
  528. name = types[objectKeys[i]].name,
  529. info = types[objectKeys[i]].description,
  530. warn = types[objectKeys[i]].warning,
  531. array = types[objectKeys[i]];
  532.  
  533. if (array.alternate) {
  534. result = extractRel(array.alternate);
  535. }
  536.  
  537. if (!result && array.extension) {
  538. result = extractFile(array.extension);
  539. }
  540.  
  541. if (!result && array.uri) {
  542. result = extractURI(array.uri);
  543. }
  544.  
  545. if (!result && array.url) {
  546. result = extractURL(array.url);
  547. }
  548.  
  549. if (!result && array.urn) {
  550. result = extractURN(array.urn);
  551. }
  552.  
  553. if (!result && array.web) {
  554. result = extractWeb(array.web);
  555. }
  556.  
  557. if (accept && result) {
  558. links.push(createLink(result, name, info, warn, objectKeys[i]));
  559. GM.registerMenuCommand(name, () => openUrl(result));
  560. }
  561. }
  562.  
  563. if (links.length) {
  564. buildBar(links);
  565. }
  566.  
  567. // https://henrik.nyh.se/
  568. // https://postmarketos.org/
  569. /* document.addEventListener ("scroll", function() {
  570. if (window.pageYOffset > 10) { // TODO when first bar is out of focus
  571. document
  572. .querySelector('#' + namespace + '-bar')
  573. .style.setProperty('position', 'fixed', 'important');
  574. } else {
  575. document
  576. .querySelector('#' + namespace + '-bar')
  577. .style.setProperty('position', 'absolute', 'important');
  578. }
  579. }) */
  580.  
  581. })();
  582.  
  583. function openUrl(url) {
  584. //window.open(url, '_blank');
  585. window.open(url, '_self');
  586. }
  587.  
  588. function buildBar(links) {
  589. let barElement = document.createElement(namespace);
  590. barElement.id = namespace + '-bar';
  591. barElement.style.all = 'unset';
  592. barElement.style.width = '100%';
  593. barElement.style.opacity = 0.5; // 0.75
  594. barElement.style.backgroundColor = '#000'; //'#2c3e50';
  595. barElement.style.color = '#eee';
  596. //barElement.style.setProperty("color", "#eee", "!important")
  597. barElement.style.fontVariant = 'small-caps';
  598. barElement.style.left = 0;
  599. barElement.style.right = 0;
  600. barElement.style.top = 0;
  601. barElement.style.zIndex = 2147483647;
  602. barElement.style.maxHeight = 'fit-content';
  603. //barElement.style.maxWidth = '100vw';
  604. barElement.style.padding = '12px'; //13px //15px //11px //9px //6px //3px //1px
  605. barElement.style.position = 'fixed';
  606. barElement.style.display = 'block';
  607. barElement.style.textAlign = 'center';
  608. barElement.style.direction = 'ltr';
  609. barElement.style.userSelect = 'none';
  610. //barElement.style.overflow = 'hidden';
  611. //barElement.style.transition = 'all 1s ease 0.1s';
  612. barElement.onclick = () => { barElement.remove(); }
  613. barElement.onmouseover = () => { barElement.style.opacity = 0.9; }
  614. barElement.onmouseleave = () => {
  615. var secs = 5;
  616. function timeOut() {
  617. secs -= 1;
  618. if (secs > 0) {
  619. setTimeout(timeOut, 1000);
  620. }
  621. if (secs == 0) {
  622. barElement.querySelector('#' + namespace + '-info-square') &&
  623. barElement.querySelector('#' + namespace + '-info-square').remove();
  624. barElement.querySelector('#' + namespace + '-warn-square') &&
  625. barElement.querySelector('#' + namespace + '-warn-square').remove();
  626. }
  627. } timeOut();
  628. }
  629.  
  630. barElement.onmouseout = () => {
  631. var secs = 20;
  632. function timeOut() {
  633. barElement.onmouseout = () => { secs = 20; }
  634. secs -= 1;
  635. if (secs == 15) {
  636. // FIXME Not working due to !important we have set below
  637. //barElement.style.setProperty('opacity', 'unset', 'important');
  638. barElement.style.opacity = 0.3;
  639. setTimeout(timeOut, 1000);
  640. } else if (secs == 5) {
  641. //barElement.style.setProperty('opacity', 'unset', 'important');
  642. barElement.style.opacity = 0;
  643. setTimeout(timeOut, 1000);
  644. } else if (secs == 0) {
  645. barElement.remove();
  646. return;
  647. } else {
  648. setTimeout(timeOut, 1000);
  649. }
  650. } timeOut();
  651. }
  652.  
  653. // Set !important
  654. for (let i = 0; i < barElement.style.length; i++) {
  655. barElement.style.setProperty(
  656. barElement.style[i],
  657. barElement.style.getPropertyValue(barElement.style[i]),
  658. 'important'
  659. );
  660. }
  661.  
  662. document.body.prepend(barElement);
  663.  
  664. //barElement.append(closeButton(barElement));
  665. links.forEach(link => barElement.append(link));
  666. //console.log("eles.forEach(ele => barElement.append(ele));")
  667. //console.log(eles)
  668.  
  669. if (
  670. // NOTE Not working '#i2p.schimon.blackbelt.bittorrent'
  671. barElement.querySelector('*[id$=bittorrent]') &&
  672. !barElement.querySelector('*[id$=-torrent]')
  673. ) {
  674. // TODO Add after BitTorrent
  675. // TODO place this in the tooltip
  676. barElement.prepend(createLink(
  677. generateTorrentUrl(barElement),
  678. '🎁️ Torrent',
  679. '⛲️ Bittorrent Metadata File',
  680. null,
  681. 'ftl-torrent-cache'))
  682. }
  683.  
  684. // Timer from https://stackoverflow.com/questions/27406765/hide-div-after-x-amount-of-seconds
  685.  
  686. var secs = 33;
  687. function timeOut() {
  688. secs -= 1;
  689. if (secs == 0) {
  690. //barElement.style.display = 'none';
  691. barElement.style.opacity = 0.2;
  692. return;
  693. }
  694. else {
  695. setTimeout(timeOut, 1000);
  696. }
  697. }
  698. timeOut();
  699.  
  700. }
  701.  
  702.  
  703. function warningBox(node, id) {
  704. // "description" : "<b>WARNING</b><br/>This chat service logs your activities and conversations to its records and discloses them to governments legally and illegally.<br/><br/><i>Use <u><a href='https://xmpp.org/software/clients/' style='color:#000' title='Jabber'>XMPP</a></u> for true privacy.</i>",
  705. node.addEventListener ("mouseover", function() {
  706. //if (this.name) {
  707. // this.setAttribute('warn', this.name);
  708. // this.removeAttribute('name');
  709. //}
  710. //if (document.querySelector('#' + namespace + '-warn-square')) {
  711. // document.querySelector('#' + namespace + '-warn-square').remove();
  712. //}
  713. let infoSquare = document.createElement('warn-square');
  714. infoSquare.id = namespace + '-warn-square';
  715. //infoSquare.innerHTML = this.getAttribute('warn');
  716. infoSquare.innerHTML = '<b>WARNING</b></br>' + types[id].warning + '<br/><br/><b>Get XMPP</b></br>Communicate with <u><a href="https://xmpp.org/software/clients/" style="color:#fff">XMPP</a></u> (aka Jabber) for private and secure chat.';
  717. infoSquare.style.all = 'unset';
  718. infoSquare.style.background = 'SlateGrey';
  719. infoSquare.style.border = '3px solid #000';
  720. infoSquare.style.borderRadius = '5px';
  721. infoSquare.style.color = 'White';
  722. infoSquare.style.display = 'block';
  723. infoSquare.style.fontFamily = 'system-ui';
  724. infoSquare.style.fontSize = '22px';
  725. infoSquare.style.fontVariant = 'none';
  726. infoSquare.style.left = '25%';
  727. infoSquare.style.margin = 'auto';
  728. infoSquare.style.padding = '10px';
  729. infoSquare.style.position = 'fixed';
  730. infoSquare.style.right = '25%';
  731. infoSquare.style.top = '180px'; // 15% // 7%
  732. infoSquare.style.filter = `drop-shadow(2px 4px 6px grey)`;
  733. /*
  734. infoSquare.style.width = 50%;
  735. infoSquare.style.font-size = 70%;
  736. */
  737. //infoSquare.style.justifyContent = 'center';
  738. //infoSquare.style.alignItems = 'center';
  739. infoSquare.style.textAlign = 'center';
  740. /* white-space = pre; in case we have html tags */
  741. //infoSquare.textContent = text;
  742. infoSquare.style.zIndex = 2147483647;
  743. document.querySelector('#' + namespace + '-bar').append(infoSquare);
  744. });
  745. }
  746.  
  747.  
  748. function infoBox(node, id) {
  749. node.addEventListener ("mouseover", function() {
  750. //if (this.title) {
  751. // this.setAttribute('info', this.title);
  752. // this.removeAttribute('title');
  753. //}
  754. if (document.querySelector('#' + namespace + '-info-square')) {
  755. document.querySelector('#' + namespace + '-info-square').remove();
  756. }
  757. if (document.querySelector('#' + namespace + '-warn-square')) {
  758. document.querySelector('#' + namespace + '-warn-square').remove();
  759. }
  760. let infoSquare = document.createElement('info-square');
  761. infoSquare.id = namespace + '-info-square';
  762. //infoSquare.innerHTML = this.getAttribute('info');
  763. infoSquare.innerHTML = types[id].description;
  764. infoSquare.style.all = 'unset';
  765. infoSquare.style.background = 'WhiteSmoke';
  766. infoSquare.style.border = '3px solid #000';
  767. infoSquare.style.borderRadius = '5px';
  768. infoSquare.style.color = '#000';
  769. infoSquare.style.display = 'block';
  770. infoSquare.style.fontFamily = 'system-ui';
  771. infoSquare.style.fontSize = '22px';
  772. infoSquare.style.fontVariant = 'none';
  773. infoSquare.style.left = '25%';
  774. infoSquare.style.margin = 'auto';
  775. infoSquare.style.padding = '10px';
  776. infoSquare.style.position = 'fixed';
  777. infoSquare.style.right = '25%';
  778. infoSquare.style.top = '80px'; // 15% // 7%
  779. infoSquare.style.filter = `drop-shadow(2px 4px 6px grey)`;
  780. /*
  781. infoSquare.style.width = 50%;
  782. infoSquare.style.font-size = 70%;
  783. */
  784. //infoSquare.style.justifyContent = 'center';
  785. //infoSquare.style.alignItems = 'center';
  786. infoSquare.style.textAlign = 'center';
  787. /* white-space = pre; in case we have html tags */
  788. //infoSquare.textContent = text;
  789. infoSquare.style.zIndex = 2147483647;
  790. document.querySelector('#' + namespace + '-bar').append(infoSquare);
  791. });
  792. }
  793.  
  794. // NOTE TODO semi-recursive callback
  795. // NOTE TODO typeof
  796. function extractFile(array) {
  797. let i = 0;
  798.  
  799. do {
  800. // FIXME Mainstream to support ends-with
  801. // fn:ends-with appears to be missing in some engines
  802. query = [
  803. `//a[contains(@href, ".${array[i]}")]/@href`,
  804. `//a[contains(@download, ".${array[i]}")]/@download`];
  805. // `//a[ends-with(@href, ".${array[i]}")]/@href`
  806. // `//a[ends-with(text(), ".${array[i]}")]/@href`
  807. result = executeQuery(query, 'xpath');
  808. i = i + 1;
  809. } while (!result && i < array.length);
  810.  
  811. if (result) {
  812. protocol = location.protocol
  813. hostname = location.hostname
  814. //console.log(result)
  815. switch (true) {
  816.  
  817. case (result.startsWith('/')):
  818. result = protocol + '//' + hostname + result;
  819. break;
  820.  
  821. case (!result.includes(':')):
  822. result = protocol + '//' + hostname + '/' + result;
  823. break;
  824.  
  825. //case (result.startsWith('http')):
  826. //break;
  827. }
  828.  
  829. //console.log(result)
  830. let url = new URL(result);
  831. let bol = url.pathname.endsWith(array[i-1]);
  832. if (bol) { return result; };
  833. }
  834. }
  835.  
  836.  
  837. function extractRel(array) {
  838. let i = 0;
  839.  
  840. do {
  841. query = [
  842. // Also rel="feed". See https://miranda-ng.org/
  843. `//link[@rel="alternate"\
  844. and contains(@type, "${array[i]}")\
  845. ]/@href`];
  846. result = executeQuery(query, 'xpath');
  847. i = i + 1;
  848. } while (!result && i < array.length);
  849.  
  850. if (result) { return result; };
  851. }
  852.  
  853.  
  854. function extractURI(array) {
  855. let i = 0;
  856.  
  857. do {
  858. query = [
  859. `//a[starts-with(@href, "${array[i]}:")\
  860. and not(starts-with(@href, "tg://msg_url?"))\
  861. and not(starts-with(@href, "mailto:?"))\
  862. and not(contains(@href, "/send?"))\
  863. ]/@href`];
  864. result = executeQuery(query, 'xpath');
  865. i = i + 1;
  866. } while (!result && i < array.length);
  867.  
  868. if (result) {
  869. let url = new URL(result);
  870. let bol = url.protocol.match(array[i-1]);
  871. if (bol) { return result; };
  872. }
  873. }
  874.  
  875.  
  876. function extractURL(array) {
  877. let i = 0;
  878.  
  879. do {
  880. query = [
  881. `//a[starts-with(@href, "http")
  882. and contains(@href, ".${array[i]}")
  883. ]/@href`];
  884. // FIXME mainstream
  885. //'//a[starts-with(@href, "http") and ends-with(@href, "' + array[i] + '")]/@href'
  886. result = executeQuery(query, 'xpath');
  887. i = i + 1;
  888. } while (!result && i < array.length);
  889.  
  890. if (result) {
  891. let url = new URL(result);
  892. let bol = url.hostname.endsWith(array[i-1]);
  893. if (bol) { return result };
  894. //if (!url) {
  895. // url = url.host.contains(array[i] + ':');
  896. //}
  897. }
  898. }
  899.  
  900.  
  901. function extractURN(array) {
  902. let i = 0;
  903.  
  904. do {
  905. query = [
  906. `//a[starts-with(@href, "magnet")\
  907. and contains(@href, "${array[i]}")\
  908. ]/@href`];
  909. result = executeQuery(query, 'xpath');
  910. i = i + 1;
  911. } while (!result && i < array.length);
  912.  
  913. if (result) {
  914. let url = new URL(result);
  915. url.searchParams.delete('tr');
  916. result = url.protocol + url.search;
  917. result = decodeURIComponent(result);
  918. return result;
  919. //let bol = url.hostname.startsWith(array[i-1]);
  920. //if (bol) { createLink(result, type) };
  921. }
  922. }
  923.  
  924.  
  925. function extractWeb(array) {
  926. let i = 0;
  927.  
  928. do {
  929. query = [
  930. `//a[starts-with(@href, "http")\
  931. and contains(@href, "://${array[i]}")\
  932. and not(starts-with(@href, "https://wa.me/?text"))\
  933. and not(starts-with(@href, "https://t.me/share"))\
  934. and not(starts-with(@href, "https://telegram.me/share"))\
  935. and not(contains(@href, "com.github.android"))\
  936. and not(contains(@href, "1477376905"))\
  937. ]/@href`];
  938. result = executeQuery(query, 'xpath');
  939. i = i + 1;
  940. } while (!result && i < array.length);
  941.  
  942. if (result) { return result; };
  943. }
  944.  
  945.  
  946. // TODO
  947. // String.prototype.toLowerCase()
  948. // href Magnet: (magnet:) is not detected, or
  949. // Set document MIMEType to plain/text
  950. function executeQuery(queries, method) {
  951.  
  952. let i = 0;
  953. do {
  954. switch(method) {
  955. case 'css':
  956. result = document.querySelector(queries[i]);
  957. //if (result) {result = result.href};
  958. if (result) {return result.href};
  959. break;
  960.  
  961. case 'xpath':
  962. // NOTE This may cause 404 error.
  963. // Use getPathTo()
  964. // https://stackoverflow.com/questions/2631820/how-do-i-ensure-saved-click-coordinates-can-be-reload-to-the-same-place-even-if/2631931#2631931
  965. /*
  966. xhtmlFile = new XMLSerializer().serializeToString(document).toLowerCase()
  967. //xhtmlFile = '<html>'+document.documentElement.innerHTML.toLowerCase()+'</html>'
  968. domParser = new DOMParser();
  969. xhtmlFile = domParser.parseFromString(xhtmlFile, 'text/html');
  970. result = document.evaluate(
  971. queries[i], xhtmlFile, null, XPathResult.STRING_TYPE);
  972. */
  973. result = document.evaluate(
  974. queries[i], document, null, XPathResult.STRING_TYPE);
  975. //if (result) {result = result.stringValue};
  976. if (result) {return result.stringValue};
  977. }
  978. } while (!result && i < queries.length);
  979. }
  980.  
  981. function createLink(uri, title, info, warn, id) {
  982. //if (type[4]) {
  983. //let tip = document.createElement('spna');
  984. //tip.class = 'tooltip';
  985. //tip.append('type[4]');
  986. //}
  987.  
  988. //type = type.split(' ');
  989. //sym = getUrnProperty(uri, 'sym');
  990. //net = getUrnProperty(uri, 'net');
  991.  
  992. let aElement = document.createElement('a');
  993. aElement.id = namespace + '-' + id;
  994. aElement.textContent = title;
  995. aElement.href = uri;
  996. //aElement.title = info;
  997. aElement.name = warn;
  998. aElement.style.all = 'unset';
  999. aElement.style.color = '#eee';
  1000. aElement.style.font = 'caption';
  1001. aElement.style.fontFamily = 'system-ui';
  1002. aElement.style.fontSize = '20px'; // 13px
  1003. aElement.style.fontVariantCaps = 'all-small-caps';
  1004. aElement.style.textDecoration = 'none';
  1005. aElement.style.cursor = 'default';
  1006.  
  1007. //aElement.style.fontWeight = 'bold';
  1008. //aElement.style.padding = '3px 9px 3px 9px';
  1009. //aElement.style.margin = '0 9px 0 9px';
  1010. aElement.style.margin = '2% 9px 2% 9px';
  1011. //aElement.style.background = 'black';
  1012. //aElement.style.borderBottomLeftRadius = '9px';
  1013. //aElement.style.borderBottomRightRadius = '9px';
  1014.  
  1015. //aElement.style.forEach (style => style + '!important');
  1016. for (let i = 0; i < aElement.style.length; i++) {
  1017. aElement.style.setProperty(
  1018. aElement.style[i],
  1019. aElement.style.getPropertyValue(aElement.style[i]),
  1020. 'important'
  1021. );
  1022. }
  1023.  
  1024. infoBox(aElement, id);
  1025.  
  1026. if (warn) {
  1027. warningBox(aElement, id);
  1028. }
  1029.  
  1030. //aElement.append(tip);
  1031.  
  1032. //console.log(aElement)
  1033. //console.log(aElements)
  1034. return aElement;
  1035. }
  1036.  
  1037. function generateTorrentUrl(node) {
  1038. // TODO generate link else-if onclick
  1039. // 404 https://bookshelf.theanarchistlibrary.org/library/librarian-previous-announcements-en#toc1
  1040. href = node.querySelector('*[id*=bittorrent]').href;
  1041. let url = new URL(href);
  1042. name = url.searchParams.get('dn');
  1043. if (!name) {name = document.title};
  1044. //xt = url.searchParams.get('xt');
  1045. let hash = url.searchParams.get('xt').slice(9);
  1046. //if (ha.length === 40 && xt.startsWith('urn:btih'))
  1047. if (hash.length === 40 || hash.length === 32) {
  1048. if (hash.length === 32) {
  1049. hash = convertBase32IntoHashSHA1Sum(hash);
  1050. }
  1051. let links = [
  1052. 'https://watercache.libertycorp.org/get/' + hash + '/' + name,
  1053. 'https://itorrents.org/torrent/' + hash + '.torrent?title=' + name,
  1054. 'https://firecache.libertycorp.org/get/' + hash + '/' + name,
  1055. 'http://fcache63sakpihd44kxdduy6kgpdhgejgp323wci435zwy6kiylcnfad.onion/get/' + hash + '/' + name,
  1056. ];
  1057. return links[1];
  1058. //return links[Math.floor(Math.random()*links.length)];
  1059. }
  1060. }
  1061.  
  1062. // Torrent V1
  1063. // TODO handle compressed sha1 http://www.debath.co.uk/MakeAKey.html
  1064. // TODO convert base32 to hash
  1065. // 32/40 https://linuxtracker.org/?page=torrent-details&id=173a0f61ef92b158547937fa0c01e9dc704779f9
  1066. function convertBase32IntoHashSHA1Sum(hash) {
  1067. // Input your Base32 hash
  1068. let base32_hash = hash;
  1069.  
  1070. // Add missing padding
  1071. base32_hash = base32_hash + '='.repeat((8 - base32_hash.length % 8) % 8);
  1072.  
  1073. // Lowercase and convert to binary
  1074. const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
  1075. let bits = '';
  1076. for(let i = 0; i < base32_hash.length; i++) {
  1077. const value = alphabet.indexOf(base32_hash[i]);
  1078. bits += value.toString(2).padStart(5, '0');
  1079. }
  1080.  
  1081. // Convert bits to bytes
  1082. const byteCount = bits.length / 8;
  1083. const byteArray = new Uint8Array(byteCount);
  1084. for(let i = 0; i < byteCount; i++) {
  1085. byteArray[i] = parseInt(bits.substr(i * 8, 8), 2);
  1086. }
  1087.  
  1088. // Convert binary hash into a hexadecimal string
  1089. let hex_hash = '';
  1090. for(let i = 0; i < byteArray.byteLength; i++) {
  1091. hex_hash += ('0' + byteArray[i].toString(16)).substr(-2);
  1092. }
  1093.  
  1094. return hex_hash;
  1095. }
  1096.  
  1097. function closeButton(barElement) {
  1098.  
  1099.  
  1100. let spanElement = document.createElement('span');
  1101. spanElement.textContent = 'X';
  1102. spanElement.style.all = 'unset';
  1103. spanElement.style.color = '#eee';
  1104. spanElement.style.font = 'caption';
  1105. spanElement.style.fontFamily = 'system-ui';
  1106. spanElement.style.fontSize = '15px'; // 13px
  1107. spanElement.style.fontVariantCaps = 'all-small-caps';
  1108. spanElement.style.textDecoration = 'none';
  1109.  
  1110. spanElement.style.fontWeight = 'bold';
  1111. spanElement.style.padding = '3px 9px 3px 9px';
  1112. //spanElement.style.margin = '0 9px 0 9px';
  1113. spanElement.style.background = 'black';
  1114. spanElement.style.borderBottomLeftRadius = '9px';
  1115. spanElement.style.borderBottomRightRadius = '9px';
  1116.  
  1117. //spanElement.style.forEach (style => style + '!important');
  1118. for (let i = 0; i < spanElement.style.length; i++) {
  1119. spanElement.style.setProperty(
  1120. spanElement.style[i],
  1121. spanElement.style.getPropertyValue(spanElement.style[i]),
  1122. 'important'
  1123. );
  1124. }
  1125.  
  1126. spanElement.onclick = () => { barElement.remove(); }
  1127.  
  1128. return spanElement;
  1129.  
  1130. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址