pervert-monkey 1.0.15 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/core/pervertmonkey.core.es.d.ts +23 -16
  2. package/dist/core/pervertmonkey.core.es.js +157 -153
  3. package/dist/core/pervertmonkey.core.es.js.map +1 -1
  4. package/dist/core/pervertmonkey.core.umd.js +157 -153
  5. package/dist/core/pervertmonkey.core.umd.js.map +1 -1
  6. package/dist/userscripts/3hentai.user.js +10 -4
  7. package/dist/userscripts/camgirlfinder.user.js +2 -2
  8. package/dist/userscripts/camwhores.user.js +4 -5
  9. package/dist/userscripts/e-hentai.user.js +7 -6
  10. package/dist/userscripts/ebalka.user.js +12 -8
  11. package/dist/userscripts/eporner.user.js +5 -2
  12. package/dist/userscripts/erome.user.js +7 -11
  13. package/dist/userscripts/eroprofile.user.js +2 -2
  14. package/dist/userscripts/javhdporn.user.js +2 -2
  15. package/dist/userscripts/missav.user.js +3 -8
  16. package/dist/userscripts/motherless.user.js +3 -3
  17. package/dist/userscripts/namethatporn.user.js +4 -4
  18. package/dist/userscripts/nhentai.user.js +2 -2
  19. package/dist/userscripts/obmenvsem.user.js +3 -2
  20. package/dist/userscripts/pornhub.user.js +2 -2
  21. package/dist/userscripts/spankbang.user.js +6 -7
  22. package/dist/userscripts/thisvid.user.js +7 -8
  23. package/dist/userscripts/xhamster.user.js +6 -6
  24. package/dist/userscripts/xvideos.user.js +8 -8
  25. package/package.json +1 -1
  26. package/src/core/data-handler/data-filter-fn-defaults.ts +8 -8
  27. package/src/core/data-handler/data-filter-fn.ts +10 -8
  28. package/src/core/data-handler/data-filter.ts +10 -8
  29. package/src/core/data-handler/data-manager.ts +52 -106
  30. package/src/core/infinite-scroll/index.ts +6 -4
  31. package/src/core/parsers/thumb-data-parser.ts +11 -4
  32. package/src/userscripts/index.ts +1 -1
  33. package/src/userscripts/scripts/3hentai.ts +5 -3
  34. package/src/userscripts/scripts/camgirlfinder.ts +1 -1
  35. package/src/userscripts/scripts/camwhores.ts +3 -4
  36. package/src/userscripts/scripts/e-hentai.ts +9 -7
  37. package/src/userscripts/scripts/ebalka.ts +15 -8
  38. package/src/userscripts/scripts/eporner.ts +4 -1
  39. package/src/userscripts/scripts/erome.ts +6 -12
  40. package/src/userscripts/scripts/eroprofile.ts +1 -1
  41. package/src/userscripts/scripts/javhdporn.ts +1 -1
  42. package/src/userscripts/scripts/missav.ts +2 -7
  43. package/src/userscripts/scripts/motherless.ts +4 -3
  44. package/src/userscripts/scripts/namethatporn.ts +3 -3
  45. package/src/userscripts/scripts/nhentai.ts +1 -1
  46. package/src/userscripts/scripts/obmenvsem.ts +6 -2
  47. package/src/userscripts/scripts/pornhub.ts +1 -1
  48. package/src/userscripts/scripts/spankbang.ts +4 -6
  49. package/src/userscripts/scripts/thisvid.ts +6 -6
  50. package/src/userscripts/scripts/xhamster.ts +5 -5
  51. package/src/userscripts/scripts/xvideos.ts +7 -7
  52. package/src/utils/dom/dom-observers.ts +3 -3
  53. package/src/utils/dom/index.ts +10 -7
  54. package/src/utils/index.ts +4 -22
  55. package/src/utils/performance/index.ts +44 -0
@@ -16,6 +16,8 @@ export declare function chunks<T>(arr: T[], size: number): T[][];
16
16
 
17
17
  export declare function circularShift(n: number, c?: number, s?: number): number;
18
18
 
