data-manager

handles loaded html, takes care of data, applying filters

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

  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.0
  8. // @match *://*/*
  9. // ==/UserScript==
  10. /* globals */
  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: new class {
  43. tag = 'filtered-public';
  44. createFilter = () => {
  45. return (v) => [this.tag, !this.rules.IS_PRIVATE(v.element) && this.state.filterPublic]
  46. }
  47. },
  48. filterPrivate: new class {
  49. tag = 'filtered-private';
  50. createFilter = () => {
  51. return (v) => [this.tag, this.rules.IS_PRIVATE(v.element) && this.state.filterPrivate]
  52. }
  53. },
  54. filterDuration: new class {
  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: new class {
  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: new class {
  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. // add filters style
  94. const tags = Object.keys(this.dataFilters).map(k => `.${this.dataFilters[k].tag}`).join(',');
  95. GM_addStyle(`${tags} { display: none !important; }`);
  96.  
  97. // bind state
  98. Object.values(this.dataFilters).forEach(f => {
  99. f.state = this.state;
  100. f.rules = this.rules;
  101. });
  102. }
  103.  
  104. isFiltered(el) {
  105. return el.className.includes('filtered');
  106. }
  107.  
  108. filter_ = (filters, offset = 0) => {
  109. const runFilters = [];
  110.  
  111. for (const f of Object.keys(filters)) {
  112. runFilters.push(this.dataFilters[f].createFilter());
  113. }
  114.  
  115. let offset_counter = 1;
  116. for (const v of this.data.values()) {
  117. offset_counter++;
  118. if (offset_counter > offset) {
  119. for (const rf of runFilters) {
  120. const [tag, condition] = rf(v);
  121. v.element.classList.toggle(tag, condition);
  122. }
  123. }
  124. }
  125. }
  126.  
  127. filterAll = (offset) => {
  128. const applyFilters = Object.assign({}, ...Object.keys(this.dataFilters).map(f => ({ [f]: state[f] })));
  129. this.filter_(applyFilters, offset);
  130. }
  131.  
  132. handleLoadedHTML = (html, container) => {
  133. const thumbs = this.rules.GET_THUMBS(html);
  134.  
  135. const data_offset = this.data.size;
  136.  
  137. for (const thumbElement of thumbs) {
  138. const url = this.rules.THUMB_URL(thumbElement);
  139. if (!url || this.data.has(url)) continue;
  140.  
  141. const { title, duration } = this.rules.THUMB_DATA(thumbElement);
  142.  
  143. this.data.set(url, { element: thumbElement, duration, title });
  144.  
  145. const { img, imgSrc } = this.rules.THUMB_IMG_DATA(thumbElement);
  146. this.lazyImgLoader.lazify(thumbElement, img, imgSrc);
  147.  
  148. (container || this.rules.CONTAINER).appendChild(thumbElement);
  149. }
  150.  
  151. this.filterAll(data_offset);
  152. };
  153. }
  154.  

QingJ © 2025

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