pervert-monkey 1.0.9 → 1.0.11

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 (67) hide show
  1. package/dist/core/pervertmonkey.core.es.d.ts +126 -59
  2. package/dist/core/pervertmonkey.core.es.js +1051 -366
  3. package/dist/core/pervertmonkey.core.es.js.map +1 -1
  4. package/dist/core/pervertmonkey.core.umd.js +1051 -366
  5. package/dist/core/pervertmonkey.core.umd.js.map +1 -1
  6. package/dist/userscripts/3hentai.user.js +14 -8
  7. package/dist/userscripts/camgirlfinder.user.js +1 -1
  8. package/dist/userscripts/camwhores.user.js +15 -9
  9. package/dist/userscripts/e-hentai.user.js +12 -6
  10. package/dist/userscripts/ebalka.user.js +11 -5
  11. package/dist/userscripts/eporner.user.js +15 -10
  12. package/dist/userscripts/erome.user.js +11 -10
  13. package/dist/userscripts/eroprofile.user.js +9 -5
  14. package/dist/userscripts/javhdporn.user.js +9 -5
  15. package/dist/userscripts/missav.user.js +10 -6
  16. package/dist/userscripts/motherless.user.js +11 -7
  17. package/dist/userscripts/namethatporn.user.js +17 -13
  18. package/dist/userscripts/nhentai.user.js +12 -6
  19. package/dist/userscripts/obmenvsem.user.js +14 -18
  20. package/dist/userscripts/pornhub.user.js +14 -10
  21. package/dist/userscripts/spankbang.user.js +10 -6
  22. package/dist/userscripts/thisvid.user.js +27 -21
  23. package/dist/userscripts/xhamster.user.js +26 -26
  24. package/dist/userscripts/xvideos.user.js +19 -17
  25. package/package.json +6 -3
  26. package/src/core/{data-control → data-handler}/data-filter.ts +3 -8
  27. package/src/core/{data-control → data-handler}/data-manager.ts +8 -7
  28. package/src/core/data-handler/index.ts +2 -0
  29. package/src/core/index.ts +4 -4
  30. package/src/core/infinite-scroll/index.ts +29 -38
  31. package/src/core/parsers/index.ts +4 -0
  32. package/src/core/{pagination-parsing → parsers/pagination-parser}/index.ts +3 -0
  33. package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-strategies/PaginationStrategy.ts +1 -1
  34. package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-strategies/PaginationStrategyDataParams.ts +1 -1
  35. package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-strategies/PaginationStrategyPathnameParams.ts +2 -1
  36. package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-utils/index.ts +1 -4
  37. package/src/core/parsers/thumb-data-parser.ts +115 -0
  38. package/src/core/parsers/thumb-img-parser.ts +65 -0
  39. package/src/core/parsers/thumbs-parser.ts +29 -0
  40. package/src/core/rules/index.ts +39 -207
  41. package/src/userscripts/index.ts +1 -1
  42. package/src/userscripts/scripts/3hentai.ts +15 -7
  43. package/src/userscripts/scripts/camwhores.ts +18 -13
  44. package/src/userscripts/scripts/e-hentai.ts +12 -7
  45. package/src/userscripts/scripts/ebalka.ts +11 -5
  46. package/src/userscripts/scripts/eporner.ts +16 -10
  47. package/src/userscripts/scripts/erome.ts +11 -10
  48. package/src/userscripts/scripts/eroprofile.ts +10 -6
  49. package/src/userscripts/scripts/javhdporn.ts +9 -5
  50. package/src/userscripts/scripts/missav.ts +10 -6
  51. package/src/userscripts/scripts/motherless.ts +12 -8
  52. package/src/userscripts/scripts/namethatporn.ts +20 -15
  53. package/src/userscripts/scripts/nhentai.ts +13 -8
  54. package/src/userscripts/scripts/obmenvsem.ts +31 -15
  55. package/src/userscripts/scripts/pornhub.ts +21 -21
  56. package/src/userscripts/scripts/spankbang.ts +11 -43
  57. package/src/userscripts/scripts/thisvid.ts +31 -25
  58. package/src/userscripts/scripts/xhamster.ts +31 -32
  59. package/src/userscripts/scripts/xvideos.ts +18 -16
  60. package/src/utils/index.ts +7 -19
  61. package/src/utils/observers/index.ts +8 -4
  62. package/src/utils/parsers/index.ts +4 -0
  63. package/src/utils/parsers/time-parser.ts +2 -2
  64. package/src/utils/strings/index.ts +2 -0
  65. package/src/core/data-control/index.ts +0 -2
  66. /package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-strategies/PaginationStrategySearchParams.ts +0 -0
  67. /package/src/core/{pagination-parsing → parsers/pagination-parser}/pagination-strategies/index.ts +0 -0
