Black Belt

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.

当前为 2023-11-23 提交的版本,查看 最新版本

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