data-manager

handles loaded html, takes care of data, applying filters

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

  1. // ==UserScript==
  2. // @name data-manager
  3. // @description handles loaded html, takes care of data, applying filters
  4. // @namespace http://tampermonkey.net/
  5. // @author smartacephale
  6. // @license MIT
  7. // @version 1.1.4
  8. // @match *://*/*
  9. // ==/UserScript==
  10. /* globals LazyImgLoader, stringToWords */
  11.  
  12. /** Manages thumbs, applying filters, lazy loading, keep list unique */
  13. class DataManager {
  14. /**
  15. * @param {Rules} rules - WEBSITE_RULES class which have to implement methods:
  16. * GET_THUMBS,
  17. * THUMB_URL,
  18. * THUMB_DATA,
  19. * THUMB_IMG_DATA (required for lazy loading, return undefined if no need it)
  20. * IS_PRIVATE * optional
  21. *
  22. * @param {Object} state - object with props:
  23. * filterDuration
  24. * filterDurationFrom
  25. * filterDurationTo
  26. * filterExclude
  27. * filterExcludeWords
  28. * filterInclude
  29. * filterIncludeWords
  30. * filterPublic * optional
  31. * filterPrivate * optional
  32. */
  33. constructor(rules, state) {
  34. this.rules = rules;
  35. this.state = state;
  36. this.data = new Map();
  37. this.setupFilters();
  38. this.lazyImgLoader = LazyImgLoader.create((target) => !this.isFiltered(target));
  39. }
  40.  
  41. dataFilters = {
  42. filterPublic: {
  43. tag: 'filtered-public',
  44. createFilter() {
  45. return (v) => [this.tag, !this.rules.IS_PRIVATE(v.element) && this.state.filterPublic];
  46. }
  47. },
  48. filterPrivate: {
  49. tag: 'filtered-private',
  50. createFilter(){
  51. return (v) => [this.tag, this.rules.IS_PRIVATE(v.element) && this.state.filterPrivate];
  52. }
  53. },
  54. filterDuration: {
  55. tag: 'filtered-duration',
  56. createFilter() {
  57. return (v) => {
  58. const notInRange = v.duration < this.state.filterDurationFrom || v.duration > this.state.filterDurationTo;
  59. return [this.tag, this.state.filterDuration && notInRange];
  60. }
  61. }
  62. },
  63. filterExclude: {
  64. tag: 'filtered-exclude',
  65. createFilter() {
  66. const tags = stringToWords(this.state.filterExcludeWords);
  67. return (v) => {
  68. const containTags = tags.some(tag => v.title.includes(tag));
  69. return [this.tag, this.state.filterExclude && containTags];
  70. }
  71. }
  72. },
  73. filterInclude: {
  74. tag: 'filtered-include',
  75. createFilter() {
  76. const tags = stringToWords(this.state.filterIncludeWords);
  77. return (v) => {
  78. const containTagsNot = tags.some(tag => !v.title.includes(tag));
  79. return [this.tag, this.state.filterInclude && containTagsNot];
  80. }
  81. }
  82. }
  83. }
  84.  
  85. setupFilters() {
  86. // select through state jfc it's just wrong but fuck it fuck my ass
  87. Object.keys(this.dataFilters).forEach(k => {
  88. if (!Object.hasOwn(this.state, k)) {
  89. delete this.dataFilters[k];
  90. }
  91. });
  92.  
  93. const tags = Object.keys(this.dataFilters).map(k => `.${this.dataFilters[k].tag}`).join(',');
  94. GM_addStyle(`${tags} { display: none !important; }`);
  95.  
  96. Object.values(this.dataFilters).forEach(f => {
  97. f.state = this.state;
  98. f.rules = this.rules;
  99. });
  100. }
  101.  
  102. isFiltered(el) {
  103. return el.className.includes('filtered');
  104. }
  105.  
  106. filter_ = (filters, offset = 0) => {
  107. const runFilters = [];
  108.  
  109. for (const f of Object.keys(filters)) {
  110. runFilters.push(this.dataFilters[f].createFilter());
  111. }
  112.  
  113. let offset_counter = 1;
  114. for (const v of this.data.values()) {
  115. offset_counter++;
  116. if (offset_counter > offset) {
  117. for (const rf of runFilters) {
  118. const [tag, condition] = rf(v);
  119. v.element.classList.toggle(tag, condition);
  120. }
  121. }
  122. }
  123. }
  124.  
  125. filterAll = (offset) => {
  126. const applyFilters = Object.assign({}, ...Object.keys(this.dataFilters).map(f => ({ [f]: this.state[f] })));
  127. this.filter_(applyFilters, offset);
  128. }
  129.  
  130. handleLoadedHTML = (html, container, removeDuplicates = false) => {
  131. const thumbs = this.rules.GET_THUMBS(html);
  132. const data_offset = this.data.size;
  133.  
  134. for (const thumbElement of thumbs) {
  135. const url = this.rules.THUMB_URL(thumbElement);
  136. if (!url || this.data.has(url)) {
  137. if (removeDuplicates) thumbElement.remove();
  138. continue;
  139. }
  140.  
  141. const { title, duration } = this.rules.THUMB_DATA(thumbElement);
  142. this.data.set(url, { element: thumbElement, duration, title });
  143.  
  144. const { img, imgSrc } = this.rules.THUMB_IMG_DATA(thumbElement);
  145. this.lazyImgLoader.lazify(thumbElement, img, imgSrc);
  146.  
  147. const parent = container || this.rules.CONTAINER;
  148. if (!parent.contains(thumbElement)) parent.appendChild(thumbElement);
  149. }
  150.  
  151. this.filterAll(data_offset);
  152. };
  153. }
  154.  

QingJ © 2025

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