Black Belt

Finds useful links and displays a top bar with various of links from contact details to media documents, including Metalinks, Podcasts, Syndication Feeds (Atom, JSON & RSS), Torrents and Userscripts. Also supports Chat, Email, Geoposition, IPFS, Magnet links of eXact Topic (xt), VoIP, Wallet schemes and more.

当前为 2023-05-17 提交的版本,查看 最新版本

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