@@ -0,0 +1,115 @@
1
+ import {
2
+ querySelectorLast,
3
+ querySelectorText,
4
+ sanitizeStr,
5
+ timeToSeconds,
6
+ } from '../../utils';
7
+
8
+ type Primitive = string | number | boolean;
9
+ type PrimitiveString = 'boolean' | 'string' | 'number' | 'duration';
10
+ type ThumbData = Record<string, Primitive>;
11
+ type ThumbDataSelector = {
12
+ name: string;
13
+ selector: string;
14
+ type: PrimitiveString;
15
+ };
16
+ type ThumbDataSelectorsRaw = Record<
17
+ string,
18
+ string | Pick<ThumbDataSelector, 'selector' | 'type'>
19
+ >;
20
+
21
+ export class ThumbDataParser {
22
+ private autoParseText(thumb: HTMLElement): ThumbData {
23
+ const title = sanitizeStr(thumb.innerText);
24
+ const duration = timeToSeconds(title.match(/\d+m|\d+:\d+/)?.[0] || '');
25
+ return { title, duration };
26
+ }
27
+
28
+ private preprocessCustomThumbDataSelectors() {
29
+ if (!this.selectors) return;
30
+ Object.entries(this.selectors).forEach(([key, value]) => {
31
+ if (typeof value === 'string') {
32
+ const defaultSelector = this.defaultThumbDataSelectors.find((e) => e.name === key);
33
+ if (!defaultSelector) {
34
+ this.thumbDataSelectors.push({ name: key, selector: value, type: 'string' });
35
+ } else {
36
+ defaultSelector.selector = value;
37
+ this.thumbDataSelectors.push(defaultSelector);
38
+ }
39
+ } else {
40
+ this.thumbDataSelectors.push({ name: key, ...value });
41
+ }
42
+ });
43
+ }
44
+
45
+ private thumbDataSelectors: ThumbDataSelector[] = [];
46
+ private readonly defaultThumbDataSelectors: ThumbDataSelector[] = [
47
+ { name: 'title', type: 'string', selector: '[class *= title],[title]' },
48
+ {
49
+ name: 'uploader',
50
+ type: 'string',
51
+ selector: '[class *= uploader], [class *= user], [class *= name]',
52
+ },
53
+ { name: 'duration', type: 'duration', selector: '[class *= duration]' },
54
+ ];
55
+
56
+ private getThumbDataWith(
57
+ thumb: HTMLElement,
58
+ { type, selector }: ThumbDataSelector,
59
+ ): Primitive {
60
+ if (type === 'boolean') {
61
+ return !!thumb.querySelector(selector);
62
+ }
63
+ if (type === 'string') {
64
+ return sanitizeStr(querySelectorLast(thumb, selector)?.innerText || '');
65
+ }
66
+ if (type === 'duration') {
67
+ return timeToSeconds(querySelectorText(thumb, selector));
68
+ }
69
+ return Number.parseInt(querySelectorText(thumb, selector));
70
+ }
71
+
72
+ constructor(
73
+ public strategy: 'manual' | 'auto-select' | 'auto-text' = 'manual',
74
+ public selectors: ThumbDataSelectorsRaw = {},
75
+ public callback?: (thumb: HTMLElement, thumbData: ThumbData) => void,
76
+ public stringsMeltInTitle = true,
77
+ ) {
78
+ this.preprocessCustomThumbDataSelectors();
79
+ }
80
+
81
+ public static create(
82
+ o: Partial<
83
+ Pick<ThumbDataParser, 'strategy' | 'selectors' | 'callback' | 'stringsMeltInTitle'>
84
+ > = {},
85
+ ) {
86
+ return new ThumbDataParser(o.strategy, o.selectors, o.callback, o.stringsMeltInTitle);
87
+ }
88
+
89
+ public getThumbData(thumb: HTMLElement): ThumbData {
90
+ if (this.strategy === 'auto-text') {
91
+ return this.autoParseText(thumb);
92
+ }
93
+
94
+ if (this.strategy === 'auto-select') {
95
+ this.thumbDataSelectors = this.defaultThumbDataSelectors;
96
+ }
97
+
98
+ const thumbData = Object.fromEntries(
99
+ this.thumbDataSelectors.map((s) => [s.name, this.getThumbDataWith(thumb, s)]),
100
+ );
101
+
102
+ if (this.stringsMeltInTitle) {
103
+ Object.entries(thumbData).forEach(([k, v]) => {
104
+ if (typeof v === 'string' && k !== 'title') {
105
+ thumbData.title = `${thumbData.title} ${k}:${v}`;
106
+ delete thumbData[k];
107
+ }
108
+ });
109
+ }
110
+
111
+ this.callback?.(thumb, thumbData);
112
+
113
+ return thumbData;
114
+ }
115
+ }
@@ -0,0 +1,65 @@
1
+ import { removeClassesAndDataAttributes } from '../../utils';
2
+
3
+ export class ThumbImgParser {
4
+ public selector?: string | string[] | ((img: HTMLImageElement) => string);
5
+ public remove?: 'auto' | string;
6
+ public strategy: 'default' | 'auto' = 'default';
7
+
8
+ public static create(
9
+ options: Partial<
10
+ Pick<ThumbImgParser, 'selector' | 'remove' | 'strategy' | 'getImgData'>
11
+ > = {},
12
+ ) {
13
+ return Object.assign(new ThumbImgParser(), options);
14
+ }
15
+
16
+ private removeAttrs(img: HTMLImageElement) {
17
+ if (!this.remove) return;
18
+ if (this.remove === 'auto') {
19
+ removeClassesAndDataAttributes(img, 'lazy');
20
+ } else {
21
+ if (this.remove.startsWith('.')) {
22
+ img.classList.remove(this.remove.slice(1));
23
+ } else {
24
+ img.removeAttribute(this.remove);
25
+ }
26
+ }
27
+ }
28
+
29
+ private getImgSrc(img: HTMLImageElement) {
30
+ const possibleAttrs = (this.selector as string)
31
+ ? [this.selector as string].flat()
32
+ : ['data-src', 'src'];
33
+
34
+ for (const attr of possibleAttrs) {
35
+ const imgSrc = img.getAttribute(attr);
36
+ if (imgSrc) {
37
+ return imgSrc;
38
+ }
39
+ }
40
+
41
+ return '';
42
+ }
43
+
44
+ public getImgData(thumb: HTMLElement) {
45
+ if (this.strategy === 'default' && !this.selector) return {};
46
+
47
+ const img = thumb.querySelector<HTMLImageElement>('img');
48
+ if (!img) return {};
49
+
50
+ const imgSrc =
51
+ typeof this.selector === 'function' ? this.selector(img) : this.getImgSrc(img);
52
+
53
+ this.removeAttrs(img);
54
+
55
+ if (img.src.includes('data:image')) {
56
+ img.src = '';
57
+ }
58
+
59
+ if (img.complete && img.naturalWidth > 0) {
60
+ return {};
61
+ }
62
+
63
+ return { img, imgSrc };
64
+ }
65
+ }
@@ -0,0 +1,29 @@
1
+ export class ThumbsParser {
2
+ public selector = '.thumb';
3
+ public strategy: 'default' | 'auto' = 'default';
4
+ public transform?: (thumb: HTMLElement) => void;
5
+
6
+ public static create(options: Partial<Pick<ThumbsParser, 'selector' | 'strategy' | 'transform'>> = {}, containerSelector: string) {
7
+ return Object.assign(new ThumbsParser(containerSelector), options);
8
+ }
9
+ constructor(public containerSelector: string) { }
10
+
11
+ public getThumbs(html: HTMLElement): HTMLElement[] {
12
+ if (!html) return [];
13
+ let thumbs: HTMLElement[];
14
+
15
+ if (this.strategy === 'auto') {
16
+ if (typeof this.selector !== 'string') return [];
17
+ const container = html.querySelector(this.containerSelector);
18
+ thumbs = [...(container?.children || [])] as HTMLElement[];
19
+ }
20
+
21
+ thumbs = Array.from(html.querySelectorAll<HTMLElement>(this.selector));
22
+
23
+ if (typeof this.transform === 'function') {
24
+ thumbs.forEach(this.transform);
25
+ }
26
+
27
+ return thumbs;
28
+ }
29
+ }
@@ -2,174 +2,46 @@ import { JabronioGUI, JabronioStore, type JabroniTypes, setupScheme } from 'jabr
2
2
  import {
3
3
  getCommonParents,
4
4
  querySelectorLast,
5
- querySelectorText,
6
- removeClassesAndDataAttributes,
7
- sanitizeStr,
8
- timeToSeconds,
9
5
  waitForElementToDisappear,
10
6
  } from '../../utils';