19
+ export declare function containMutation(container: HTMLElement, callback: () => void): void;
20
+
19
21
  export declare function copyAttributes<T extends Element = HTMLElement>(target: T, source: T): void;
20
22
 
21
23
  export declare type DataElement = {
@@ -26,13 +28,13 @@ export declare type DataElement = {
26
28
  export declare class DataFilter {
27
29
  private rules;
28
30
  filters: Map<string, () => DataFilterFnRendered>;
31
+ filterMapping: Record<string, string>;
29
32
  constructor(rules: Rules);
30
- static isFiltered(el: HTMLElement): boolean;
31
- applyCSSFilters(wrapper?: (cssRule: string) => string): void;
33
+ static isFiltered(e: HTMLElement): boolean;
34
+ createCssFilters(wrapper?: (cssRule: string) => string): void;
32
35
  customDataFilterFns: Record<string, DataFilterFnFrom<any>>;
33
36
  registerFilters(customFilters: (Record<string, DataFilterFnFrom<any>> | string)[]): void;
34
37
  registerFilter(customSelectorName: string): void;
35
- filterMapping: Record<string, string>;
36
38
  selectFilters(filters: {
37
39
  [key: string]: boolean;
38
40
  }): (() => DataFilterFnRendered)[];
@@ -43,7 +45,8 @@ declare class DataFilterFn<R> {
43
45
  deps: string[];
44
46
  name: string;
45
47
  $preDefine?: ((state: StoreState) => R) | undefined;
46
- private tag;
48
+ static prefix: string;
49
+ static setPrefix(name: string): string;
47
50
  constructor(handle: DataFilterFnHandle<R>, deps: string[] | undefined, name: string, $preDefine?: ((state: StoreState) => R) | undefined);
48
51
  static from<R>(options: DataFilterFnFrom<R>, name: string): DataFilterFn<R>;
49
52
  renderFn(state: StoreState): () => DataFilterFnRendered;
@@ -56,7 +59,7 @@ declare type DataFilterFnHandle<R> = (el: DataElement, state: StoreState, $preDe
56
59
  declare type DataFilterFnRendered = (v: DataElement) => DataFilterFnRenderedResult;
57
60
 
58
61
  declare type DataFilterFnRenderedResult = {
59
- tag: string;
62
+ name: string;
60
63
  condition: boolean;
61
64
  };
62
65
 
@@ -67,9 +70,9 @@ export declare class DataManager {
67
70
  private lazyImgLoader;
68
71
  dataFilter: DataFilter;
69
72
  constructor(rules: Rules, containerHomogenity?: Parameters<typeof checkHomogenity>[2] | undefined);
70
- applyFilters: (filters?: Record<string, boolean>, offset?: number) => Promise<void>;
71
- filterAll: (offset?: number) => Promise<void>;
72
- parseData: (html: HTMLElement, container?: HTMLElement, removeDuplicates?: boolean, shouldLazify?: boolean) => void;
73
+ applyFilters(filters?: Record<string, boolean>, offset?: number): Promise<void>;
74
+ filterAll(offset?: number): Promise<void>;
75
+ parseData(html: HTMLElement, container?: HTMLElement, removeDuplicates?: boolean, shouldLazify?: boolean): Promise<void>;
73
76
  sortBy<K extends keyof DataElement>(key: K, direction?: boolean): void;
74
77
  }
75
78
 
@@ -197,11 +200,11 @@ declare const DefaultScheme: [{
197
200
  }];
198
201
  }];
199
202
 
200
- export declare function downloader(options?: {
201
- append: string;
202
- after: string;
203
- button: string;
204
- cbBefore: () => void;
203
+ export declare function downloader(options: {
204
+ append?: string;
205
+ after?: string;
206
+ buttonHtml: string;
207
+ doBefore?: () => void;
205
208
  }): void;
206
209
 
207
210
  export declare function exterminateVideo(video: HTMLVideoElement): void;
@@ -220,6 +223,8 @@ export declare function fetchWith<T extends JSON | string | HTMLElement>(input:
220
223
 
221
224
  export declare function findNextSibling<T extends Element = HTMLElement>(e: T): Element | null;
222
225
 
226
+ export declare function findSelfOrChild<T extends HTMLElement>(element: T, selector: string): T | null;
227
+
223
228
  /**
224
229
  * Converts a duration string (e.g., "1h 22min 3sec") to HH:MM:SS format.
225
230
  * @param timeStr - The duration string to format.
@@ -258,6 +263,8 @@ declare type InfiniteScrollerOptions = Pick<InfiniteScroller, 'rules'> & Partial
258
263
 
259
264
  export declare function instantiateTemplate(sourceSelector: string, attributeUpdates: Record<string, string>, contentUpdates: Record<string, string>): string;
260
265
 
266
+ export declare function irange(start?: number, step?: number): Generator<number, void, unknown>;
267
+
261
268
  declare type IScrollerSubject = {
262
269
  type: 'scroll';
263
270
  scroller: InfiniteScroller;
@@ -380,8 +387,6 @@ export declare function parseUrl(s: HTMLAnchorElement | Location | URL | string)
380
387
 
381
388
  declare type Primitive = string | number | boolean;
382
389
 
383
- declare type PrimitiveString = 'boolean' | 'string' | 'number' | 'float' | 'duration';
384
-
385
390
  export declare function querySelectorLast<T extends Element = HTMLElement>(root: ParentNode | undefined, selector: string): T | undefined;
386
391
 
387
392
  export declare function querySelectorLastNumber(selector: string, e?: ParentNode): number;
@@ -444,6 +449,8 @@ export declare class Rules {
444
449
  constructor(options: Partial<Rules>);
445
450
  }
446
451
 
452
+ export declare function runIdleJob<T>(iterator: Iterator<T>, job: (v: T) => void): Promise<unknown>;
453
+
447
454
  export declare function sanitizeStr(s: string): string;
448
455
 
449
456
  declare type SchemeKeys = JabroniTypes.ExtractValuesByKey<typeof DefaultScheme, 'title'>;
@@ -472,7 +479,7 @@ export declare class ThumbDataParser {
472
479
  declare type ThumbDataSelector = {
473
480
  name: string;
474
481
  selector: string;
475
- type: PrimitiveString;
482
+ type: 'boolean' | 'string' | 'number' | 'float' | 'duration';
476
483
  };
477
484
 
478
485
  declare type ThumbDataSelectorsRaw = Record<string, string | Pick<ThumbDataSelector, 'selector' | 'type'>>;
@@ -11,21 +11,23 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
11
11
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
12
12
  var _i2, _n2, _t, _e2, _s2, _l2, _o2, _d, _p2, _g, _C_instances, r_fn, R_fn, b_fn, u_fn, m_fn, a_fn, P_fn, E_fn, S_fn, O_fn, k_fn, x_fn, h_fn, f_fn, T_fn, A_fn, y_fn, w_fn, c_fn, C_fn, _a, _i3, _n3, _t2, _e3, _s3, _l3, _b;
13
13
  import { GM_addStyle } from "vite-plugin-monkey/dist/client";
14
- class DataFilterFn {
14
+ const _DataFilterFn = class _DataFilterFn {
15
15
  constructor(handle, deps = [], name, $preDefine) {
16
- __publicField(this, "tag");
17
16
  this.handle = handle;
18
17
  this.deps = deps;
19
18
  this.name = name;
20
19
  this.$preDefine = $preDefine;
21
- this.tag = `filter-${name}`;
20
+ this.name = _DataFilterFn.setPrefix(name);
21
+ }
22
+ static setPrefix(name) {
23
+ return `${_DataFilterFn.prefix}${name}`;
22
24
  }
23
25
  static from(options, name) {
24
26
  if (typeof options === "function") {
25
27
  const deps = [name];
26
- return new DataFilterFn(options, deps, name);
28
+ return new _DataFilterFn(options, deps, name);
27
29
  }
28
- return new DataFilterFn(
30
+ return new _DataFilterFn(
29
31
  options.handle,
30
32
  options.deps,
31
33
  name,
@@ -33,20 +35,19 @@ class DataFilterFn {
33
35
  );
34
36
  }
35
37
  renderFn(state) {
36
- const tag = this.tag;
38
+ const name = this.name;
37
39
  return () => {
38
40
  var _a3;
39
41
  const preDefined = (_a3 = this.$preDefine) == null ? void 0 : _a3.call(this, state);
40
42
  return (a2) => {
41
43
  const condition = this.handle(a2, state, preDefined);
42
- return {
43
- condition,
44
- tag
45
- };
44
+ return { condition, name };
46
45
  };
47
46
  };
48
47
  }
49
- }
48
+ };
49
+ __publicField(_DataFilterFn, "prefix", "filter-");
50
+ let DataFilterFn = _DataFilterFn;
50
51
  function chunks(arr, size) {
51
52
  return Array.from(
52
53
  { length: Math.ceil(arr.length / size) },
@@ -117,10 +118,10 @@ function sanitizeStr(s) {
117
118
  }
118
119
  function waitForElementToAppear(parent, selector, callback) {
119
120
  const observer = new MutationObserver((_mutations) => {
120
- const el2 = parent.querySelector(selector);
121
- if (el2) {
121
+ const e = parent.querySelector(selector);
122
+ if (e) {
122
123
  observer.disconnect();
123
- callback(el2);
124
+ callback(e);
124
125
  }
125
126
  });
126
127
  observer.observe(document.body, { childList: true, subtree: true });
@@ -171,6 +172,12 @@ function watchDomChangesWithThrottle(element, callback, throttle = 1e3, times =
171
172
  observer.observe(element, options);
172
173
  return observer;
173
174
  }
175
+ function findSelfOrChild(element, selector) {
176
+ if (element.matches(selector)) {
177
+ return element;
178
+ }
179
+ return element.querySelector(selector);
180
+ }
174
181
  function querySelectorLast(root = document, selector) {
175
182
  const nodes = root.querySelectorAll(selector);
176
183
  return nodes.length > 0 ? nodes[nodes.length - 1] : void 0;
@@ -219,7 +226,7 @@ function removeClassesAndDataAttributes(element, keyword) {
219
226
  });
220
227
  }
221
228
  function getCommonParents(elements) {
222
- const parents = Array.from(elements).map((el2) => el2.parentElement).filter((parent) => parent !== null);
229
+ const parents = Array.from(elements).map((e) => e.parentElement).filter((parent) => parent !== null);
223
230
  return [...new Set(parents)];
224
231
  }
225
232
  function findNextSibling(e) {
@@ -263,15 +270,15 @@ function exterminateVideo(video) {
263
270
  video.load();
264
271
  video.remove();
265
272
  }
266
- function downloader(options = { append: "", after: "", button: "", cbBefore: () => {
267
- } }) {
273
+ function downloader(options) {
268
274
  var _a3, _b2;
269
- const btn = parseHtml(options.button);
275
+ const btn = parseHtml(options.buttonHtml);
270
276
  if (options.append) (_a3 = document.querySelector(options.append)) == null ? void 0 : _a3.append(btn);
271
277
  if (options.after) (_b2 = document.querySelector(options.after)) == null ? void 0 : _b2.after(btn);
272
- btn.addEventListener("click", (e) => {
278
+ btn == null ? void 0 : btn.addEventListener("click", (e) => {
279
+ var _a4;
273
280
  e.preventDefault();
274
- if (options.cbBefore) options.cbBefore();
281
+ (_a4 = options.doBefore) == null ? void 0 : _a4.call(options);
275
282
  waitForElementToAppear(document.body, "video", (video) => {
276
283
  window.location.href = video.getAttribute("src");
277
284
  });
@@ -465,12 +472,48 @@ function parseDataParams(str) {
465
472
  function parseCssUrl(s) {
466
473
  return s.replace(/url\("|"\).*/g, "");
467
474
  }
475
+ function runIdleJob(iterator2, job) {
476
+ return new Promise((resolve) => {
477
+ const scheduler = window.requestIdleCallback || ((cb) => {
478
+ return setTimeout(() => {
479
+ cb({
480
+ didTimeout: true,
481
+ timeRemaining: () => 50
482
+ });
483
+ }, 1);
484
+ });
485
+ function runBatch(deadline) {
486
+ while (deadline.timeRemaining() > 0) {
487
+ const { value, done } = iterator2.next();
488
+ if (done) {
489
+ resolve(true);
490
+ return;
491
+ }
492
+ job(value);
493
+ }
494
+ scheduler(runBatch);
495
+ }
496
+ scheduler(runBatch);
497
+ });
498
+ }
499
+ function containMutation(container, callback) {
500
+ container.style.contain = "content";
501
+ try {
502
+ callback();
503
+ } finally {
504
+ requestAnimationFrame(() => {
505
+ requestAnimationFrame(() => {
506
+ container.style.contain = "none";
507
+ });
508
+ });
509
+ }
510
+ }
468
511
  function createTextFilter(filterName, dataPropName, positive) {
469
512
  const filterNameValue = `${filterName}Words`;
470
513
  return {
471
- handle(el2, state, searchFilter) {
514
+ handle(e, state, searchFilter) {
472
515
  if (!Object.hasOwn(state, filterName) || !state[filterName]) return false;
473
- return !(searchFilter == null ? void 0 : searchFilter(el2[dataPropName]));
516
+ return !(searchFilter == null ? void 0 : searchFilter(e[dataPropName]));
474
517
  },
475
518
  $preDefine: (state) => {
476
519
  const r = new RegexFilter(state[filterNameValue]);
@@ -481,9 +524,9 @@ function createTextFilter(filterName, dataPropName, positive) {
481
524
  };
482
525
  }
483
526
  const filterDuration = {
484
- handle(el2, state, notInRange) {
527
+ handle(e, state, notInRange) {
485
528
  if (!state.filterDuration) return false;
486
- return !!(notInRange == null ? void 0 : notInRange(el2.duration));
529
+ return !!(notInRange == null ? void 0 : notInRange(e.duration));
487
530
  },
488
531
  $preDefine: (state) => {
489
532
  const from = state.filterDurationFrom;
@@ -501,26 +544,27 @@ const defaultDataFilterFns = {
501
544
  filterInclude: createTextFilter("filterInclude", "title", true),
502
545
  filterUploaderExclude: createTextFilter("filterUploaderExclude", "uploader", false),
503
546
  filterUploaderInclude: createTextFilter("filterUploaderInclude", "uploader", true),
504
- filterHD: (el2, state) => state.filterHD && !el2.hd,
505
- filterNonHD: (el2, state) => state.filterNonHD && el2.hd,
506
- filterPrivate: (el2, state) => state.filterPrivate && el2.private,
507
- filterPublic: (el2, state) => state.filterPublic && !el2.private
547
+ filterHD: (e, state) => state.filterHD && !e.hd,
548
+ filterNonHD: (e, state) => state.filterNonHD && e.hd,
549
+ filterPrivate: (e, state) => state.filterPrivate && e.private,
550
+ filterPublic: (e, state) => state.filterPublic && !e.private
508
551
  };
509
552
  class DataFilter {
510
553
  constructor(rules) {
511
554
  __publicField(this, "filters", /* @__PURE__ */ new Map());
512
- __publicField(this, "customDataFilterFns", {});
513
555
  __publicField(this, "filterMapping", {});
556
+ __publicField(this, "customDataFilterFns", {});
514
557
  this.rules = rules;
515
558
  this.registerFilters(rules.customDataFilterFns);
516
- this.applyCSSFilters();
559
+ this.createCssFilters();
517
560
  }
518
- static isFiltered(el2) {
519
- return el2.className.includes("filter-");
561
+ static isFiltered(e) {
562
+ return e.className.includes(DataFilterFn.prefix);
520
563
  }
521
- applyCSSFilters(wrapper) {
564
+ createCssFilters(wrapper) {
522
565
  this.filters.forEach((_2, name) => {
523
- const cssRule = `.filter-${name} { display: none !important; }`;
566
+ const className = DataFilterFn.setPrefix(name);
567
+ const cssRule = `.${className} { display: none !important; }`;
524
568
  GM_addStyle(wrapper ? wrapper(cssRule) : cssRule);
525
569
  });
526
570
  }
@@ -533,12 +577,12 @@ class DataFilter {
533
577
  });
534
578
  }
535
579
  registerFilter(customSelectorName) {
536
- var _a3;
537
580
  const dataFilterFn = DataFilterFn.from(
538
581
  this.customDataFilterFns[customSelectorName],
539
582
  customSelectorName
540
583
  );
541
- (_a3 = [customSelectorName, ...dataFilterFn.deps]) == null ? void 0 : _a3.forEach((name) => {
584
+ dataFilterFn.deps.push(customSelectorName);
585
+ dataFilterFn.deps.forEach((name) => {
542
586
  Object.assign(this.filterMapping, { [name]: customSelectorName });
543
587
  });
544
588
  this.filters.set(customSelectorName, dataFilterFn.renderFn(this.rules.store.state));
@@ -555,129 +599,82 @@ class DataManager {
555
599
  (target) => !DataFilter.isFiltered(target)
556
600
  ));
557
601
  __publicField(this, "dataFilter");
558
- __publicField(this, "applyFilters", async (filters = {}, offset = 0) => {
559
- const filtersToApply = this.dataFilter.selectFilters(filters);
560
- if (filtersToApply.length === 0) return;
561
- const iterator2 = this.data.values().drop(offset);
562
- let finished = false;
563
- const updates = [];
564
- await new Promise((resolve) => {
565
- function runBatch(deadline) {
566
- while (deadline.timeRemaining() > 0) {
567
- const { value, done } = iterator2.next();
568
- finished = !!done;
569
- if (done) break;
570
- for (const f of filtersToApply) {
571
- const { tag, condition } = f()(value);
572
- updates.push({ e: value.element, tag, condition });
573
- }
574
- }
575
- if (!finished) {
576
- requestIdleCallback(runBatch);
577
- } else {
578
- resolve(true);
579
- }
580
- }
581
- requestIdleCallback(runBatch);
582
- });
583
- const parents = new Set(updates.map((u) => u.e.parentElement));
584
- requestAnimationFrame(() => {
585
- const revertDisplayStyle = [...parents].map((p) => {
586
- const display = p == null ? void 0 : p.style.display;
587
- if (!display) return void 0;
588
- p.style.display = "none";
589
- p.style.contain = "layout style paint";
590
- p.style.willChange = "contents";
591
- return () => {
592
- p.style.display = display;
593
- requestAnimationFrame(() => {
594
- p.style.willChange = "auto";
595
- });
596
- };
597
- });
598
- updates.forEach((u) => {
599
- u.e.classList.toggle(u.tag, u.condition);
602
+ this.rules = rules;
603
+ this.containerHomogenity = containerHomogenity;
604
+ this.dataFilter = new DataFilter(this.rules);
605
+ }
606
+ async applyFilters(filters = {}, offset = 0) {
607
+ const filtersToApply = this.dataFilter.selectFilters(filters);
608
+ if (filtersToApply.length === 0) return;
609
+ const iterator2 = this.data.values().drop(offset);
610
+ const updates = [];
611
+ await runIdleJob(iterator2, (v2) => {
612
+ for (const f of filtersToApply) {
613
+ const { name, condition } = f()(v2);
614
+ updates.push({ e: v2.element, name, condition });
615
+ }
616
+ });
617
+ const parents = Map.groupBy(updates, (u) => u.e.parentElement);
618
+ parents.forEach((v2, parent) => {
619
+ const f = () => {
620
+ v2.forEach((u) => {
621
+ u.e.classList.toggle(u.name, u.condition);
600
622
  });
601
- revertDisplayStyle.forEach((f) => {
602
- f == null ? void 0 : f();
623
+ };
624
+ if (!parent) {
625
+ f();
626
+ } else {
627
+ requestAnimationFrame(() => {
628
+ containMutation(parent, f);
603
629
  });
604
- });
630
+ }
605
631
  });
606
- __publicField(this, "filterAll", async (offset) => {
607
- const keys = Array.from(this.dataFilter.filters.keys());
608
- const filters = Object.fromEntries(
609
- keys.map((k2) => [k2, this.rules.store.state[k2]])
632
+ }
633
+ async filterAll(offset) {
634
+ const keys = Array.from(this.dataFilter.filters.keys());
635
+ const filters = Object.fromEntries(
636
+ keys.map((k2) => [k2, this.rules.store.state[k2]])
637
+ );
638
+ await this.applyFilters(filters, offset);
639
+ }
640
+ async parseData(html, container, removeDuplicates = false, shouldLazify = true) {
641
+ const thumbs = this.rules.thumbsParser.getThumbs(html);
642
+ const dataOffset = this.data.size;
643
+ const fragment = document.createDocumentFragment();
644
+ const parent = container || this.rules.container;
645
+ const homogenity = !!this.containerHomogenity;
646
+ for (const thumbElement of thumbs) {
647
+ const url = this.rules.thumbDataParser.getUrl(thumbElement);
648
+ const isHomogenic = homogenity && !checkHomogenity(
649
+ parent,
650
+ thumbElement.parentElement,
651
+ this.containerHomogenity
610
652
  );
611
- await this.applyFilters(filters, offset);
612
- });
613
- __publicField(this, "parseData", (html, container, removeDuplicates = false, shouldLazify = true) => {
614
- const thumbs = this.rules.thumbsParser.getThumbs(html);
615
- const dataOffset = this.data.size;
616
- const fragment = document.createDocumentFragment();
617
- const parent = container || this.rules.container;
618
- const homogenity = !!this.containerHomogenity;
619
- if (parent) {
620
- parent.style.contain = "layout style paint";
621
- parent.style.willChange = "contents";
653
+ if (!url || this.data.has(url) || parent !== container && (parent == null ? void 0 : parent.contains(thumbElement)) || isHomogenic) {
654
+ if (removeDuplicates) thumbElement.remove();
655
+ continue;
622
656
  }
623
- for (const thumbElement of thumbs) {
624
- const url = this.rules.thumbDataParser.getUrl(thumbElement);
625
- if (!url || this.data.has(url) || parent !== container && (parent == null ? void 0 : parent.contains(thumbElement)) || homogenity && !checkHomogenity(
626
- parent,
627
- thumbElement.parentElement,
628
- this.containerHomogenity
629
- )) {
630
- if (removeDuplicates) thumbElement.remove();
631
- continue;
632
- }
633
- const data = this.rules.thumbDataParser.getThumbData(thumbElement);
634
- this.data.set(url, { element: thumbElement, ...data });
635
- if (shouldLazify) {
636
- const { img, imgSrc } = this.rules.thumbImgParser.getImgData(thumbElement);
637
- this.lazyImgLoader.lazify(thumbElement, img, imgSrc);
638
- }
639
- fragment.append(thumbElement);
657
+ const data = this.rules.thumbDataParser.getThumbData(thumbElement);
658
+ this.data.set(url, { element: thumbElement, ...data });
659
+ if (shouldLazify) {
660
+ const { img, imgSrc } = this.rules.thumbImgParser.getImgData(thumbElement);
661
+ this.lazyImgLoader.lazify(thumbElement, img, imgSrc);
640
662
  }
641
- this.filterAll(dataOffset).then(() => {
642
- requestAnimationFrame(() => {
643
- parent == null ? void 0 : parent.appendChild(fragment);
644
- });
645
- });
646
- });
647
- this.rules = rules;
648
- this.containerHomogenity = containerHomogenity;
649
- this.dataFilter = new DataFilter(this.rules);
663
+ fragment.append(thumbElement);
664
+ }
665
+ await this.filterAll(dataOffset);
666
+ if (!parent) return;
667
+ containMutation(parent, () => parent == null ? void 0 : parent.appendChild(fragment));
650
668
  }
651
669
  sortBy(key, direction = true) {
652
670
  if (this.data.size < 2) return;
653
- const elements = this.data.values().toArray().filter((e) => e.element.parentElement !== null).map((e) => e);
654
- const containers = new Set(elements.map((e) => e.element.parentElement));
655
- containers.forEach((c) => {
656
- c.style.contain = "layout style paint";
657
- c.style.willChange = "contents";
658
- });
659
- const elementsByContainers = /* @__PURE__ */ new Map();
660
- containers.forEach((c) => {
661
- elementsByContainers.set(c, []);
662
- });
663
- elements.forEach((e) => {
664
- const parent = e.element.parentElement;
665
- const container = elementsByContainers.get(parent);
666
- container == null ? void 0 : container.push(e);
667
- });
671
+ const ds2 = this.data.values().toArray().filter((e) => e.element.parentElement !== null);
672
+ const byContainers = Map.groupBy(ds2, (e) => e.element.parentElement);
668
673
  const dir = direction ? -1 : 1;
669
- for (const [container, items] of elementsByContainers) {
674
+ for (const [container, items] of byContainers) {
670
675
  items.sort((a2, b2) => (a2[key] - b2[key]) * dir);
671
- const domNodes = items.map((e) => e.element);
672
- const display = container.style.display;
673
- container.style.display = "none";
674
- container.replaceChildren(...domNodes);
675
- requestAnimationFrame(() => {
676
- container.style.display = display;
677
- requestAnimationFrame(() => {
678
- container.style.willChange = "auto";
679
- });
680
- });
676
+ const children = items.map((e) => e.element);
677
+ containMutation(container, () => container.replaceChildren(...children));
681
678
  }
682
679
  }
683
680
  }
@@ -2014,10 +2011,8 @@ class InfiniteScroller {
2014
2011
  }
2015
2012
  async doScroll(url, offset) {
2016
2013
  const page = await this.getPaginationData(url);
2017
- const prevScrollPos = document.documentElement.scrollTop;
2018
2014
  this.paginationOffset = Math.max(this.paginationOffset, offset);
2019
2015
  this.subject.next({ type: "scroll", scroller: this, page });
2020
- window.scrollTo(0, prevScrollPos);
2021
2016
  if (this.rules.store.state.writeHistory) {
2022
2017
  history.replaceState({}, "", url);
2023
2018
  }
@@ -2039,7 +2034,10 @@ class InfiniteScroller {
2039
2034
  infiniteScroller.subject.subscribe((x2) => {
2040
2035
  if (x2.type === "scroll") {
2041
2036
  rules.store.state.$paginationOffset = x2.scroller.paginationOffset;
2042
- rules.dataManager.parseData(x2.page);
2037
+ const prevScrollPos = document.documentElement.scrollTop;
2038
+ rules.dataManager.parseData(x2.page).then(() => {
2039
+ window.scrollTo(0, prevScrollPos);
2040
+ });
2043
2041
  }
2044
2042
  });
2045
2043
  rules.store.stateSubject.subscribe(() => {
@@ -3075,8 +3073,10 @@ class ThumbDataParser {
3075
3073
  }
3076
3074
  autoParseText(thumb) {
3077
3075
  var _a3;
3078
- const title = sanitizeStr(thumb.innerText);
3079
- const duration = timeToSeconds(((_a3 = title.match(/\d+m|\d+:\d+/)) == null ? void 0 : _a3[0]) || "");
3076
+ let title = sanitizeStr(thumb.innerText);
3077
+ const durationStr = ((_a3 = title.match(/(\d+:\d+:?\d+?)|\d+m/)) == null ? void 0 : _a3[0]) || "";
3078
+ const duration = timeToSeconds(durationStr);
3079
+ title = title.replaceAll(durationStr, "");
3080
3080
  return { title, duration };
3081
3081
  }
3082
3082
  getUrl(thumb) {
@@ -10013,6 +10013,7 @@ export {
10013
10013
  checkHomogenity,
10014
10014
  chunks,
10015
10015
  circularShift,
10016
+ containMutation,
10016
10017
  copyAttributes,
10017
10018
  downloader,
10018
10019
  exterminateVideo,
@@ -10021,10 +10022,12 @@ export {
10021
10022
  fetchText,
10022
10023
  fetchWith,
10023
10024
  findNextSibling,
10025
+ findSelfOrChild,
10024
10026
  formatTimeToHHMMSS,
10025
10027
  getCommonParents,
10026
10028
  getPaginationStrategy,
10027
10029
  instantiateTemplate,
10030
+ irange,
10028
10031
  memoize,
10029
10032
  objectToFormData,
10030
10033
  parseCssUrl,
@@ -10039,6 +10042,7 @@ export {
10039
10042
  range,
10040
10043
  removeClassesAndDataAttributes,
10041
10044
  replaceElementTag,
10045
+ runIdleJob,
10042
10046
  sanitizeStr,
10043
10047
  splitWith,
10044
10048
  timeToSeconds,