Greasy Fork 还支持 简体中文。

Gazelle: Toggle Format Visibility

Hide formats with your discretion.

目前為 2024-12-07 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @id what-toggle-formats
  3. // @name Gazelle: Toggle Format Visibility
  4. // @namespace hateradio)))
  5. // @author hateradio
  6. // @version 4.2
  7. // @description Hide formats with your discretion.
  8. // @include https://redacted.sh/torrents.php*
  9. // @include https://redacted.sh/artist.php?id=*
  10. // @include https://redacted.sh/bookmarks.php*
  11. // @include https://redacted.sh/collages.php*
  12. // @include https://redacted.sh/top10.php*
  13.  
  14. // @include https://orpheus.network/torrents.php*
  15. // @include https://orpheus.network/artist.php?id=*
  16. // @include https://orpheus.network/bookmarks.php*
  17. // @include https://orpheus.network/collages.php*
  18. // @include https://orpheus.network/top10.php*
  19.  
  20.  
  21. // @grant none
  22. // @updated 07 DEC 2024
  23. // @since 28 OCT 2010
  24. // ==/UserScript==
  25.  
  26. (function () {
  27. 'use strict';
  28.  
  29. // P O L Y
  30. if (!Element.prototype.matches)
  31. Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
  32.  
  33. if (!Element.prototype.closest) {
  34. Element.prototype.closest = function (s) {
  35. let el = this;
  36. if (!document.documentElement.contains(el)) return null;
  37. do {
  38. if (el.matches(s)) return el;
  39. el = el.parentElement || el.parentNode;
  40. } while (el !== null && el.nodeType === 1);
  41. return null;
  42. };
  43. }
  44.  
  45.  
  46. // O N HELPER
  47. const on = (element, type, selector, listener) => {
  48. element.addEventListener(type, event => {
  49. const onTarget = event.target.closest(selector);
  50. if (onTarget) {
  51. event.onTarget = onTarget;
  52. listener(event);
  53. }
  54. }, false);
  55. };
  56.  
  57. // S T O R A G E HANDLE
  58. const strg = {
  59. on: (function () { try { let a, b = localStorage, c = Math.random().toString(16).substr(2, 8); b.setItem(c, c); a = b.getItem(c); return a === c ? !b.removeItem(c) : false; } catch (e) { return false; } }()),
  60. read: function (key) { return this.on ? JSON.parse(localStorage.getItem(key)) : false; },
  61. save: function (key, dat) { return this.on ? !localStorage.setItem(key, JSON.stringify(dat)) : false; },
  62. wipe: function (key) { return this.on ? !localStorage.removeItem(key) : false; },
  63. zero: function (o) { for (let k in o) { if (o.hasOwnProperty(k)) { return false; } } return true; },
  64. grab: function (key, def) { const s = strg.read(key); return strg.zero(s) ? def : s; }
  65. };
  66.  
  67.  
  68. const hide = {
  69. loc: document.querySelector('.sidebar') || document.querySelector('.linkbox'),
  70.  
  71. typ: ['CD', 'Vinyl', 'WEB', 'SACD', 'DVD', 'DAT', 'Cassette', 'BD', 'Soundboard'],
  72. cod: ['FLAC', 'Ogg', 'AAC', 'AC3', 'DTS', 'MP3'],
  73. enc: ['192', 'APS', 'V2', 'V1', '256', 'APX', 'V0', '320', '/ Lossless', '24bit Lossless'],
  74. lch: ['Scene', 'Freeleech', 'Neutral Leech', 'Reported', 'Bad'],
  75.  
  76. hid: strg.read('togglesettings2') || [],
  77.  
  78. div: document.createElement('div'),
  79.  
  80. fra: document.createDocumentFragment(),
  81.  
  82. init: function () {
  83. const s = document.createElement('style'),
  84. css = '.hider-f { text-decoration: line-through } #format-hide { text-align: center; margin: 3px 0px } .gtfv__item { cursor: pointer }';
  85.  
  86. s.type = 'text/css';
  87. s.textContent = css;
  88. document.head.appendChild(s);
  89.  
  90. on(this.div, 'click', 'span', this.change.bind(this));
  91.  
  92. // run!
  93. this.location();
  94. this.generate();
  95. this.toggle(this.hid);
  96. this.toggle(this.hid, true);
  97. this.mark();
  98. },
  99. location: function () {
  100. this.div.id = 'format-hide';
  101. this.div.className = 'box pad box_artists';
  102. this.loc.parentNode.insertBefore(this.div, this.loc);
  103. },
  104. makeSpan: function (t) {
  105. const s = document.createElement('span');
  106. s.dataset.type = t;
  107. s.textContent = t.replace(/(?:\/|\\)/, '');
  108. s.id = 'gtfv__' + encodeURIComponent(t);
  109. s.className = 'gtfv__item';
  110. return s;
  111. },
  112. create: function (formats) {
  113. return formats.map(this.makeSpan).forEach(s => {
  114. this.fra.appendChild(s);
  115. this.fra.appendChild(document.createTextNode(' '));
  116. });
  117. },
  118. generate: function () {
  119. this.create(this.typ);
  120. this.fra.appendChild(document.createElement('br'));
  121. this.create(this.cod);
  122. this.fra.appendChild(document.createTextNode(' \u00D7 '));
  123. this.create(this.enc);
  124. this.fra.appendChild(document.createElement('br'));
  125. this.create(this.lch);
  126. this.div.appendChild(this.fra);
  127. },
  128. change: function (e) {
  129. const el = e.onTarget,
  130. type = el.dataset.type,
  131. idx = this.hid.indexOf(type),
  132. media = (this.typ.indexOf(type) !== -1);
  133.  
  134. el.classList.toggle('hider-f');
  135.  
  136. console.log('change', type, `isMedia: ${idx === -1}`);
  137. if (idx === -1) { // not found, add this type to the array of hidden formats
  138. this.hid.push(type);
  139. this.toggle(this.hid, media, false);
  140. } else { // found, remove this type
  141. this.hid.splice(idx, 1);
  142. this.toggle([type], media, true);
  143. }
  144.  
  145. console.log(this.hid);
  146. },
  147. createRegExp: function (formats, isMedia) {
  148. // console.log('createRegExp for', formats, isMedia);
  149. let reg = isMedia ? '\\b' : '';
  150. reg += '(?:' + formats.join('|') + ')\\b';
  151. reg = new RegExp(reg, 'i');
  152. return reg;
  153. },
  154. toggle: function (formats, isMedia, show) {
  155. console.debug('toggle', {formats, isMedia, show});
  156. const regex = formats.length > 0 ? this.createRegExp(formats, isMedia) : null;
  157.  
  158. let root = ''
  159.  
  160. if (regex) {
  161. strg.save('togglesettings2', this.hid);
  162.  
  163. if (processors.top10.enabled) {
  164. root = 'top10';
  165. } else if (isMedia) {
  166. root = 'media';
  167. } else {
  168. root = 'general';
  169. }
  170.  
  171. processors.$exec(root, regex, show);
  172. }
  173. },
  174. mark: function () {
  175. console.log('mark');
  176. Array.from(this.hid)
  177. .map(text => document.getElementById('gtfv__' + encodeURIComponent(text)))
  178. .forEach(el => el.classList.add('hider-f'));
  179. },
  180. click: function (el) {
  181. let evt;
  182. if (el.click) {
  183. el.click();
  184. } else {
  185. evt = document.createEvent('MouseEvents');
  186. evt.initEvent('click', true, true);
  187. el.dispatchEvent(evt);
  188. }
  189. }
  190. };
  191.  
  192.  
  193. const processors = {
  194. $exec: function (root, regex, show) {
  195. const action = show ? 'show' : 'hide';
  196.  
  197. const t = processors[root];
  198.  
  199. Array.from(t.elements())
  200. .filter(e => regex.test(e.textContent))
  201. .map(t.toElement)
  202. .forEach(t[action] || processors.general[action]);
  203. },
  204. general: {
  205. elements: () => (document.getElementById('discog_table') || document.querySelector('.torrent_table')).querySelectorAll('a[href^="torrents.php?id="],a[onclick]'),
  206. toElement: e => e.parentNode.parentNode,
  207. show: e => e.removeAttribute('style'),
  208. hide: e => e.setAttribute('style', 'display:none')
  209. },
  210. top10: {
  211. enabled: document.location.pathname === '/top10.php',
  212. elements: () => Array.from(document.querySelectorAll('.torrent_table .group_info strong')).map(e => e.nextSibling),
  213. toElement: e => e.parentElement.parentElement.parentElement
  214. },
  215. media: {
  216. elements: () => document.querySelectorAll('.edition_info > strong'),
  217. toElement: e => e.querySelector('a'),
  218. show: e => e.textContent !== '-' && hide.click(e),
  219. hide: e => e.textContent !== '+' && hide.click(e)
  220. }
  221. };
  222.  
  223. hide.init();
  224.  
  225. }());