11
- import { DataManager } from '../data-control';
12
- import type { DataSelectorFn } from '../data-control/data-filter';
13
- import { InfiniteScroller, type OffsetGenerator } from '../infinite-scroll/';
7
+ import { DataManager, type DataSelectorFn } from '../data-handler';
8
+ import { InfiniteScroller, type OffsetGenerator } from '../infinite-scroll';
14
9
  import { DefaultScheme, type SchemeOptions, StoreStateDefault } from '../jabroni-config';
15
- import { getPaginationStrategy } from '../pagination-parsing';
16
- import type { PaginationStrategy } from '../pagination-parsing/pagination-strategies';
17
-
18
- type ThumbData = {
19
- title: string;
20
- duration?: number;
21
- } & { [x: string]: string | boolean | number };
22
-
23
- type _CustomThumbDataSelector = {
24
- selector: string;
25
- type: 'boolean' | 'string' | 'number';
26
- };
27
-
28
- type CustomThumbDataSelector = {
29
- [x: string]: _CustomThumbDataSelector;
30
- };
31
-
32
- export class RulesGlobal {
33
- public delay?: number;
34
-
35
- public customGenerator?: OffsetGenerator;
36
-
10
+ import {
11
+ getPaginationStrategy,
12
+ type PaginationStrategy,
13
+ ThumbDataParser,
14
+ ThumbImgParser,
15
+ ThumbsParser
16
+ } from '../parsers';
17
+
18
+ export class Rules {
37
19
  public getThumbUrl(thumb: HTMLElement | HTMLAnchorElement) {
38
20
  return ((thumb.querySelector('a[href]') || thumb) as HTMLAnchorElement).href;
39
21
  }
40
22
 
41
- public titleSelector: undefined | string;
42
- public uploaderSelector: undefined | string;
43
- public durationSelector: undefined | string;
44
-
45
- public customThumbDataSelectors: undefined | CustomThumbDataSelector;
46
- public getThumbDataStrategy: 'default' | 'auto-select' | 'auto-text' = 'default';
47
-
48
- public getThumbDataCallback?: (thumb: HTMLElement, thumbData: ThumbData) => void;
49
-
50
- public getThumbData(thumb: HTMLElement): ThumbData {
51
- let { titleSelector, uploaderSelector, durationSelector } = this;
52
- const thumbData: ThumbData = { title: '' };
23
+ public thumb: Parameters<typeof ThumbDataParser.create>[0] = {};
24
+ public thumbDataParser: ThumbDataParser;
53
25
 
54
- if (this.getThumbDataStrategy === 'auto-text') {
55
- const text = sanitizeStr(thumb.innerText);
56
- thumbData.title = text;
57
- thumbData.duration = timeToSeconds(text.match(/\d+m|\d+:\d+/)?.[0] || '');
58
- return thumbData;
59
- }
60
-
61
- if (this.getThumbDataStrategy === 'auto-select') {
62
- titleSelector = '[class *= title],[title]';
63
- durationSelector = '[class *= duration]';
64
- uploaderSelector = '[class *= uploader], [class *= user], [class *= name]';
65
- }
66
-
67
- if (this.getThumbDataStrategy === 'auto-select') {
68
- const selected = querySelectorLast(thumb, titleSelector as string);
69
- if (selected) {
70
- thumbData.title = sanitizeStr(selected.innerText as string);
71
- } else {
72
- thumbData.title = sanitizeStr(thumb.innerText);
73
- }
74
- } else {
75
- thumbData.title = querySelectorText(thumb, titleSelector);
76
- }
77
-
78
- if (uploaderSelector) {
79
- const uploader = querySelectorText(thumb, uploaderSelector);
80
- thumbData.title = `${thumbData.title} user:${uploader}`;
81
- }
82
-
83
- if (durationSelector) {
84
- const duration = timeToSeconds(querySelectorText(thumb, durationSelector));
85
- thumbData.duration = duration;
86
- }
26
+ public thumbImg: Parameters<typeof ThumbImgParser.create>[0] = {};
27
+ public thumbImgParser: ThumbImgParser;
87
28
 
88
- this.getThumbDataCallback?.(thumb, thumbData);
29
+ public thumbs: Parameters<typeof ThumbsParser.create>[0] = {};
30
+ public thumbsParser: ThumbsParser;
89
31
 
90
- function getCustomThumbData(
91
- selector: _CustomThumbDataSelector['selector'],
92
- type: _CustomThumbDataSelector['type'],
93
- ): string | number | boolean {
94
- if (type === 'boolean') {
95
- return !!thumb.querySelector(selector);
96
- }
97
- if (type === 'string') {
98
- return querySelectorText(thumb, selector);
99
- }
100
- return Number.parseInt(querySelectorText(thumb, selector));
101
- }
32
+ public containerSelector: string | (() => HTMLElement) = '.container';
33
+ public containerSelectorLast?: string;
102
34
 
103
- if (this.customThumbDataSelectors) {
104
- Object.entries(this.customThumbDataSelectors).forEach(([name, x]) => {
105
- const data = getCustomThumbData(x.selector, x.type);
106
- Object.assign(thumbData, { [name]: data });
107
- });
35
+ get container() {
36
+ if (typeof this.containerSelectorLast === 'string') {
37
+ return querySelectorLast(document, this.containerSelectorLast) as HTMLElement;
108
38
  }
109
-
110
- return thumbData;
111
- }
112
-
113
- public getThumbImgDataAttrSelector?:
114
- | string
115
- | string[]
116
- | ((img: HTMLImageElement) => string);
117
- public getThumbImgDataAttrDelete?: 'auto' | string;
118
- public getThumbImgDataStrategy: 'default' | 'auto' = 'default';
119
-
120
- public getThumbImgData(thumb: HTMLElement) {
121
- const result: { img?: HTMLImageElement; imgSrc?: string } = {};
122
-
123
- if (this.getThumbImgDataStrategy === 'auto') {
124
- const img = thumb.querySelector<HTMLImageElement>('img');
125
- if (!img) return {};
126
-
127
- result.img = img;
128
-
129
- if (typeof this.getThumbImgDataAttrSelector === 'function') {
130
- result.imgSrc = this.getThumbImgDataAttrSelector(img);
131
- } else {
132
- const possibleAttrs = this.getThumbImgDataAttrSelector
133
- ? [this.getThumbImgDataAttrSelector].flat()
134
- : ['data-src', 'src'];
135
-
136
- for (const attr of possibleAttrs) {
137
- const imgSrc = img.getAttribute(attr);
138
- if (imgSrc) {
139
- result.imgSrc = imgSrc;
140
- img.removeAttribute(attr);
141
- break;
142
- }
143
- }
144
- }
145
-
146
- if (this.getThumbImgDataAttrDelete) {
147
- if (this.getThumbImgDataAttrDelete === 'auto') {
148
- removeClassesAndDataAttributes(img, 'lazy');
149
- } else {
150
- if (this.getThumbImgDataAttrDelete.startsWith('.')) {
151
- img.classList.remove(this.getThumbImgDataAttrDelete.slice(1));
152
- } else {
153
- img.removeAttribute(this.getThumbImgDataAttrDelete);
154
- }
155
- }
156
-
157
- if (img.src.includes('data:image')) {
158
- result.img.src = '';
159
- }
160
-
161
- if (img.complete && img.naturalWidth > 0) {
162
- return {};
163
- }
164
- }
39
+ if (typeof this.containerSelector === 'string') {
40
+ return document.querySelector<HTMLElement>(this.containerSelector) as HTMLElement;
165
41
  }
166
-
167
- return result;
42
+ return this.containerSelector();
168
43
  }
169
44
 
170
- public containerSelector: string | (() => HTMLElement) = '.container';
171
- public containerSelectorLast?: string;
172
-
173
45
  public intersectionObservableSelector?: string;
174
46
 
175
47
  public get intersectionObservable() {
@@ -184,42 +56,11 @@ export class RulesGlobal {
184
56
  this.paginationStrategy.getPaginationElement()) as HTMLElement;
185
57
  }
186
58
 
187
- get container() {
188
- if (typeof this.containerSelectorLast === 'string') {
189
- return querySelectorLast(document, this.containerSelectorLast) as HTMLElement;
190
- }
191
- if (typeof this.containerSelector === 'string') {
192
- return document.querySelector<HTMLElement>(this.containerSelector) as HTMLElement;
193
- }
194
- return this.containerSelector();
195
- }
196
-
197
- public thumbsSelector = '.thumb';
198
- public getThumbsStrategy: 'default' | 'auto' = 'default';
199
- public getThumbsTransform?: (thumb: HTMLElement) => void;
200
-
201
- public getThumbs(html: HTMLElement): HTMLElement[] {
202
- if (!html) return [];
203
- let thumbs: HTMLElement[];
204
-
205
- if (this.getThumbsStrategy === 'auto') {
206
- if (typeof this.containerSelector !== 'string') return [];
207
- const container = html.querySelector(this.containerSelector);
208
- thumbs = [...(container?.children || [])] as HTMLElement[];
209
- }
210
-
211
- thumbs = Array.from(html.querySelectorAll<HTMLElement>(this.thumbsSelector));
212
-
213
- if (typeof this.getThumbsTransform === 'function') {
214
- thumbs.forEach(this.getThumbsTransform);
215
- }
216
-
217
- return thumbs;
218
- }
219
-
220
59
  public paginationStrategyOptions: Partial<PaginationStrategy> = {};
221
60
  public paginationStrategy: PaginationStrategy;
222
61
 
62
+ public dataManager: DataManager;
63
+ public dataHomogenity: ConstructorParameters<typeof DataManager>[1];
223
64
  public customDataSelectorFns: (Record<string, DataSelectorFn<any>> | string)[] = [
224
65
  'filterInclude',
225
66
  'filterExclude',
@@ -229,6 +70,9 @@ export class RulesGlobal {
229
70
  public animatePreview?: (doc: HTMLElement) => void;
230
71
 
231
72
  public storeOptions?: JabroniTypes.StoreStateOptions;
73
+ public schemeOptions: SchemeOptions = [];
74
+ public store: JabronioStore;
75
+ public gui: JabronioGUI;
232
76
 
233
77
  private createStore() {
234
78
  const config = { ...StoreStateDefault, ...this.storeOptions };
@@ -236,8 +80,6 @@ export class RulesGlobal {
236
80
  return this.store;
237
81
  }
238
82
 
239
- public schemeOptions: SchemeOptions = [];
240
-
241
83
  private createGui() {
242
84
  const scheme = setupScheme(
243
85
  this.schemeOptions as Parameters<typeof setupScheme>[0],
@@ -247,10 +89,7 @@ export class RulesGlobal {
247
89
  return this.gui;
248
90
  }
249
91
 
250
- public store: JabronioStore;
251
- public gui: JabronioGUI;
252
- public dataManager: DataManager;
253
-
92
+ public customGenerator?: OffsetGenerator;
254
93
  public infiniteScroller?: InfiniteScroller;
255
94
  public getPaginationData?: InfiniteScroller['getPaginationData'];
256
95
 
@@ -268,7 +107,7 @@ export class RulesGlobal {
268
107
  this.dataManager?.parseData(this.container, this.container);
269
108
  }
270
109
  if (this.gropeStrategy === 'all-in-all') {
271
- getCommonParents(this.getThumbs(document.body)).forEach((c) => {
110
+ getCommonParents(this.thumbsParser.getThumbs(document.body)).forEach((c) => {
272
111
  this.dataManager.parseData(c, c, true);
273
112
  });
274
113
  }
@@ -307,17 +146,6 @@ export class RulesGlobal {
307
146
  });
308
147
  }
309
148
 
310
- public dataManagerOptions: Partial<DataManager> = {};
311
-
312
- private setupDataManager() {
313
- this.dataManager = new DataManager(this);
314
- if (this.dataManagerOptions) {
315
- Object.assign(this.dataManager, this.dataManagerOptions);
316
- }
317
-
318
- return this.dataManager;
319
- }
320
-
321
149
  private mutationObservers: MutationObserver[] = [];
322
150
 
323
151
  public resetOnPaginationOrContainerDeath = true;
@@ -352,7 +180,7 @@ export class RulesGlobal {
352
180
 
353
181
  this.paginationStrategy = getPaginationStrategy(this.paginationStrategyOptions);
354
182
 
355
- this.setupDataManager();
183
+ this.dataManager = new DataManager(this, this.dataHomogenity);
356
184
  this.setupStoreListeners();
357
185
 
358
186
  this.resetInfiniteScroller();
@@ -366,17 +194,21 @@ export class RulesGlobal {
366
194
  this.resetOn();
367
195
  }
368
196
 
369
- constructor(options: Partial<RulesGlobal>) {
197
+ constructor(options: Partial<Rules>) {
370
198
  if (this.isEmbedded) throw Error('Embedded is not supported');
371
199
 
372
200
  Object.assign(this, options);
373
201
 
202
+ this.thumbDataParser = ThumbDataParser.create(this.thumb);
203
+ this.thumbImgParser = ThumbImgParser.create(this.thumbImg);
204
+ this.thumbsParser = ThumbsParser.create(this.thumbs, this.containerSelector as string);
205
+
374
206
  this.paginationStrategy = getPaginationStrategy(this.paginationStrategyOptions);
375
207
 
376
208
  this.store = this.createStore();
377
209
  this.gui = this.createGui();
378
210
 
379
- this.dataManager = this.setupDataManager();
211
+ this.dataManager = new DataManager(this, this.dataHomogenity);
380
212
 
381
213
  this.reset();
382
214
  // console.log('data', this.dataManager.data.values().toArray());
@@ -1 +1 @@
1
- import './scripts/xvideos';
1
+ import './scripts/eporner'
@@ -1,5 +1,5 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
- import { RulesGlobal } from '../../core';
2
+ import { Rules } from '../../core';
3
3
  import { circularShift, OnHover, Tick } from '../../utils';
4
4
 
5
5
  export const meta: MonkeyUserScript = {
@@ -9,11 +9,19 @@ export const meta: MonkeyUserScript = {
9
9
  match: 'https://*.3hentai.net/*',
10
10
  };
11
11
 
12
- const rules = new RulesGlobal({
12
+ const rules = new Rules({
13
13
  containerSelectorLast: '.listing-container',
14
- thumbsSelector: '.doujin-col',
15
- titleSelector: '.title',
16
- getThumbImgDataStrategy: 'auto',
14
+ thumbs: {
15
+ selector: '.doujin-col'
16
+ },
17
+ thumb: {
18
+ selectors: {
19
+ title: '.title',
20
+ }
21
+ },
22
+ thumbImg: {
23
+ strategy: 'auto',
24
+ },
17
25
  gropeStrategy: 'all-in-all',
18
26
  customDataSelectorFns: ['filterInclude', 'filterExclude'],
19
27
  schemeOptions: ['Text Filter', 'Badge', 'Advanced'],
@@ -22,7 +30,7 @@ const rules = new RulesGlobal({
22
30
 
23
31
  function animatePreview() {
24
32
  const tick = new Tick(500, false);
25
- const end = 999;
33
+ const end = 9999;
26
34
 
27
35
  function rotate(src: string) {
28
36
  return src.replace(/(\d+)(?=t\.jpg$)/,
@@ -36,7 +44,7 @@ function animatePreview() {
36
44
  const t = e as HTMLImageElement;
37
45
  const origin = t.src;
38
46
  t.src = t.src.replace(/\w+\.\w+$/, '1t.jpg');
39
- t.onerror = (_) => { t.src = origin };
47
+ t.onerror = (_) => tick.stop();
40
48
  tick.start(
41
49
  () => {
42
50
  t.src = rotate(t.src);
@@ -1,13 +1,13 @@
1
1
  import { LSKDB } from 'lskdb';
2
2
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
3
3
  import { GM_addStyle, unsafeWindow } from '$';
4
- import { getPaginationStrategy, InfiniteScroller, RulesGlobal } from '../../core';
4
+ import { getPaginationStrategy, InfiniteScroller, Rules } from '../../core';
5
5
  import {
6
6
  circularShift,
7
7
  downloader,
8
8
  fetchHtml,
9
- objectToFormData,
10
9
  OnHover,
10
+ objectToFormData,
11
11
  parseHtml,
12
12
  querySelectorLastNumber,
13
13
  querySelectorText,
@@ -39,22 +39,27 @@ const IS_COMMUNITY_LIST = /\/members\/$/.test(location.pathname);
39
39
  const IS_VIDEO_PAGE = /^(\/videos)?\/\d+\//.test(location.pathname);
40
40
  const IS_LOGGED_IN = document.cookie.includes('kt_member');
41
41
 
42
- const rules = new RulesGlobal({
42
+ const rules = new Rules({
43
43
  containerSelector:
44
44
  '[id*="playlist"]:has(> .item .title),[id*="videos"]:has(> .item .title),form:has(>.item .title)',
45
45
  paginationStrategyOptions: {
46
46
  paginationSelector: '.pagination:not([id *= member])',
47
47
  overwritePaginationLast: IS_MEMBER_PAGE ? () => 1 : (x) => (x === 9 ? 9999 : x),
48
48
  },
49
- getThumbImgDataAttrSelector: 'data-original',
50
- getThumbImgDataStrategy: 'auto',
51
- thumbsSelector:
52
- '.list-videos .item, .playlist .item, .list-playlists > div > .item, .item:has(.title)',
53
- gropeStrategy: 'all-in-all',
54
- getThumbDataStrategy: 'auto-select',
55
- customThumbDataSelectors: {
56
- private: { type: 'boolean', selector: '[class*=private]' },
49
+ thumbs: {
50
+ selector:
51
+ '.list-videos .item, .playlist .item, .list-playlists > div > .item, .item:has(.title)'
57
52
  },
53
+ thumb: {
54
+ strategy: 'auto-select',
55
+ selectors: {
56
+ private: { type: 'boolean', selector: '[class*=private]' },
57
+ },
58
+ },
59
+ thumbImg: {
60
+ selector: 'data-original',
61
+ },
62
+ gropeStrategy: 'all-in-all',
58
63
  customDataSelectorFns: [
59
64
  'filterInclude',
60
65
  'filterExclude',
@@ -175,7 +180,7 @@ async function getMemberFriends(id: number | string) {
175
180
  const paginationStrategy = getPaginationStrategy({
176
181
  doc,
177
182
  url,
178
- overwritePaginationLast: (x) => (x === 9 ? 999 : x),
183
+ overwritePaginationLast: (x: number) => (x === 9 ? 999 : x),
179
184
  });
180
185
 
181
186
  const gen = InfiniteScroller.generatorForPaginationStrategy(paginationStrategy);
@@ -301,7 +306,7 @@ async function acceptFriendRequest(id: number | string) {
301
306
  async function clearMessages() {
302
307
  const pages = InfiniteScroller.generatorForPaginationStrategy(
303
308
  getPaginationStrategy({
304
- overwritePaginationLast: (x) => (x === 9 ? 999 : x),
309
+ overwritePaginationLast: (x: number) => (x === 9 ? 999 : x),
305
310
  }),
306
311
  );
307
312