pervert-monkey 1.0.0 → 1.0.1

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 (46) hide show
  1. package/README.md +0 -3
  2. package/dist/core/pervertmonkey.core.es.d.ts +4 -4
  3. package/dist/core/pervertmonkey.core.es.js +24 -1
  4. package/dist/core/pervertmonkey.core.es.js.map +1 -1
  5. package/dist/core/pervertmonkey.core.umd.js +24 -1
  6. package/dist/core/pervertmonkey.core.umd.js.map +1 -1
  7. package/dist/userscripts/3hentai.user.js +1045 -1049
  8. package/dist/userscripts/camgirlfinder.user.js +38 -42
  9. package/dist/userscripts/camwhores.user.js +1429 -1433
  10. package/dist/userscripts/e-hentai.user.js +1077 -1081
  11. package/dist/userscripts/ebalka.user.js +1092 -1096
  12. package/dist/userscripts/eporner.user.js +1129 -1133
  13. package/dist/userscripts/erome.user.js +1109 -1113
  14. package/dist/userscripts/eroprofile.user.js +1062 -1066
  15. package/dist/userscripts/javhdporn.user.js +1046 -1050
  16. package/dist/userscripts/missav.user.js +1047 -1051
  17. package/dist/userscripts/motherless.user.js +1223 -1227
  18. package/dist/userscripts/namethatporn.user.js +1083 -1087
  19. package/dist/userscripts/nhentai.user.js +1123 -1127
  20. package/dist/userscripts/pornhub.user.js +1065 -1069
  21. package/dist/userscripts/spankbang.user.js +1097 -1101
  22. package/dist/userscripts/thisvid.user.js +2660 -0
  23. package/dist/userscripts/xhamster.user.js +1224 -1228
  24. package/dist/userscripts/xvideos.user.js +1116 -1120
  25. package/package.json +9 -9
  26. package/src/core/index.ts +4 -0
  27. package/src/index.ts +2 -42
  28. package/src/userscripts/scripts/3hentai.ts +1 -1
  29. package/src/userscripts/scripts/camwhores.ts +8 -9
  30. package/src/userscripts/scripts/e-hentai.ts +2 -2
  31. package/src/userscripts/scripts/ebalka.ts +2 -3
  32. package/src/userscripts/scripts/eporner.ts +2 -2
  33. package/src/userscripts/scripts/erome.ts +1 -1
  34. package/src/userscripts/scripts/eroprofile.ts +1 -1
  35. package/src/userscripts/scripts/javhdporn.ts +1 -1
  36. package/src/userscripts/scripts/missav.ts +1 -1
  37. package/src/userscripts/scripts/motherless.ts +2 -4
  38. package/src/userscripts/scripts/namethatporn.ts +1 -1
  39. package/src/userscripts/scripts/nhentai.ts +2 -2
  40. package/src/userscripts/scripts/pornhub.ts +1 -1
  41. package/src/userscripts/scripts/spankbang.ts +4 -5
  42. package/src/userscripts/scripts/xhamster.ts +5 -5
  43. package/src/userscripts/scripts/xvideos.ts +2 -3
  44. package/src/utils/index.ts +39 -0
  45. package/src/vite-env.d.ts +1 -1
  46. package/src/userscripts/scripts/thisvid.ts +0 -716
@@ -19,1584 +19,1580 @@
19
19
  // @run-at document-idle
20
20
  // ==/UserScript==
21
21
 
22
- (function () {
22
+ (function (jabroniOutfit) {
23
23
  'use strict';
24
24
 
25
- (function (jabroniOutfit) {
26
-
27
- class LSKDB {
28
- constructor(prefix = "lsm-", lockKey = "lsmngr-lock") {
29
- this.prefix = prefix;
30
- this.lockKey = lockKey;
31
- }
32
- prefixedKey(key) {
33
- return `${this.prefix}${key}`;
34
- }
35
- getAllKeys() {
36
- const res = [];
37
- for (const key in localStorage) {
38
- if (key.startsWith(this.prefix)) {
39
- res.push(key.slice(this.prefix.length));
40
- }
25
+ class LSKDB {
26
+ constructor(prefix = "lsm-", lockKey = "lsmngr-lock") {
27
+ this.prefix = prefix;
28
+ this.lockKey = lockKey;
29
+ }
30
+ prefixedKey(key) {
31
+ return `${this.prefix}${key}`;
32
+ }
33
+ getAllKeys() {
34
+ const res = [];
35
+ for (const key in localStorage) {
36
+ if (key.startsWith(this.prefix)) {
37
+ res.push(key.slice(this.prefix.length));
41
38
  }
42
- return res;
43
39
  }
44
- getKeys(n = 12, remove = true) {
45
- const res = [];
46
- for (const key in localStorage) {
47
- if (res.length >= n) break;
48
- if (key.startsWith(this.prefix)) {
49
- res.push(key.slice(this.prefix.length));
50
- }
51
- }
52
- if (remove) {
53
- res.forEach((k) => this.removeKey(k));
40
+ return res;
41
+ }
42
+ getKeys(n = 12, remove = true) {
43
+ const res = [];
44
+ for (const key in localStorage) {
45
+ if (res.length >= n) break;
46
+ if (key.startsWith(this.prefix)) {
47
+ res.push(key.slice(this.prefix.length));
54
48
  }
55
- return res;
56
- }
57
- hasKey(key) {
58
- return localStorage.getItem(this.prefixedKey(key)) !== null;
59
- }
60
- removeKey(key) {
61
- localStorage.removeItem(this.prefixedKey(key));
62
- }
63
- setKey(key) {
64
- localStorage.setItem(this.prefixedKey(key), "");
65
49
  }
66
- isLocked() {
67
- const lock = parseInt(localStorage.getItem(this.lockKey));
68
- const locktime = 5 * 60 * 1e3;
69
- return !(!lock || Date.now() - lock > locktime);
50
+ if (remove) {
51
+ res.forEach((k) => this.removeKey(k));
70
52
  }
71
- lock(value) {
72
- if (value) {
73
- localStorage.setItem(this.lockKey, JSON.stringify(Date.now()));
74
- } else {
75
- localStorage.removeItem(this.lockKey);
76
- }
53
+ return res;
54
+ }
55
+ hasKey(key) {
56
+ return localStorage.getItem(this.prefixedKey(key)) !== null;
57
+ }
58
+ removeKey(key) {
59
+ localStorage.removeItem(this.prefixedKey(key));
60
+ }
61
+ setKey(key) {
62
+ localStorage.setItem(this.prefixedKey(key), "");
63
+ }
64
+ isLocked() {
65
+ const lock = parseInt(localStorage.getItem(this.lockKey));
66
+ const locktime = 5 * 60 * 1e3;
67
+ return !(!lock || Date.now() - lock > locktime);
68
+ }
69
+ lock(value) {
70
+ if (value) {
71
+ localStorage.setItem(this.lockKey, JSON.stringify(Date.now()));
72
+ } else {
73
+ localStorage.removeItem(this.lockKey);
77
74
  }
78
75
  }
76
+ }
79
77
 
80
- var _GM_addStyle = (() => typeof GM_addStyle != "undefined" ? GM_addStyle : undefined)();
81
- var _unsafeWindow = (() => typeof unsafeWindow != "undefined" ? unsafeWindow : undefined)();
78
+ var _GM_addStyle = (() => typeof GM_addStyle != "undefined" ? GM_addStyle : undefined)();
79
+ var _unsafeWindow = (() => typeof unsafeWindow != "undefined" ? unsafeWindow : undefined)();
82
80
 
83
- function wait(milliseconds) {
84
- return new Promise((resolve) => setTimeout(resolve, milliseconds));
85
- }
81
+ function memoize(fn) {
82
+ const cache = new Map();
83
+ const memoizedFunction = ((...args) => {
84
+ const key = JSON.stringify(args);
85
+ if (cache.has(key)) {
86
+ return cache.get(key);
87
+ }
88
+ const result = fn(...args);
89
+ cache.set(key, result);
90
+ return result;
91
+ });
92
+ return memoizedFunction;
93
+ }
94
+
95
+ function objectToFormData(obj) {
96
+ const formData = new FormData();
97
+ Object.entries(obj).forEach(([k, v]) => {
98
+ formData.append(k, v);
99
+ });
100
+ return formData;
101
+ }
102
+
103
+ function splitWith(s, c = ",") {
104
+ return s.split(c).map((s2) => s2.trim()).filter(Boolean);
105
+ }
106
+ function sanitizeStr(s) {
107
+ return s?.replace(/\n|\t/g, " ").replace(/ {2,}/g, " ").trim() || "";
108
+ }
86
109
 
87
- function splitWith(s, c = ",") {
88
- return s.split(c).map((s2) => s2.trim()).filter(Boolean);
110
+ class RegexFilter {
111
+ regexes;
112
+ constructor(str, flags = "gi") {
113
+ this.regexes = memoize(this.compileSearchRegex)(str, flags);
89
114
  }
90
- function sanitizeStr(s) {
91
- return s?.replace(/\n|\t/g, " ").replace(/ {2,}/g, " ").trim() || "";
115
+ compileSearchRegex(str, flags) {
116
+ try {
117
+ if (str.startsWith("r:")) return [new RegExp(str.slice(2), flags)];
118
+ const regexes = splitWith(str).map(
119
+ (s) => s.replace(/f:(\w+)/g, (_, w) => `(^|\\ |,)${w}($|\\ |,)`)
120
+ ).map((_) => new RegExp(_, flags));
121
+ return regexes;
122
+ } catch (_) {
123
+ return [];
124
+ }
92
125
  }
93
-
94
- function waitForElementToAppear(parent, selector, callback) {
95
- const observer = new MutationObserver((_mutations) => {
96
- const el = parent.querySelector(selector);
97
- if (el) {
98
- observer.disconnect();
99
- callback(el);
100
- }
101
- });
102
- observer.observe(document.body, { childList: true, subtree: true });
103
- return observer;
104
- }
105
- function waitForElementToDisappear(observable, callback) {
106
- const observer = new MutationObserver((_mutations) => {
107
- if (!observable.isConnected) {
108
- observer.disconnect();
109
- callback();
110
- }
111
- });
112
- observer.observe(document.body, { childList: true, subtree: true });
113
- return observer;
126
+ hasEvery(str) {
127
+ return this.regexes.every((r) => r.test(str));
128
+ }
129
+ hasNone(str) {
130
+ return this.regexes.every((r) => !r.test(str));
114
131
  }
132
+ }
115
133
 
116
- function querySelectorLast(root = document, selector) {
117
- const nodes = root.querySelectorAll(selector);
118
- return nodes.length > 0 ? nodes[nodes.length - 1] : void 0;
119
- }
120
- function querySelectorLastNumber(selector, e = document) {
121
- const text = querySelectorText(e, selector);
122
- return Number(text.match(/\d+/g)?.pop() || 0);
123
- }
124
- function querySelectorText(e, selector) {
125
- if (typeof selector !== "string") return "";
126
- const text = e.querySelector(selector)?.innerText || "";
127
- return sanitizeStr(text);
128
- }
129
- function parseHtml(html) {
130
- const parsed = new DOMParser().parseFromString(html, "text/html").body;
131
- if (parsed.children.length > 1) return parsed;
132
- return parsed.firstElementChild;
133
- }
134
- function removeClassesAndDataAttributes(element, keyword) {
135
- Array.from(element.classList).forEach((className) => {
136
- if (className.includes(keyword)) {
137
- element.classList.remove(className);
134
+ class DataFilter {
135
+ constructor(rules) {
136
+ this.rules = rules;
137
+ this.registerFilters(rules.customDataSelectorFns);
138
+ this.applyCSSFilters();
139
+ }
140
+ filters = new Map();
141
+ static isFiltered(el) {
142
+ return el.className.includes("filter-");
143
+ }
144
+ applyCSSFilters(wrapper) {
145
+ this.filters.forEach((_, name) => {
146
+ const cssRule = `.filter-${name} { display: none !important; }`;
147
+ if (wrapper) {
148
+ _GM_addStyle(wrapper(cssRule));
149
+ } else {
150
+ _GM_addStyle(cssRule);
138
151
  }
139
152
  });
140
- Array.from(element.attributes).forEach((attr) => {
141
- if (attr.name.startsWith("data-") && attr.name.includes(keyword)) {
142
- element.removeAttribute(attr.name);
153
+ }
154
+ customDataSelectorFns = {};
155
+ registerFilters(customFilters) {
156
+ customFilters.forEach((o) => {
157
+ if (typeof o === "string") {
158
+ this.customDataSelectorFns[o] = DataFilter.customDataSelectorFnsDefault[o];
159
+ this.registerFilter(o);
160
+ } else {
161
+ const k = Object.keys(o)[0];
162
+ this.customDataSelectorFns[k] = o[k];
163
+ this.registerFilter(k);
143
164
  }
144
165
  });
145
166
  }
146
- function getCommonParents(elements) {
147
- const parents = Array.from(elements).map((el) => el.parentElement).filter((parent) => parent !== null);
148
- return [...new Set(parents)];
149
- }
150
- function checkHomogenity(a, b, options) {
151
- if (!a || !b) return false;
152
- if (options.id) {
153
- if (a.id !== b.id) return false;
167
+ customSelectorParser(name, selector) {
168
+ if ("handle" in selector) {
169
+ return selector;
170
+ } else {
171
+ return { handle: selector, deps: [name] };
154
172
  }
155
- if (options.className) {
156
- const ca = a.className;
157
- const cb = b.className;
158
- if (!(ca.length > cb.length ? ca.includes(cb) : cb.includes(ca))) {
159
- return false;
160
- }
161
- }
162
- return true;
163
- }
164
- function downloader(options = { append: "", after: "", button: "", cbBefore: () => {
165
- } }) {
166
- const btn = parseHtml(options.button);
167
- if (options.append) document.querySelector(options.append)?.append(btn);
168
- if (options.after) document.querySelector(options.after)?.after(btn);
169
- btn.addEventListener("click", (e) => {
170
- e.preventDefault();
171
- if (options.cbBefore) options.cbBefore();
172
- waitForElementToAppear(document.body, "video", (video) => {
173
- window.location.href = video.getAttribute("src");
174
- });
173
+ }
174
+ registerFilter(customSelectorName) {
175
+ const handler = this.customSelectorParser(
176
+ customSelectorName,
177
+ this.customDataSelectorFns[customSelectorName]
178
+ );
179
+ const tag = `filter-${customSelectorName}`;
180
+ [customSelectorName, ...handler.deps || []]?.forEach((name) => {
181
+ Object.assign(this.filterMapping, { [name]: customSelectorName });
175
182
  });
183
+ const fn = () => {
184
+ const preDefined = handler.$preDefine?.(this.rules.store.state);
185
+ return (v) => {
186
+ const condition = handler.handle(v, this.rules.store.state, preDefined);
187
+ return {
188
+ condition,
189
+ tag
190
+ };
191
+ };
192
+ };
193
+ this.filters.set(customSelectorName, fn);
176
194
  }
177
-
178
- const MOBILE_UA = {
179
- "User-Agent": [
180
- "Mozilla/5.0 (Linux; Android 10; K)",
181
- "AppleWebKit/537.36 (KHTML, like Gecko)",
182
- "Chrome/114.0.0.0 Mobile Safari/537.36"
183
- ].join(" ")
184
- };
185
- async function fetchWith(input, options) {
186
- const requestInit = options.init || {};
187
- if (options.mobile) {
188
- Object.assign(requestInit, { headers: new Headers(MOBILE_UA) });
189
- }
190
- const r = await fetch(input, requestInit).then((r2) => r2);
191
- return parseHtml(await r.text());
195
+ filterMapping = {};
196
+ selectFilters(filters) {
197
+ const selectedFilters = Object.keys(filters).filter((k) => k in this.filterMapping).map((k) => this.filterMapping[k]).map((k) => this.filters.get(k));
198
+ return selectedFilters;
192
199
  }
193
- const fetchHtml = (input) => fetchWith(input, { });
194
-
195
- class LazyImgLoader {
196
- lazyImgObserver;
197
- attributeName = "data-lazy-load";
198
- constructor(shouldDelazify) {
199
- this.lazyImgObserver = new Observer((target) => {
200
- if (shouldDelazify(target)) {
201
- this.delazify(target);
200
+ static customDataSelectorFnsDefault = {
201
+ filterDuration: {
202
+ handle(el, state, notInRange) {
203
+ return state.filterDuration && notInRange(el.duration);
204
+ },
205
+ $preDefine: (state) => {
206
+ const from = state.filterDurationFrom;
207
+ const to = state.filterDurationTo;
208
+ function notInRange(d) {
209
+ return d < from || d > to;
202
210
  }
203
- });
204
- }
205
- lazify(_target, img, imgSrc) {
206
- if (!img || !imgSrc) return;
207
- img.setAttribute(this.attributeName, imgSrc);
208
- img.src = "";
209
- this.lazyImgObserver.observe(img);
211
+ return notInRange;
212
+ },
213
+ deps: ["filterDurationFrom", "filterDurationTo"]
214
+ },
215
+ filterExclude: {
216
+ handle(el, state, searchFilter) {
217
+ if (!state.filterExclude) return false;
218
+ return !searchFilter.hasNone(el.title);
219
+ },
220
+ $preDefine: (state) => new RegexFilter(state.filterExcludeWords),
221
+ deps: ["filterExcludeWords"]
222
+ },
223
+ filterInclude: {
224
+ handle(el, state, searchFilter) {
225
+ if (!state.filterInclude) return false;
226
+ return !searchFilter.hasEvery(el.title);
227
+ },
228
+ $preDefine: (state) => new RegexFilter(state.filterIncludeWords),
229
+ deps: ["filterIncludeWords"]
210
230
  }
211
- delazify = (target) => {
212
- this.lazyImgObserver.observer.unobserve(target);
213
- target.src = target.getAttribute(this.attributeName);
214
- target.removeAttribute(this.attributeName);
215
- };
216
- }
231
+ };
232
+ }
217
233
 
218
- class Observer {
219
- constructor(callback) {
220
- this.callback = callback;
221
- this.observer = new IntersectionObserver(this.handleIntersection.bind(this));
222
- }
223
- observer;
224
- timeout;
225
- observe(target) {
226
- this.observer.observe(target);
234
+ function waitForElementToAppear(parent, selector, callback) {
235
+ const observer = new MutationObserver((_mutations) => {
236
+ const el = parent.querySelector(selector);
237
+ if (el) {
238
+ observer.disconnect();
239
+ callback(el);
227
240
  }
228
- throttle(target, throttleTime) {
229
- this.observer.unobserve(target);
230
- this.timeout = window.setTimeout(() => this.observer.observe(target), throttleTime);
241
+ });
242
+ observer.observe(document.body, { childList: true, subtree: true });
243
+ return observer;
244
+ }
245
+ function waitForElementToDisappear(observable, callback) {
246
+ const observer = new MutationObserver((_mutations) => {
247
+ if (!observable.isConnected) {
248
+ observer.disconnect();
249
+ callback();
231
250
  }
232
- handleIntersection(entries) {
233
- for (const entry of entries) {
234
- if (entry.isIntersecting) {
235
- this.callback(entry.target);
236
- }
237
- }
251
+ });
252
+ observer.observe(document.body, { childList: true, subtree: true });
253
+ return observer;
254
+ }
255
+
256
+ function querySelectorLast(root = document, selector) {
257
+ const nodes = root.querySelectorAll(selector);
258
+ return nodes.length > 0 ? nodes[nodes.length - 1] : void 0;
259
+ }
260
+ function querySelectorLastNumber(selector, e = document) {
261
+ const text = querySelectorText(e, selector);
262
+ return Number(text.match(/\d+/g)?.pop() || 0);
263
+ }
264
+ function querySelectorText(e, selector) {
265
+ if (typeof selector !== "string") return "";
266
+ const text = e.querySelector(selector)?.innerText || "";
267
+ return sanitizeStr(text);
268
+ }
269
+ function parseHtml(html) {
270
+ const parsed = new DOMParser().parseFromString(html, "text/html").body;
271
+ if (parsed.children.length > 1) return parsed;
272
+ return parsed.firstElementChild;
273
+ }
274
+ function removeClassesAndDataAttributes(element, keyword) {
275
+ Array.from(element.classList).forEach((className) => {
276
+ if (className.includes(keyword)) {
277
+ element.classList.remove(className);
238
278
  }
239
- dispose() {
240
- if (this.timeout) clearTimeout(this.timeout);
241
- this.observer.disconnect();
279
+ });
280
+ Array.from(element.attributes).forEach((attr) => {
281
+ if (attr.name.startsWith("data-") && attr.name.includes(keyword)) {
282
+ element.removeAttribute(attr.name);
242
283
  }
243
- static observeWhile(target, callback, throttleTime) {
244
- const observer_ = new Observer(async (target2) => {
245
- const condition = await callback();
246
- if (condition) observer_.throttle(target2, throttleTime);
247
- });
248
- observer_.observe(target);
249
- return observer_;
284
+ });
285
+ }
286
+ function getCommonParents(elements) {
287
+ const parents = Array.from(elements).map((el) => el.parentElement).filter((parent) => parent !== null);
288
+ return [...new Set(parents)];
289
+ }
290
+ function checkHomogenity(a, b, options) {
291
+ if (!a || !b) return false;
292
+ if (options.id) {
293
+ if (a.id !== b.id) return false;
294
+ }
295
+ if (options.className) {
296
+ const ca = a.className;
297
+ const cb = b.className;
298
+ if (!(ca.length > cb.length ? ca.includes(cb) : cb.includes(ca))) {
299
+ return false;
250
300
  }
251
301
  }
302
+ return true;
303
+ }
304
+ function downloader(options = { append: "", after: "", button: "", cbBefore: () => {
305
+ } }) {
306
+ const btn = parseHtml(options.button);
307
+ if (options.append) document.querySelector(options.append)?.append(btn);
308
+ if (options.after) document.querySelector(options.after)?.after(btn);
309
+ btn.addEventListener("click", (e) => {
310
+ e.preventDefault();
311
+ if (options.cbBefore) options.cbBefore();
312
+ waitForElementToAppear(document.body, "video", (video) => {
313
+ window.location.href = video.getAttribute("src");
314
+ });
315
+ });
316
+ }
252
317
 
253
- class InfiniteScroller {
254
- enabled = true;
255
- paginationOffset = 1;
256
- parseData;
257
- rules;
258
- observer;
259
- paginationGenerator;
260
- constructor(options) {
261
- this.rules = options.rules;
262
- this.paginationOffset = this.rules.paginationStrategy.getPaginationOffset();
263
- Object.assign(this, options);
264
- if (this.rules.getPaginationData) {
265
- this.getPaginationData = this.rules.getPaginationData;
318
+ class LazyImgLoader {
319
+ lazyImgObserver;
320
+ attributeName = "data-lazy-load";
321
+ constructor(shouldDelazify) {
322
+ this.lazyImgObserver = new Observer((target) => {
323
+ if (shouldDelazify(target)) {
324
+ this.delazify(target);
325
+ }
326
+ });
327
+ }
328
+ lazify(_target, img, imgSrc) {
329
+ if (!img || !imgSrc) return;
330
+ img.setAttribute(this.attributeName, imgSrc);
331
+ img.src = "";
332
+ this.lazyImgObserver.observe(img);
333
+ }
334
+ delazify = (target) => {
335
+ this.lazyImgObserver.observer.unobserve(target);
336
+ target.src = target.getAttribute(this.attributeName);
337
+ target.removeAttribute(this.attributeName);
338
+ };
339
+ }
340
+
341
+ class Observer {
342
+ constructor(callback) {
343
+ this.callback = callback;
344
+ this.observer = new IntersectionObserver(this.handleIntersection.bind(this));
345
+ }
346
+ observer;
347
+ timeout;
348
+ observe(target) {
349
+ this.observer.observe(target);
350
+ }
351
+ throttle(target, throttleTime) {
352
+ this.observer.unobserve(target);
353
+ this.timeout = window.setTimeout(() => this.observer.observe(target), throttleTime);
354
+ }
355
+ handleIntersection(entries) {
356
+ for (const entry of entries) {
357
+ if (entry.isIntersecting) {
358
+ this.callback(entry.target);
266
359
  }
267
- this.paginationGenerator = this.rules.customGenerator || InfiniteScroller.generatorForPaginationStrategy(this.rules.paginationStrategy);
268
- this.setObserver(this.rules.observable);
269
- this.setAutoScroll();
270
- }
271
- dispose() {
272
- if (this.observer) this.observer.dispose();
273
- }
274
- setObserver(observable) {
275
- if (this.observer) this.observer.dispose();
276
- this.observer = Observer.observeWhile(
277
- observable,
278
- this.generatorConsumer,
279
- this.rules.store.state.delay
280
- );
281
- return this;
282
- }
283
- onScrollCBs = [];
284
- onScroll(callback, initCall = false) {
285
- if (initCall) callback(this);
286
- this.onScrollCBs.push(callback);
287
- return this;
288
- }
289
- _onScroll() {
290
- this.onScrollCBs.forEach((cb) => {
291
- cb(this);
292
- });
293
360
  }
294
- setAutoScroll() {
295
- const autoScrollWrapper = async () => {
296
- if (this.rules.store.state.autoScroll) {
297
- await wait(this.rules.store.state.delay);
298
- await this.generatorConsumer();
299
- await autoScrollWrapper();
361
+ }
362
+ dispose() {
363
+ if (this.timeout) clearTimeout(this.timeout);
364
+ this.observer.disconnect();
365
+ }
366
+ static observeWhile(target, callback, throttleTime) {
367
+ const observer_ = new Observer(async (target2) => {
368
+ const condition = await callback();
369
+ if (condition) observer_.throttle(target2, throttleTime);
370
+ });
371
+ observer_.observe(target);
372
+ return observer_;
373
+ }
374
+ }
375
+
376
+ class DataManager {
377
+ constructor(rules) {
378
+ this.rules = rules;
379
+ this.dataFilter = new DataFilter(this.rules);
380
+ }
381
+ data = new Map();
382
+ lazyImgLoader = new LazyImgLoader(
383
+ (target) => !DataFilter.isFiltered(target)
384
+ );
385
+ dataFilter;
386
+ applyFilters = async (filters = {}, offset = 0) => {
387
+ const filtersToApply = this.dataFilter.selectFilters(filters);
388
+ if (filtersToApply.length === 0) return;
389
+ const iterator = this.data.values().drop(offset);
390
+ let finished = false;
391
+ await new Promise((resolve) => {
392
+ function runBatch(deadline) {
393
+ const updates = [];
394
+ while (deadline.timeRemaining() > 0) {
395
+ const { value, done } = iterator.next();
396
+ finished = !!done;
397
+ if (done) break;
398
+ for (const f of filtersToApply) {
399
+ const { tag, condition } = f()(value);
400
+ updates.push({ e: value.element, tag, condition });
401
+ }
300
402
  }
301
- };
302
- autoScrollWrapper();
303
- this.rules.store.stateSubject.subscribe((type) => {
304
- if (type?.autoScroll) {
305
- autoScrollWrapper();
403
+ if (updates.length > 0) {
404
+ requestAnimationFrame(() => {
405
+ updates.forEach((u) => {
406
+ u.e.classList.toggle(u.tag, u.condition);
407
+ });
408
+ });
409
+ }
410
+ if (!finished) {
411
+ requestIdleCallback(runBatch);
412
+ } else {
413
+ resolve(true);
306
414
  }
307
- });
308
- }
309
- generatorConsumer = async () => {
310
- if (!this.enabled) return false;
311
- const {
312
- value: { url, offset },
313
- done
314
- } = await this.paginationGenerator.next();
315
- if (!done && url) {
316
- await this.doScroll(url, offset);
317
415
  }
318
- return !done;
319
- };
320
- async getPaginationData(url) {
321
- return await fetchHtml(url);
322
- }
323
- async doScroll(url, offset) {
324
- const nextPageHtml = await this.getPaginationData(url);
325
- const prevScrollPos = document.documentElement.scrollTop;
326
- this.paginationOffset = Math.max(this.paginationOffset, offset);
327
- this.parseData?.(nextPageHtml);
328
- this._onScroll();
329
- window.scrollTo(0, prevScrollPos);
330
- if (this.rules.store.state.writeHistory) {
331
- history.replaceState({}, "", url);
416
+ requestIdleCallback(runBatch);
417
+ });
418
+ };
419
+ filterAll = async (offset) => {
420
+ const keys = Array.from(this.dataFilter.filters.keys());
421
+ const filters = Object.fromEntries(
422
+ keys.map((k) => [k, this.rules.store.state[k]])
423
+ );
424
+ await this.applyFilters(filters, offset);
425
+ };
426
+ parseDataParentHomogenity;
427
+ parseData = (html, container, removeDuplicates = false, shouldLazify = true) => {
428
+ const thumbs = this.rules.getThumbs(html);
429
+ const dataOffset = this.data.size;
430
+ const fragment = document.createDocumentFragment();
431
+ const parent = container || this.rules.container;
432
+ const homogenity = !!this.parseDataParentHomogenity;
433
+ for (const thumbElement of thumbs) {
434
+ const url = this.rules.getThumbUrl(thumbElement);
435
+ if (!url || this.data.has(url) || parent !== container && parent?.contains(thumbElement) || homogenity && !checkHomogenity(
436
+ parent,
437
+ thumbElement.parentElement,
438
+ this.parseDataParentHomogenity
439
+ )) {
440
+ if (removeDuplicates) thumbElement.remove();
441
+ continue;
332
442
  }
333
- }
334
- static async *generatorForPaginationStrategy(pstrategy) {
335
- const _offset = pstrategy.getPaginationOffset();
336
- const end = pstrategy.getPaginationLast();
337
- const urlGenerator = pstrategy.getPaginationUrlGenerator();
338
- for (let offset = _offset; offset <= end; offset++) {
339
- const url = await urlGenerator(offset);
340
- yield { url, offset };
443
+ const data = this.rules.getThumbData(thumbElement);
444
+ this.data.set(url, { element: thumbElement, ...data });
445
+ if (shouldLazify) {
446
+ const { img, imgSrc } = this.rules.getThumbImgData(thumbElement);
447
+ this.lazyImgLoader.lazify(thumbElement, img, imgSrc);
341
448
  }
449
+ fragment.append(thumbElement);
342
450
  }
343
- static create(rules) {
344
- const enabled = rules.store.state.infiniteScrollEnabled;
345
- rules.store.state.$paginationLast = rules.paginationStrategy.getPaginationLast();
346
- const infiniteScroller = new InfiniteScroller({
347
- enabled,
348
- parseData: rules.dataManager.parseData,
349
- rules
350
- }).onScroll(({ paginationOffset }) => {
351
- rules.store.state.$paginationOffset = paginationOffset;
352
- }, true);
353
- rules.store.stateSubject.subscribe(() => {
354
- infiniteScroller.enabled = rules.store.state.infiniteScrollEnabled;
451
+ this.filterAll(dataOffset).then(() => {
452
+ requestAnimationFrame(() => {
453
+ parent.appendChild(fragment);
355
454
  });
356
- return infiniteScroller;
357
- }
455
+ });
456
+ };
457
+ sortBy(key, direction = true) {
458
+ if (this.data.size < 2) return;
459
+ let sorted = this.data.values().toArray().sort((a, b) => {
460
+ return a[key] - b[key];
461
+ });
462
+ if (!direction) sorted = sorted.reverse();
463
+ const container = sorted[0].element.parentElement;
464
+ container.style.visibility = "hidden";
465
+ sorted.forEach((s) => {
466
+ container.append(s.element);
467
+ });
468
+ container.style.visibility = "visible";
358
469
  }
470
+ }
359
471
 
360
- var Pe=Object.defineProperty;var a=(e,t)=>Pe(e,"name",{value:t,configurable:true});var P=class{type=3;name="";prefix="";value="";suffix="";modifier=3;constructor(t,r,n,c,l,f){this.type=t,this.name=r,this.prefix=n,this.value=c,this.suffix=l,this.modifier=f;}hasCustomName(){return this.name!==""&&typeof this.name!="number"}};a(P,"Part");var Re=/[$_\p{ID_Start}]/u,Ee=/[$_\u200C\u200D\p{ID_Continue}]/u,v=".*";function Oe(e,t){return (t?/^[\x00-\xFF]*$/:/^[\x00-\x7F]*$/).test(e)}a(Oe,"isASCII");function D(e,t=false){let r=[],n=0;for(;n<e.length;){let c=e[n],l=a(function(f){if(!t)throw new TypeError(f);r.push({type:"INVALID_CHAR",index:n,value:e[n++]});},"ErrorOrInvalid");if(c==="*"){r.push({type:"ASTERISK",index:n,value:e[n++]});continue}if(c==="+"||c==="?"){r.push({type:"OTHER_MODIFIER",index:n,value:e[n++]});continue}if(c==="\\"){r.push({type:"ESCAPED_CHAR",index:n++,value:e[n++]});continue}if(c==="{"){r.push({type:"OPEN",index:n,value:e[n++]});continue}if(c==="}"){r.push({type:"CLOSE",index:n,value:e[n++]});continue}if(c===":"){let f="",s=n+1;for(;s<e.length;){let i=e.substr(s,1);if(s===n+1&&Re.test(i)||s!==n+1&&Ee.test(i)){f+=e[s++];continue}break}if(!f){l(`Missing parameter name at ${n}`);continue}r.push({type:"NAME",index:n,value:f}),n=s;continue}if(c==="("){let f=1,s="",i=n+1,o=false;if(e[i]==="?"){l(`Pattern cannot start with "?" at ${i}`);continue}for(;i<e.length;){if(!Oe(e[i],false)){l(`Invalid character '${e[i]}' at ${i}.`),o=true;break}if(e[i]==="\\"){s+=e[i++]+e[i++];continue}if(e[i]===")"){if(f--,f===0){i++;break}}else if(e[i]==="("&&(f++,e[i+1]!=="?")){l(`Capturing groups are not allowed at ${i}`),o=true;break}s+=e[i++];}if(o)continue;if(f){l(`Unbalanced pattern at ${n}`);continue}if(!s){l(`Missing pattern at ${n}`);continue}r.push({type:"REGEX",index:n,value:s}),n=i;continue}r.push({type:"CHAR",index:n,value:e[n++]});}return r.push({type:"END",index:n,value:""}),r}a(D,"lexer");function F(e,t={}){let r=D(e);t.delimiter??="/#?",t.prefixes??="./";let n=`[^${x(t.delimiter)}]+?`,c=[],l=0,f=0,i=new Set,o=a(u=>{if(f<r.length&&r[f].type===u)return r[f++].value},"tryConsume"),h=a(()=>o("OTHER_MODIFIER")??o("ASTERISK"),"tryConsumeModifier"),p=a(u=>{let d=o(u);if(d!==void 0)return d;let{type:g,index:y}=r[f];throw new TypeError(`Unexpected ${g} at ${y}, expected ${u}`)},"mustConsume"),A=a(()=>{let u="",d;for(;d=o("CHAR")??o("ESCAPED_CHAR");)u+=d;return u},"consumeText"),xe=a(u=>u,"DefaultEncodePart"),N=t.encodePart||xe,H="",$=a(u=>{H+=u;},"appendToPendingFixedValue"),M=a(()=>{H.length&&(c.push(new P(3,"","",N(H),"",3)),H="");},"maybeAddPartFromPendingFixedValue"),X=a((u,d,g,y,Z)=>{let m=3;switch(Z){case "?":m=1;break;case "*":m=0;break;case "+":m=2;break}if(!d&&!g&&m===3){$(u);return}if(M(),!d&&!g){if(!u)return;c.push(new P(3,"","",N(u),"",m));return}let S;g?g==="*"?S=v:S=g:S=n;let k=2;S===n?(k=1,S=""):S===v&&(k=0,S="");let E;if(d?E=d:g&&(E=l++),i.has(E))throw new TypeError(`Duplicate name '${E}'.`);i.add(E),c.push(new P(k,E,N(u),S,N(y),m));},"addPart");for(;f<r.length;){let u=o("CHAR"),d=o("NAME"),g=o("REGEX");if(!d&&!g&&(g=o("ASTERISK")),d||g){let m=u??"";t.prefixes.indexOf(m)===-1&&($(m),m=""),M();let S=h();X(m,d,g,"",S);continue}let y=u??o("ESCAPED_CHAR");if(y){$(y);continue}if(o("OPEN")){let m=A(),S=o("NAME"),k=o("REGEX");!S&&!k&&(k=o("ASTERISK"));let E=A();p("CLOSE");let be=h();X(m,S,k,E,be);continue}M(),p("END");}return c}a(F,"parse");function x(e){return e.replace(/([.+*?^${}()[\]|/\\])/g,"\\$1")}a(x,"escapeString");function B(e){return e&&e.ignoreCase?"ui":"u"}a(B,"flags");function q(e,t,r){return W(F(e,r),t,r)}a(q,"stringToRegexp");function T(e){switch(e){case 0:return "*";case 1:return "?";case 2:return "+";case 3:return ""}}a(T,"modifierToString");function W(e,t,r={}){r.delimiter??="/#?",r.prefixes??="./",r.sensitive??=false,r.strict??=false,r.end??=true,r.start??=true,r.endsWith="";let n=r.start?"^":"";for(let s of e){if(s.type===3){s.modifier===3?n+=x(s.value):n+=`(?:${x(s.value)})${T(s.modifier)}`;continue}t&&t.push(s.name);let i=`[^${x(r.delimiter)}]+?`,o=s.value;if(s.type===1?o=i:s.type===0&&(o=v),!s.prefix.length&&!s.suffix.length){s.modifier===3||s.modifier===1?n+=`(${o})${T(s.modifier)}`:n+=`((?:${o})${T(s.modifier)})`;continue}if(s.modifier===3||s.modifier===1){n+=`(?:${x(s.prefix)}(${o})${x(s.suffix)})`,n+=T(s.modifier);continue}n+=`(?:${x(s.prefix)}`,n+=`((?:${o})(?:`,n+=x(s.suffix),n+=x(s.prefix),n+=`(?:${o}))*)${x(s.suffix)})`,s.modifier===0&&(n+="?");}let c=`[${x(r.endsWith)}]|$`,l=`[${x(r.delimiter)}]`;if(r.end)return r.strict||(n+=`${l}?`),r.endsWith.length?n+=`(?=${c})`:n+="$",new RegExp(n,B(r));r.strict||(n+=`(?:${l}(?=${c}))?`);let f=false;if(e.length){let s=e[e.length-1];s.type===3&&s.modifier===3&&(f=r.delimiter.indexOf(s)>-1);}return f||(n+=`(?=${l}|${c})`),new RegExp(n,B(r))}a(W,"partsToRegexp");var b={delimiter:"",prefixes:"",sensitive:true,strict:true},J={delimiter:".",prefixes:"",sensitive:true,strict:true},Q={delimiter:"/",prefixes:"/",sensitive:true,strict:true};function ee(e,t){return e.length?e[0]==="/"?true:!t||e.length<2?false:(e[0]=="\\"||e[0]=="{")&&e[1]=="/":false}a(ee,"isAbsolutePathname");function te(e,t){return e.startsWith(t)?e.substring(t.length,e.length):e}a(te,"maybeStripPrefix");function ke(e,t){return e.endsWith(t)?e.substr(0,e.length-t.length):e}a(ke,"maybeStripSuffix");function _(e){return !e||e.length<2?false:e[0]==="["||(e[0]==="\\"||e[0]==="{")&&e[1]==="["}a(_,"treatAsIPv6Hostname");var re=["ftp","file","http","https","ws","wss"];function U(e){if(!e)return true;for(let t of re)if(e.test(t))return true;return false}a(U,"isSpecialScheme");function ne(e,t){if(e=te(e,"#"),t||e==="")return e;let r=new URL("https://example.com");return r.hash=e,r.hash?r.hash.substring(1,r.hash.length):""}a(ne,"canonicalizeHash");function se(e,t){if(e=te(e,"?"),t||e==="")return e;let r=new URL("https://example.com");return r.search=e,r.search?r.search.substring(1,r.search.length):""}a(se,"canonicalizeSearch");function ie(e,t){return t||e===""?e:_(e)?K(e):j(e)}a(ie,"canonicalizeHostname");function ae(e,t){if(t||e==="")return e;let r=new URL("https://example.com");return r.password=e,r.password}a(ae,"canonicalizePassword");function oe(e,t){if(t||e==="")return e;let r=new URL("https://example.com");return r.username=e,r.username}a(oe,"canonicalizeUsername");function ce(e,t,r){if(r||e==="")return e;if(t&&!re.includes(t))return new URL(`${t}:${e}`).pathname;let n=e[0]=="/";return e=new URL(n?e:"/-"+e,"https://example.com").pathname,n||(e=e.substring(2,e.length)),e}a(ce,"canonicalizePathname");function le(e,t,r){return z(t)===e&&(e=""),r||e===""?e:G(e)}a(le,"canonicalizePort");function fe(e,t){return e=ke(e,":"),t||e===""?e:w(e)}a(fe,"canonicalizeProtocol");function z(e){switch(e){case "ws":case "http":return "80";case "wws":case "https":return "443";case "ftp":return "21";default:return ""}}a(z,"defaultPortForProtocol");function w(e){if(e==="")return e;if(/^[-+.A-Za-z0-9]*$/.test(e))return e.toLowerCase();throw new TypeError(`Invalid protocol '${e}'.`)}a(w,"protocolEncodeCallback");function he(e){if(e==="")return e;let t=new URL("https://example.com");return t.username=e,t.username}a(he,"usernameEncodeCallback");function ue(e){if(e==="")return e;let t=new URL("https://example.com");return t.password=e,t.password}a(ue,"passwordEncodeCallback");function j(e){if(e==="")return e;if(/[\t\n\r #%/:<>?@[\]^\\|]/g.test(e))throw new TypeError(`Invalid hostname '${e}'`);let t=new URL("https://example.com");return t.hostname=e,t.hostname}a(j,"hostnameEncodeCallback");function K(e){if(e==="")return e;if(/[^0-9a-fA-F[\]:]/g.test(e))throw new TypeError(`Invalid IPv6 hostname '${e}'`);return e.toLowerCase()}a(K,"ipv6HostnameEncodeCallback");function G(e){if(e===""||/^[0-9]*$/.test(e)&&parseInt(e)<=65535)return e;throw new TypeError(`Invalid port '${e}'.`)}a(G,"portEncodeCallback");function de(e){if(e==="")return e;let t=new URL("https://example.com");return t.pathname=e[0]!=="/"?"/-"+e:e,e[0]!=="/"?t.pathname.substring(2,t.pathname.length):t.pathname}a(de,"standardURLPathnameEncodeCallback");function pe(e){return e===""?e:new URL(`data:${e}`).pathname}a(pe,"pathURLPathnameEncodeCallback");function ge(e){if(e==="")return e;let t=new URL("https://example.com");return t.search=e,t.search.substring(1,t.search.length)}a(ge,"searchEncodeCallback");function me(e){if(e==="")return e;let t=new URL("https://example.com");return t.hash=e,t.hash.substring(1,t.hash.length)}a(me,"hashEncodeCallback");var C=class{#i;#n=[];#t={};#e=0;#s=1;#l=0;#o=0;#d=0;#p=0;#g=false;constructor(t){this.#i=t;}get result(){return this.#t}parse(){for(this.#n=D(this.#i,true);this.#e<this.#n.length;this.#e+=this.#s){if(this.#s=1,this.#n[this.#e].type==="END"){if(this.#o===0){this.#b(),this.#f()?this.#r(9,1):this.#h()?this.#r(8,1):this.#r(7,0);continue}else if(this.#o===2){this.#u(5);continue}this.#r(10,0);break}if(this.#d>0)if(this.#A())this.#d-=1;else continue;if(this.#T()){this.#d+=1;continue}switch(this.#o){case 0:this.#P()&&this.#u(1);break;case 1:if(this.#P()){this.#C();let t=7,r=1;this.#E()?(t=2,r=3):this.#g&&(t=2),this.#r(t,r);}break;case 2:this.#S()?this.#u(3):(this.#x()||this.#h()||this.#f())&&this.#u(5);break;case 3:this.#O()?this.#r(4,1):this.#S()&&this.#r(5,1);break;case 4:this.#S()&&this.#r(5,1);break;case 5:this.#y()?this.#p+=1:this.#w()&&(this.#p-=1),this.#k()&&!this.#p?this.#r(6,1):this.#x()?this.#r(7,0):this.#h()?this.#r(8,1):this.#f()&&this.#r(9,1);break;case 6:this.#x()?this.#r(7,0):this.#h()?this.#r(8,1):this.#f()&&this.#r(9,1);break;case 7:this.#h()?this.#r(8,1):this.#f()&&this.#r(9,1);break;case 8:this.#f()&&this.#r(9,1);break;}}this.#t.hostname!==void 0&&this.#t.port===void 0&&(this.#t.port="");}#r(t,r){switch(this.#o){case 0:break;case 1:this.#t.protocol=this.#c();break;case 2:break;case 3:this.#t.username=this.#c();break;case 4:this.#t.password=this.#c();break;case 5:this.#t.hostname=this.#c();break;case 6:this.#t.port=this.#c();break;case 7:this.#t.pathname=this.#c();break;case 8:this.#t.search=this.#c();break;case 9:this.#t.hash=this.#c();break;}this.#o!==0&&t!==10&&([1,2,3,4].includes(this.#o)&&[6,7,8,9].includes(t)&&(this.#t.hostname??=""),[1,2,3,4,5,6].includes(this.#o)&&[8,9].includes(t)&&(this.#t.pathname??=this.#g?"/":""),[1,2,3,4,5,6,7].includes(this.#o)&&t===9&&(this.#t.search??="")),this.#R(t,r);}#R(t,r){this.#o=t,this.#l=this.#e+r,this.#e+=r,this.#s=0;}#b(){this.#e=this.#l,this.#s=0;}#u(t){this.#b(),this.#o=t;}#m(t){return t<0&&(t=this.#n.length-t),t<this.#n.length?this.#n[t]:this.#n[this.#n.length-1]}#a(t,r){let n=this.#m(t);return n.value===r&&(n.type==="CHAR"||n.type==="ESCAPED_CHAR"||n.type==="INVALID_CHAR")}#P(){return this.#a(this.#e,":")}#E(){return this.#a(this.#e+1,"/")&&this.#a(this.#e+2,"/")}#S(){return this.#a(this.#e,"@")}#O(){return this.#a(this.#e,":")}#k(){return this.#a(this.#e,":")}#x(){return this.#a(this.#e,"/")}#h(){if(this.#a(this.#e,"?"))return true;if(this.#n[this.#e].value!=="?")return false;let t=this.#m(this.#e-1);return t.type!=="NAME"&&t.type!=="REGEX"&&t.type!=="CLOSE"&&t.type!=="ASTERISK"}#f(){return this.#a(this.#e,"#")}#T(){return this.#n[this.#e].type=="OPEN"}#A(){return this.#n[this.#e].type=="CLOSE"}#y(){return this.#a(this.#e,"[")}#w(){return this.#a(this.#e,"]")}#c(){let t=this.#n[this.#e],r=this.#m(this.#l).index;return this.#i.substring(r,t.index)}#C(){let t={};Object.assign(t,b),t.encodePart=w;let r=q(this.#c(),void 0,t);this.#g=U(r);}};a(C,"Parser");var V=["protocol","username","password","hostname","port","pathname","search","hash"],O="*";function Se(e,t){if(typeof e!="string")throw new TypeError("parameter 1 is not of type 'string'.");let r=new URL(e,t);return {protocol:r.protocol.substring(0,r.protocol.length-1),username:r.username,password:r.password,hostname:r.hostname,port:r.port,pathname:r.pathname,search:r.search!==""?r.search.substring(1,r.search.length):void 0,hash:r.hash!==""?r.hash.substring(1,r.hash.length):void 0}}a(Se,"extractValues");function R(e,t){return t?I(e):e}a(R,"processBaseURLString");function L(e,t,r){let n;if(typeof t.baseURL=="string")try{n=new URL(t.baseURL),t.protocol===void 0&&(e.protocol=R(n.protocol.substring(0,n.protocol.length-1),r)),!r&&t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.username===void 0&&(e.username=R(n.username,r)),!r&&t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.username===void 0&&t.password===void 0&&(e.password=R(n.password,r)),t.protocol===void 0&&t.hostname===void 0&&(e.hostname=R(n.hostname,r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&(e.port=R(n.port,r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.pathname===void 0&&(e.pathname=R(n.pathname,r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.pathname===void 0&&t.search===void 0&&(e.search=R(n.search.substring(1,n.search.length),r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.pathname===void 0&&t.search===void 0&&t.hash===void 0&&(e.hash=R(n.hash.substring(1,n.hash.length),r));}catch{throw new TypeError(`invalid baseURL '${t.baseURL}'.`)}if(typeof t.protocol=="string"&&(e.protocol=fe(t.protocol,r)),typeof t.username=="string"&&(e.username=oe(t.username,r)),typeof t.password=="string"&&(e.password=ae(t.password,r)),typeof t.hostname=="string"&&(e.hostname=ie(t.hostname,r)),typeof t.port=="string"&&(e.port=le(t.port,e.protocol,r)),typeof t.pathname=="string"){if(e.pathname=t.pathname,n&&!ee(e.pathname,r)){let c=n.pathname.lastIndexOf("/");c>=0&&(e.pathname=R(n.pathname.substring(0,c+1),r)+e.pathname);}e.pathname=ce(e.pathname,e.protocol,r);}return typeof t.search=="string"&&(e.search=se(t.search,r)),typeof t.hash=="string"&&(e.hash=ne(t.hash,r)),e}a(L,"applyInit");function I(e){return e.replace(/([+*?:{}()\\])/g,"\\$1")}a(I,"escapePatternString");function Te(e){return e.replace(/([.+*?^${}()[\]|/\\])/g,"\\$1")}a(Te,"escapeRegexpString");function Ae(e,t){t.delimiter??="/#?",t.prefixes??="./",t.sensitive??=false,t.strict??=false,t.end??=true,t.start??=true,t.endsWith="";let r=".*",n=`[^${Te(t.delimiter)}]+?`,c=/[$_\u200C\u200D\p{ID_Continue}]/u,l="";for(let f=0;f<e.length;++f){let s=e[f];if(s.type===3){if(s.modifier===3){l+=I(s.value);continue}l+=`{${I(s.value)}}${T(s.modifier)}`;continue}let i=s.hasCustomName(),o=!!s.suffix.length||!!s.prefix.length&&(s.prefix.length!==1||!t.prefixes.includes(s.prefix)),h=f>0?e[f-1]:null,p=f<e.length-1?e[f+1]:null;if(!o&&i&&s.type===1&&s.modifier===3&&p&&!p.prefix.length&&!p.suffix.length)if(p.type===3){let A=p.value.length>0?p.value[0]:"";o=c.test(A);}else o=!p.hasCustomName();if(!o&&!s.prefix.length&&h&&h.type===3){let A=h.value[h.value.length-1];o=t.prefixes.includes(A);}o&&(l+="{"),l+=I(s.prefix),i&&(l+=`:${s.name}`),s.type===2?l+=`(${s.value})`:s.type===1?i||(l+=`(${n})`):s.type===0&&(!i&&(!h||h.type===3||h.modifier!==3||o||s.prefix!=="")?l+="*":l+=`(${r})`),s.type===1&&i&&s.suffix.length&&c.test(s.suffix[0])&&(l+="\\"),l+=I(s.suffix),o&&(l+="}"),s.modifier!==3&&(l+=T(s.modifier));}return l}a(Ae,"partsToPattern");var Y=class{#i;#n={};#t={};#e={};#s={};#l=false;constructor(t={},r,n){try{let c;if(typeof r=="string"?c=r:n=r,typeof t=="string"){let i=new C(t);if(i.parse(),t=i.result,c===void 0&&typeof t.protocol!="string")throw new TypeError("A base URL must be provided for a relative constructor string.");t.baseURL=c;}else {if(!t||typeof t!="object")throw new TypeError("parameter 1 is not of type 'string' and cannot convert to dictionary.");if(c)throw new TypeError("parameter 1 is not of type 'string'.")}typeof n>"u"&&(n={ignoreCase:!1});let l={ignoreCase:n.ignoreCase===!0},f={pathname:O,protocol:O,username:O,password:O,hostname:O,port:O,search:O,hash:O};this.#i=L(f,t,!0),z(this.#i.protocol)===this.#i.port&&(this.#i.port="");let s;for(s of V){if(!(s in this.#i))continue;let i={},o=this.#i[s];switch(this.#t[s]=[],s){case "protocol":Object.assign(i,b),i.encodePart=w;break;case "username":Object.assign(i,b),i.encodePart=he;break;case "password":Object.assign(i,b),i.encodePart=ue;break;case "hostname":Object.assign(i,J),_(o)?i.encodePart=K:i.encodePart=j;break;case "port":Object.assign(i,b),i.encodePart=G;break;case "pathname":U(this.#n.protocol)?(Object.assign(i,Q,l),i.encodePart=de):(Object.assign(i,b,l),i.encodePart=pe);break;case "search":Object.assign(i,b,l),i.encodePart=ge;break;case "hash":Object.assign(i,b,l),i.encodePart=me;break}try{this.#s[s]=F(o,i),this.#n[s]=W(this.#s[s],this.#t[s],i),this.#e[s]=Ae(this.#s[s],i),this.#l=this.#l||this.#s[s].some(h=>h.type===2);}catch{throw new TypeError(`invalid ${s} pattern '${this.#i[s]}'.`)}}}catch(c){throw new TypeError(`Failed to construct 'URLPattern': ${c.message}`)}}get[Symbol.toStringTag](){return "URLPattern"}test(t={},r){let n={pathname:"",protocol:"",username:"",password:"",hostname:"",port:"",search:"",hash:""};if(typeof t!="string"&&r)throw new TypeError("parameter 1 is not of type 'string'.");if(typeof t>"u")return false;try{typeof t=="object"?n=L(n,t,!1):n=L(n,Se(t,r),!1);}catch{return false}let c;for(c of V)if(!this.#n[c].exec(n[c]))return false;return true}exec(t={},r){let n={pathname:"",protocol:"",username:"",password:"",hostname:"",port:"",search:"",hash:""};if(typeof t!="string"&&r)throw new TypeError("parameter 1 is not of type 'string'.");if(typeof t>"u")return;try{typeof t=="object"?n=L(n,t,!1):n=L(n,Se(t,r),!1);}catch{return null}let c={};r?c.inputs=[t,r]:c.inputs=[t];let l;for(l of V){let f=this.#n[l].exec(n[l]);if(!f)return null;let s={};for(let[i,o]of this.#t[l].entries())if(typeof o=="string"||typeof o=="number"){let h=f[i+1];s[o]=h;}c[l]={input:n[l]??"",groups:s};}return c}static compareComponent(t,r,n){let c=a((i,o)=>{for(let h of ["type","modifier","prefix","value","suffix"]){if(i[h]<o[h])return -1;if(i[h]===o[h])continue;return 1}return 0},"comparePart"),l=new P(3,"","","","",3),f=new P(0,"","","","",3),s=a((i,o)=>{let h=0;for(;h<Math.min(i.length,o.length);++h){let p=c(i[h],o[h]);if(p)return p}return i.length===o.length?0:c(i[h]??l,o[h]??l)},"comparePartList");return !r.#e[t]&&!n.#e[t]?0:r.#e[t]&&!n.#e[t]?s(r.#s[t],[f]):!r.#e[t]&&n.#e[t]?s([f],n.#s[t]):s(r.#s[t],n.#s[t])}get protocol(){return this.#e.protocol}get username(){return this.#e.username}get password(){return this.#e.password}get hostname(){return this.#e.hostname}get port(){return this.#e.port}get pathname(){return this.#e.pathname}get search(){return this.#e.search}get hash(){return this.#e.hash}get hasRegExpGroups(){return this.#l}};a(Y,"URLPattern");
472
+ function wait(milliseconds) {
473
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
474
+ }
361
475
 
362
- function parseUrl(s) {
363
- return new URL(typeof s === "string" ? s : s.href);
364
- }
365
- function depaginatePathname(url, pathnamePaginationSelector = /\/(page\/)?\d+\/?$/) {
366
- const newUrl = new URL(url.toString());
367
- newUrl.pathname = newUrl.pathname.replace(pathnamePaginationSelector, "/");
368
- return newUrl;
369
- }
370
- function getPaginationLinks(doc = document, url = location.href, pathnamePaginationSelector = /\/(page\/)?\d+\/?$/) {
371
- const baseUrl = depaginatePathname(parseUrl(url), pathnamePaginationSelector);
372
- const pathnameStrict = doc instanceof Document;
373
- const host = doc.baseURI || baseUrl.origin;
374
- const urlPattern = new Y({
375
- pathname: pathnameStrict ? `${baseUrl.pathname}*` : "*",
376
- hostname: baseUrl.hostname
377
- });
378
- const pageLinks = [...doc.querySelectorAll("a[href]")].map((a) => a.href).filter((h) => URL.canParse(h));
379
- return pageLinks.filter((h) => {
380
- return urlPattern.test(new URL(h, host));
381
- });
476
+ const MOBILE_UA = {
477
+ "User-Agent": [
478
+ "Mozilla/5.0 (Linux; Android 10; K)",
479
+ "AppleWebKit/537.36 (KHTML, like Gecko)",
480
+ "Chrome/114.0.0.0 Mobile Safari/537.36"
481
+ ].join(" ")
482
+ };
483
+ async function fetchWith(input, options) {
484
+ const requestInit = options.init || {};
485
+ if (options.mobile) {
486
+ Object.assign(requestInit, { headers: new Headers(MOBILE_UA) });
487
+ }
488
+ const r = await fetch(input, requestInit).then((r2) => r2);
489
+ return parseHtml(await r.text());
490
+ }
491
+ const fetchHtml = (input) => fetchWith(input, { });
492
+
493
+ class InfiniteScroller {
494
+ enabled = true;
495
+ paginationOffset = 1;
496
+ parseData;
497
+ rules;
498
+ observer;
499
+ paginationGenerator;
500
+ constructor(options) {
501
+ this.rules = options.rules;
502
+ this.paginationOffset = this.rules.paginationStrategy.getPaginationOffset();
503
+ Object.assign(this, options);
504
+ if (this.rules.getPaginationData) {
505
+ this.getPaginationData = this.rules.getPaginationData;
506
+ }
507
+ this.paginationGenerator = this.rules.customGenerator || InfiniteScroller.generatorForPaginationStrategy(this.rules.paginationStrategy);
508
+ this.setObserver(this.rules.observable);
509
+ this.setAutoScroll();
382
510
  }
383
- function upgradePathname(curr, links, pathnamePaginationSelector = /\/(page\/)?\d+\/?$/) {
384
- if (pathnamePaginationSelector.test(curr.pathname) || links.length < 1) return curr;
385
- const linksDepaginated = links.map(
386
- (l) => depaginatePathname(l, pathnamePaginationSelector)
511
+ dispose() {
512
+ if (this.observer) this.observer.dispose();
513
+ }
514
+ setObserver(observable) {
515
+ if (this.observer) this.observer.dispose();
516
+ this.observer = Observer.observeWhile(
517
+ observable,
518
+ this.generatorConsumer,
519
+ this.rules.store.state.delay
387
520
  );
388
- if (linksDepaginated.some((l) => l.pathname === curr.pathname)) return curr;
389
- const last = linksDepaginated.at(-1);
390
- if (last.pathname !== curr.pathname) curr.pathname = last.pathname;
391
- return curr;
521
+ return this;
392
522
  }
393
-
394
- class PaginationStrategy {
395
- doc = document;
396
- url;
397
- paginationSelector = ".pagination";
398
- searchParamSelector = "page";
399
- static _pathnameSelector = /\/(page\/)?\d+\/?$/;
400
- pathnameSelector = /\/(\d+)\/?$/;
401
- dataparamSelector = "[data-parameters *= from]";
402
- overwritePaginationLast;
403
- offsetMin = 1;
404
- constructor(options) {
405
- if (options) {
406
- Object.entries(options).forEach(([k, v]) => {
407
- Object.assign(this, { [k]: v });
408
- });
523
+ onScrollCBs = [];
524
+ onScroll(callback, initCall = false) {
525
+ if (initCall) callback(this);
526
+ this.onScrollCBs.push(callback);
527
+ return this;
528
+ }
529
+ _onScroll() {
530
+ this.onScrollCBs.forEach((cb) => {
531
+ cb(this);
532
+ });
533
+ }
534
+ setAutoScroll() {
535
+ const autoScrollWrapper = async () => {
536
+ if (this.rules.store.state.autoScroll) {
537
+ await wait(this.rules.store.state.delay);
538
+ await this.generatorConsumer();
539
+ await autoScrollWrapper();
409
540
  }
410
- this.url = parseUrl(options?.url || this.doc.URL);
411
- }
412
- getPaginationElement() {
413
- return this.doc.querySelector(this.paginationSelector);
414
- }
415
- get hasPagination() {
416
- return !!this.getPaginationElement();
417
- }
418
- getPaginationOffset() {
419
- return this.offsetMin;
420
- }
421
- getPaginationLast() {
422
- if (this.overwritePaginationLast) return this.overwritePaginationLast(1);
423
- return 1;
541
+ };
542
+ autoScrollWrapper();
543
+ this.rules.store.stateSubject.subscribe((type) => {
544
+ if (type?.autoScroll) {
545
+ autoScrollWrapper();
546
+ }
547
+ });
548
+ }
549
+ generatorConsumer = async () => {
550
+ if (!this.enabled) return false;
551
+ const {
552
+ value: { url, offset },
553
+ done
554
+ } = await this.paginationGenerator.next();
555
+ if (!done && url) {
556
+ await this.doScroll(url, offset);
557
+ }
558
+ return !done;
559
+ };
560
+ async getPaginationData(url) {
561
+ return await fetchHtml(url);
562
+ }
563
+ async doScroll(url, offset) {
564
+ const nextPageHtml = await this.getPaginationData(url);
565
+ const prevScrollPos = document.documentElement.scrollTop;
566
+ this.paginationOffset = Math.max(this.paginationOffset, offset);
567
+ this.parseData?.(nextPageHtml);
568
+ this._onScroll();
569
+ window.scrollTo(0, prevScrollPos);
570
+ if (this.rules.store.state.writeHistory) {
571
+ history.replaceState({}, "", url);
424
572
  }
425
- getPaginationUrlGenerator() {
426
- return (_) => this.url.href;
573
+ }
574
+ static async *generatorForPaginationStrategy(pstrategy) {
575
+ const _offset = pstrategy.getPaginationOffset();
576
+ const end = pstrategy.getPaginationLast();
577
+ const urlGenerator = pstrategy.getPaginationUrlGenerator();
578
+ for (let offset = _offset; offset <= end; offset++) {
579
+ const url = await urlGenerator(offset);
580
+ yield { url, offset };
427
581
  }
428
582
  }
583
+ static create(rules) {
584
+ const enabled = rules.store.state.infiniteScrollEnabled;
585
+ rules.store.state.$paginationLast = rules.paginationStrategy.getPaginationLast();
586
+ const infiniteScroller = new InfiniteScroller({
587
+ enabled,
588
+ parseData: rules.dataManager.parseData,
589
+ rules
590
+ }).onScroll(({ paginationOffset }) => {
591
+ rules.store.state.$paginationOffset = paginationOffset;
592
+ }, true);
593
+ rules.store.stateSubject.subscribe(() => {
594
+ infiniteScroller.enabled = rules.store.state.infiniteScrollEnabled;
595
+ });
596
+ return infiniteScroller;
597
+ }
598
+ }
429
599
 
430
- function formatTimeToHHMMSS(timeStr) {
431
- const pad = (num) => num.toString().padStart(2, "0");
432
- const h = timeStr.match(/(\d+)\s*h/)?.[1] || "0";
433
- const m = timeStr.match(/(\d+)\s*mi?n/)?.[1] || "0";
434
- const s = timeStr.match(/(\d+)\s*sec/)?.[1] || "0";
435
- return `${pad(+h)}:${pad(+m)}:${pad(+s)}`;
600
+ var Pe=Object.defineProperty;var a=(e,t)=>Pe(e,"name",{value:t,configurable:true});var P=class{type=3;name="";prefix="";value="";suffix="";modifier=3;constructor(t,r,n,c,l,f){this.type=t,this.name=r,this.prefix=n,this.value=c,this.suffix=l,this.modifier=f;}hasCustomName(){return this.name!==""&&typeof this.name!="number"}};a(P,"Part");var Re=/[$_\p{ID_Start}]/u,Ee=/[$_\u200C\u200D\p{ID_Continue}]/u,v=".*";function Oe(e,t){return (t?/^[\x00-\xFF]*$/:/^[\x00-\x7F]*$/).test(e)}a(Oe,"isASCII");function D(e,t=false){let r=[],n=0;for(;n<e.length;){let c=e[n],l=a(function(f){if(!t)throw new TypeError(f);r.push({type:"INVALID_CHAR",index:n,value:e[n++]});},"ErrorOrInvalid");if(c==="*"){r.push({type:"ASTERISK",index:n,value:e[n++]});continue}if(c==="+"||c==="?"){r.push({type:"OTHER_MODIFIER",index:n,value:e[n++]});continue}if(c==="\\"){r.push({type:"ESCAPED_CHAR",index:n++,value:e[n++]});continue}if(c==="{"){r.push({type:"OPEN",index:n,value:e[n++]});continue}if(c==="}"){r.push({type:"CLOSE",index:n,value:e[n++]});continue}if(c===":"){let f="",s=n+1;for(;s<e.length;){let i=e.substr(s,1);if(s===n+1&&Re.test(i)||s!==n+1&&Ee.test(i)){f+=e[s++];continue}break}if(!f){l(`Missing parameter name at ${n}`);continue}r.push({type:"NAME",index:n,value:f}),n=s;continue}if(c==="("){let f=1,s="",i=n+1,o=false;if(e[i]==="?"){l(`Pattern cannot start with "?" at ${i}`);continue}for(;i<e.length;){if(!Oe(e[i],false)){l(`Invalid character '${e[i]}' at ${i}.`),o=true;break}if(e[i]==="\\"){s+=e[i++]+e[i++];continue}if(e[i]===")"){if(f--,f===0){i++;break}}else if(e[i]==="("&&(f++,e[i+1]!=="?")){l(`Capturing groups are not allowed at ${i}`),o=true;break}s+=e[i++];}if(o)continue;if(f){l(`Unbalanced pattern at ${n}`);continue}if(!s){l(`Missing pattern at ${n}`);continue}r.push({type:"REGEX",index:n,value:s}),n=i;continue}r.push({type:"CHAR",index:n,value:e[n++]});}return r.push({type:"END",index:n,value:""}),r}a(D,"lexer");function F(e,t={}){let r=D(e);t.delimiter??="/#?",t.prefixes??="./";let n=`[^${x(t.delimiter)}]+?`,c=[],l=0,f=0,i=new Set,o=a(u=>{if(f<r.length&&r[f].type===u)return r[f++].value},"tryConsume"),h=a(()=>o("OTHER_MODIFIER")??o("ASTERISK"),"tryConsumeModifier"),p=a(u=>{let d=o(u);if(d!==void 0)return d;let{type:g,index:y}=r[f];throw new TypeError(`Unexpected ${g} at ${y}, expected ${u}`)},"mustConsume"),A=a(()=>{let u="",d;for(;d=o("CHAR")??o("ESCAPED_CHAR");)u+=d;return u},"consumeText"),xe=a(u=>u,"DefaultEncodePart"),N=t.encodePart||xe,H="",$=a(u=>{H+=u;},"appendToPendingFixedValue"),M=a(()=>{H.length&&(c.push(new P(3,"","",N(H),"",3)),H="");},"maybeAddPartFromPendingFixedValue"),X=a((u,d,g,y,Z)=>{let m=3;switch(Z){case "?":m=1;break;case "*":m=0;break;case "+":m=2;break}if(!d&&!g&&m===3){$(u);return}if(M(),!d&&!g){if(!u)return;c.push(new P(3,"","",N(u),"",m));return}let S;g?g==="*"?S=v:S=g:S=n;let k=2;S===n?(k=1,S=""):S===v&&(k=0,S="");let E;if(d?E=d:g&&(E=l++),i.has(E))throw new TypeError(`Duplicate name '${E}'.`);i.add(E),c.push(new P(k,E,N(u),S,N(y),m));},"addPart");for(;f<r.length;){let u=o("CHAR"),d=o("NAME"),g=o("REGEX");if(!d&&!g&&(g=o("ASTERISK")),d||g){let m=u??"";t.prefixes.indexOf(m)===-1&&($(m),m=""),M();let S=h();X(m,d,g,"",S);continue}let y=u??o("ESCAPED_CHAR");if(y){$(y);continue}if(o("OPEN")){let m=A(),S=o("NAME"),k=o("REGEX");!S&&!k&&(k=o("ASTERISK"));let E=A();p("CLOSE");let be=h();X(m,S,k,E,be);continue}M(),p("END");}return c}a(F,"parse");function x(e){return e.replace(/([.+*?^${}()[\]|/\\])/g,"\\$1")}a(x,"escapeString");function B(e){return e&&e.ignoreCase?"ui":"u"}a(B,"flags");function q(e,t,r){return W(F(e,r),t,r)}a(q,"stringToRegexp");function T(e){switch(e){case 0:return "*";case 1:return "?";case 2:return "+";case 3:return ""}}a(T,"modifierToString");function W(e,t,r={}){r.delimiter??="/#?",r.prefixes??="./",r.sensitive??=false,r.strict??=false,r.end??=true,r.start??=true,r.endsWith="";let n=r.start?"^":"";for(let s of e){if(s.type===3){s.modifier===3?n+=x(s.value):n+=`(?:${x(s.value)})${T(s.modifier)}`;continue}t&&t.push(s.name);let i=`[^${x(r.delimiter)}]+?`,o=s.value;if(s.type===1?o=i:s.type===0&&(o=v),!s.prefix.length&&!s.suffix.length){s.modifier===3||s.modifier===1?n+=`(${o})${T(s.modifier)}`:n+=`((?:${o})${T(s.modifier)})`;continue}if(s.modifier===3||s.modifier===1){n+=`(?:${x(s.prefix)}(${o})${x(s.suffix)})`,n+=T(s.modifier);continue}n+=`(?:${x(s.prefix)}`,n+=`((?:${o})(?:`,n+=x(s.suffix),n+=x(s.prefix),n+=`(?:${o}))*)${x(s.suffix)})`,s.modifier===0&&(n+="?");}let c=`[${x(r.endsWith)}]|$`,l=`[${x(r.delimiter)}]`;if(r.end)return r.strict||(n+=`${l}?`),r.endsWith.length?n+=`(?=${c})`:n+="$",new RegExp(n,B(r));r.strict||(n+=`(?:${l}(?=${c}))?`);let f=false;if(e.length){let s=e[e.length-1];s.type===3&&s.modifier===3&&(f=r.delimiter.indexOf(s)>-1);}return f||(n+=`(?=${l}|${c})`),new RegExp(n,B(r))}a(W,"partsToRegexp");var b={delimiter:"",prefixes:"",sensitive:true,strict:true},J={delimiter:".",prefixes:"",sensitive:true,strict:true},Q={delimiter:"/",prefixes:"/",sensitive:true,strict:true};function ee(e,t){return e.length?e[0]==="/"?true:!t||e.length<2?false:(e[0]=="\\"||e[0]=="{")&&e[1]=="/":false}a(ee,"isAbsolutePathname");function te(e,t){return e.startsWith(t)?e.substring(t.length,e.length):e}a(te,"maybeStripPrefix");function ke(e,t){return e.endsWith(t)?e.substr(0,e.length-t.length):e}a(ke,"maybeStripSuffix");function _(e){return !e||e.length<2?false:e[0]==="["||(e[0]==="\\"||e[0]==="{")&&e[1]==="["}a(_,"treatAsIPv6Hostname");var re=["ftp","file","http","https","ws","wss"];function U(e){if(!e)return true;for(let t of re)if(e.test(t))return true;return false}a(U,"isSpecialScheme");function ne(e,t){if(e=te(e,"#"),t||e==="")return e;let r=new URL("https://example.com");return r.hash=e,r.hash?r.hash.substring(1,r.hash.length):""}a(ne,"canonicalizeHash");function se(e,t){if(e=te(e,"?"),t||e==="")return e;let r=new URL("https://example.com");return r.search=e,r.search?r.search.substring(1,r.search.length):""}a(se,"canonicalizeSearch");function ie(e,t){return t||e===""?e:_(e)?K(e):j(e)}a(ie,"canonicalizeHostname");function ae(e,t){if(t||e==="")return e;let r=new URL("https://example.com");return r.password=e,r.password}a(ae,"canonicalizePassword");function oe(e,t){if(t||e==="")return e;let r=new URL("https://example.com");return r.username=e,r.username}a(oe,"canonicalizeUsername");function ce(e,t,r){if(r||e==="")return e;if(t&&!re.includes(t))return new URL(`${t}:${e}`).pathname;let n=e[0]=="/";return e=new URL(n?e:"/-"+e,"https://example.com").pathname,n||(e=e.substring(2,e.length)),e}a(ce,"canonicalizePathname");function le(e,t,r){return z(t)===e&&(e=""),r||e===""?e:G(e)}a(le,"canonicalizePort");function fe(e,t){return e=ke(e,":"),t||e===""?e:w(e)}a(fe,"canonicalizeProtocol");function z(e){switch(e){case "ws":case "http":return "80";case "wws":case "https":return "443";case "ftp":return "21";default:return ""}}a(z,"defaultPortForProtocol");function w(e){if(e==="")return e;if(/^[-+.A-Za-z0-9]*$/.test(e))return e.toLowerCase();throw new TypeError(`Invalid protocol '${e}'.`)}a(w,"protocolEncodeCallback");function he(e){if(e==="")return e;let t=new URL("https://example.com");return t.username=e,t.username}a(he,"usernameEncodeCallback");function ue(e){if(e==="")return e;let t=new URL("https://example.com");return t.password=e,t.password}a(ue,"passwordEncodeCallback");function j(e){if(e==="")return e;if(/[\t\n\r #%/:<>?@[\]^\\|]/g.test(e))throw new TypeError(`Invalid hostname '${e}'`);let t=new URL("https://example.com");return t.hostname=e,t.hostname}a(j,"hostnameEncodeCallback");function K(e){if(e==="")return e;if(/[^0-9a-fA-F[\]:]/g.test(e))throw new TypeError(`Invalid IPv6 hostname '${e}'`);return e.toLowerCase()}a(K,"ipv6HostnameEncodeCallback");function G(e){if(e===""||/^[0-9]*$/.test(e)&&parseInt(e)<=65535)return e;throw new TypeError(`Invalid port '${e}'.`)}a(G,"portEncodeCallback");function de(e){if(e==="")return e;let t=new URL("https://example.com");return t.pathname=e[0]!=="/"?"/-"+e:e,e[0]!=="/"?t.pathname.substring(2,t.pathname.length):t.pathname}a(de,"standardURLPathnameEncodeCallback");function pe(e){return e===""?e:new URL(`data:${e}`).pathname}a(pe,"pathURLPathnameEncodeCallback");function ge(e){if(e==="")return e;let t=new URL("https://example.com");return t.search=e,t.search.substring(1,t.search.length)}a(ge,"searchEncodeCallback");function me(e){if(e==="")return e;let t=new URL("https://example.com");return t.hash=e,t.hash.substring(1,t.hash.length)}a(me,"hashEncodeCallback");var C=class{#i;#n=[];#t={};#e=0;#s=1;#l=0;#o=0;#d=0;#p=0;#g=false;constructor(t){this.#i=t;}get result(){return this.#t}parse(){for(this.#n=D(this.#i,true);this.#e<this.#n.length;this.#e+=this.#s){if(this.#s=1,this.#n[this.#e].type==="END"){if(this.#o===0){this.#b(),this.#f()?this.#r(9,1):this.#h()?this.#r(8,1):this.#r(7,0);continue}else if(this.#o===2){this.#u(5);continue}this.#r(10,0);break}if(this.#d>0)if(this.#A())this.#d-=1;else continue;if(this.#T()){this.#d+=1;continue}switch(this.#o){case 0:this.#P()&&this.#u(1);break;case 1:if(this.#P()){this.#C();let t=7,r=1;this.#E()?(t=2,r=3):this.#g&&(t=2),this.#r(t,r);}break;case 2:this.#S()?this.#u(3):(this.#x()||this.#h()||this.#f())&&this.#u(5);break;case 3:this.#O()?this.#r(4,1):this.#S()&&this.#r(5,1);break;case 4:this.#S()&&this.#r(5,1);break;case 5:this.#y()?this.#p+=1:this.#w()&&(this.#p-=1),this.#k()&&!this.#p?this.#r(6,1):this.#x()?this.#r(7,0):this.#h()?this.#r(8,1):this.#f()&&this.#r(9,1);break;case 6:this.#x()?this.#r(7,0):this.#h()?this.#r(8,1):this.#f()&&this.#r(9,1);break;case 7:this.#h()?this.#r(8,1):this.#f()&&this.#r(9,1);break;case 8:this.#f()&&this.#r(9,1);break;}}this.#t.hostname!==void 0&&this.#t.port===void 0&&(this.#t.port="");}#r(t,r){switch(this.#o){case 0:break;case 1:this.#t.protocol=this.#c();break;case 2:break;case 3:this.#t.username=this.#c();break;case 4:this.#t.password=this.#c();break;case 5:this.#t.hostname=this.#c();break;case 6:this.#t.port=this.#c();break;case 7:this.#t.pathname=this.#c();break;case 8:this.#t.search=this.#c();break;case 9:this.#t.hash=this.#c();break;}this.#o!==0&&t!==10&&([1,2,3,4].includes(this.#o)&&[6,7,8,9].includes(t)&&(this.#t.hostname??=""),[1,2,3,4,5,6].includes(this.#o)&&[8,9].includes(t)&&(this.#t.pathname??=this.#g?"/":""),[1,2,3,4,5,6,7].includes(this.#o)&&t===9&&(this.#t.search??="")),this.#R(t,r);}#R(t,r){this.#o=t,this.#l=this.#e+r,this.#e+=r,this.#s=0;}#b(){this.#e=this.#l,this.#s=0;}#u(t){this.#b(),this.#o=t;}#m(t){return t<0&&(t=this.#n.length-t),t<this.#n.length?this.#n[t]:this.#n[this.#n.length-1]}#a(t,r){let n=this.#m(t);return n.value===r&&(n.type==="CHAR"||n.type==="ESCAPED_CHAR"||n.type==="INVALID_CHAR")}#P(){return this.#a(this.#e,":")}#E(){return this.#a(this.#e+1,"/")&&this.#a(this.#e+2,"/")}#S(){return this.#a(this.#e,"@")}#O(){return this.#a(this.#e,":")}#k(){return this.#a(this.#e,":")}#x(){return this.#a(this.#e,"/")}#h(){if(this.#a(this.#e,"?"))return true;if(this.#n[this.#e].value!=="?")return false;let t=this.#m(this.#e-1);return t.type!=="NAME"&&t.type!=="REGEX"&&t.type!=="CLOSE"&&t.type!=="ASTERISK"}#f(){return this.#a(this.#e,"#")}#T(){return this.#n[this.#e].type=="OPEN"}#A(){return this.#n[this.#e].type=="CLOSE"}#y(){return this.#a(this.#e,"[")}#w(){return this.#a(this.#e,"]")}#c(){let t=this.#n[this.#e],r=this.#m(this.#l).index;return this.#i.substring(r,t.index)}#C(){let t={};Object.assign(t,b),t.encodePart=w;let r=q(this.#c(),void 0,t);this.#g=U(r);}};a(C,"Parser");var V=["protocol","username","password","hostname","port","pathname","search","hash"],O="*";function Se(e,t){if(typeof e!="string")throw new TypeError("parameter 1 is not of type 'string'.");let r=new URL(e,t);return {protocol:r.protocol.substring(0,r.protocol.length-1),username:r.username,password:r.password,hostname:r.hostname,port:r.port,pathname:r.pathname,search:r.search!==""?r.search.substring(1,r.search.length):void 0,hash:r.hash!==""?r.hash.substring(1,r.hash.length):void 0}}a(Se,"extractValues");function R(e,t){return t?I(e):e}a(R,"processBaseURLString");function L(e,t,r){let n;if(typeof t.baseURL=="string")try{n=new URL(t.baseURL),t.protocol===void 0&&(e.protocol=R(n.protocol.substring(0,n.protocol.length-1),r)),!r&&t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.username===void 0&&(e.username=R(n.username,r)),!r&&t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.username===void 0&&t.password===void 0&&(e.password=R(n.password,r)),t.protocol===void 0&&t.hostname===void 0&&(e.hostname=R(n.hostname,r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&(e.port=R(n.port,r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.pathname===void 0&&(e.pathname=R(n.pathname,r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.pathname===void 0&&t.search===void 0&&(e.search=R(n.search.substring(1,n.search.length),r)),t.protocol===void 0&&t.hostname===void 0&&t.port===void 0&&t.pathname===void 0&&t.search===void 0&&t.hash===void 0&&(e.hash=R(n.hash.substring(1,n.hash.length),r));}catch{throw new TypeError(`invalid baseURL '${t.baseURL}'.`)}if(typeof t.protocol=="string"&&(e.protocol=fe(t.protocol,r)),typeof t.username=="string"&&(e.username=oe(t.username,r)),typeof t.password=="string"&&(e.password=ae(t.password,r)),typeof t.hostname=="string"&&(e.hostname=ie(t.hostname,r)),typeof t.port=="string"&&(e.port=le(t.port,e.protocol,r)),typeof t.pathname=="string"){if(e.pathname=t.pathname,n&&!ee(e.pathname,r)){let c=n.pathname.lastIndexOf("/");c>=0&&(e.pathname=R(n.pathname.substring(0,c+1),r)+e.pathname);}e.pathname=ce(e.pathname,e.protocol,r);}return typeof t.search=="string"&&(e.search=se(t.search,r)),typeof t.hash=="string"&&(e.hash=ne(t.hash,r)),e}a(L,"applyInit");function I(e){return e.replace(/([+*?:{}()\\])/g,"\\$1")}a(I,"escapePatternString");function Te(e){return e.replace(/([.+*?^${}()[\]|/\\])/g,"\\$1")}a(Te,"escapeRegexpString");function Ae(e,t){t.delimiter??="/#?",t.prefixes??="./",t.sensitive??=false,t.strict??=false,t.end??=true,t.start??=true,t.endsWith="";let r=".*",n=`[^${Te(t.delimiter)}]+?`,c=/[$_\u200C\u200D\p{ID_Continue}]/u,l="";for(let f=0;f<e.length;++f){let s=e[f];if(s.type===3){if(s.modifier===3){l+=I(s.value);continue}l+=`{${I(s.value)}}${T(s.modifier)}`;continue}let i=s.hasCustomName(),o=!!s.suffix.length||!!s.prefix.length&&(s.prefix.length!==1||!t.prefixes.includes(s.prefix)),h=f>0?e[f-1]:null,p=f<e.length-1?e[f+1]:null;if(!o&&i&&s.type===1&&s.modifier===3&&p&&!p.prefix.length&&!p.suffix.length)if(p.type===3){let A=p.value.length>0?p.value[0]:"";o=c.test(A);}else o=!p.hasCustomName();if(!o&&!s.prefix.length&&h&&h.type===3){let A=h.value[h.value.length-1];o=t.prefixes.includes(A);}o&&(l+="{"),l+=I(s.prefix),i&&(l+=`:${s.name}`),s.type===2?l+=`(${s.value})`:s.type===1?i||(l+=`(${n})`):s.type===0&&(!i&&(!h||h.type===3||h.modifier!==3||o||s.prefix!=="")?l+="*":l+=`(${r})`),s.type===1&&i&&s.suffix.length&&c.test(s.suffix[0])&&(l+="\\"),l+=I(s.suffix),o&&(l+="}"),s.modifier!==3&&(l+=T(s.modifier));}return l}a(Ae,"partsToPattern");var Y=class{#i;#n={};#t={};#e={};#s={};#l=false;constructor(t={},r,n){try{let c;if(typeof r=="string"?c=r:n=r,typeof t=="string"){let i=new C(t);if(i.parse(),t=i.result,c===void 0&&typeof t.protocol!="string")throw new TypeError("A base URL must be provided for a relative constructor string.");t.baseURL=c;}else {if(!t||typeof t!="object")throw new TypeError("parameter 1 is not of type 'string' and cannot convert to dictionary.");if(c)throw new TypeError("parameter 1 is not of type 'string'.")}typeof n>"u"&&(n={ignoreCase:!1});let l={ignoreCase:n.ignoreCase===!0},f={pathname:O,protocol:O,username:O,password:O,hostname:O,port:O,search:O,hash:O};this.#i=L(f,t,!0),z(this.#i.protocol)===this.#i.port&&(this.#i.port="");let s;for(s of V){if(!(s in this.#i))continue;let i={},o=this.#i[s];switch(this.#t[s]=[],s){case "protocol":Object.assign(i,b),i.encodePart=w;break;case "username":Object.assign(i,b),i.encodePart=he;break;case "password":Object.assign(i,b),i.encodePart=ue;break;case "hostname":Object.assign(i,J),_(o)?i.encodePart=K:i.encodePart=j;break;case "port":Object.assign(i,b),i.encodePart=G;break;case "pathname":U(this.#n.protocol)?(Object.assign(i,Q,l),i.encodePart=de):(Object.assign(i,b,l),i.encodePart=pe);break;case "search":Object.assign(i,b,l),i.encodePart=ge;break;case "hash":Object.assign(i,b,l),i.encodePart=me;break}try{this.#s[s]=F(o,i),this.#n[s]=W(this.#s[s],this.#t[s],i),this.#e[s]=Ae(this.#s[s],i),this.#l=this.#l||this.#s[s].some(h=>h.type===2);}catch{throw new TypeError(`invalid ${s} pattern '${this.#i[s]}'.`)}}}catch(c){throw new TypeError(`Failed to construct 'URLPattern': ${c.message}`)}}get[Symbol.toStringTag](){return "URLPattern"}test(t={},r){let n={pathname:"",protocol:"",username:"",password:"",hostname:"",port:"",search:"",hash:""};if(typeof t!="string"&&r)throw new TypeError("parameter 1 is not of type 'string'.");if(typeof t>"u")return false;try{typeof t=="object"?n=L(n,t,!1):n=L(n,Se(t,r),!1);}catch{return false}let c;for(c of V)if(!this.#n[c].exec(n[c]))return false;return true}exec(t={},r){let n={pathname:"",protocol:"",username:"",password:"",hostname:"",port:"",search:"",hash:""};if(typeof t!="string"&&r)throw new TypeError("parameter 1 is not of type 'string'.");if(typeof t>"u")return;try{typeof t=="object"?n=L(n,t,!1):n=L(n,Se(t,r),!1);}catch{return null}let c={};r?c.inputs=[t,r]:c.inputs=[t];let l;for(l of V){let f=this.#n[l].exec(n[l]);if(!f)return null;let s={};for(let[i,o]of this.#t[l].entries())if(typeof o=="string"||typeof o=="number"){let h=f[i+1];s[o]=h;}c[l]={input:n[l]??"",groups:s};}return c}static compareComponent(t,r,n){let c=a((i,o)=>{for(let h of ["type","modifier","prefix","value","suffix"]){if(i[h]<o[h])return -1;if(i[h]===o[h])continue;return 1}return 0},"comparePart"),l=new P(3,"","","","",3),f=new P(0,"","","","",3),s=a((i,o)=>{let h=0;for(;h<Math.min(i.length,o.length);++h){let p=c(i[h],o[h]);if(p)return p}return i.length===o.length?0:c(i[h]??l,o[h]??l)},"comparePartList");return !r.#e[t]&&!n.#e[t]?0:r.#e[t]&&!n.#e[t]?s(r.#s[t],[f]):!r.#e[t]&&n.#e[t]?s([f],n.#s[t]):s(r.#s[t],n.#s[t])}get protocol(){return this.#e.protocol}get username(){return this.#e.username}get password(){return this.#e.password}get hostname(){return this.#e.hostname}get port(){return this.#e.port}get pathname(){return this.#e.pathname}get search(){return this.#e.search}get hash(){return this.#e.hash}get hasRegExpGroups(){return this.#l}};a(Y,"URLPattern");
601
+
602
+ function parseUrl(s) {
603
+ return new URL(typeof s === "string" ? s : s.href);
604
+ }
605
+ function depaginatePathname(url, pathnamePaginationSelector = /\/(page\/)?\d+\/?$/) {
606
+ const newUrl = new URL(url.toString());
607
+ newUrl.pathname = newUrl.pathname.replace(pathnamePaginationSelector, "/");
608
+ return newUrl;
609
+ }
610
+ function getPaginationLinks(doc = document, url = location.href, pathnamePaginationSelector = /\/(page\/)?\d+\/?$/) {
611
+ const baseUrl = depaginatePathname(parseUrl(url), pathnamePaginationSelector);
612
+ const pathnameStrict = doc instanceof Document;
613
+ const host = doc.baseURI || baseUrl.origin;
614
+ const urlPattern = new Y({
615
+ pathname: pathnameStrict ? `${baseUrl.pathname}*` : "*",
616
+ hostname: baseUrl.hostname
617
+ });
618
+ const pageLinks = [...doc.querySelectorAll("a[href]")].map((a) => a.href).filter((h) => URL.canParse(h));
619
+ return pageLinks.filter((h) => {
620
+ return urlPattern.test(new URL(h, host));
621
+ });
622
+ }
623
+ function upgradePathname(curr, links, pathnamePaginationSelector = /\/(page\/)?\d+\/?$/) {
624
+ if (pathnamePaginationSelector.test(curr.pathname) || links.length < 1) return curr;
625
+ const linksDepaginated = links.map(
626
+ (l) => depaginatePathname(l, pathnamePaginationSelector)
627
+ );
628
+ if (linksDepaginated.some((l) => l.pathname === curr.pathname)) return curr;
629
+ const last = linksDepaginated.at(-1);
630
+ if (last.pathname !== curr.pathname) curr.pathname = last.pathname;
631
+ return curr;
632
+ }
633
+
634
+ class PaginationStrategy {
635
+ doc = document;
636
+ url;
637
+ paginationSelector = ".pagination";
638
+ searchParamSelector = "page";
639
+ static _pathnameSelector = /\/(page\/)?\d+\/?$/;
640
+ pathnameSelector = /\/(\d+)\/?$/;
641
+ dataparamSelector = "[data-parameters *= from]";
642
+ overwritePaginationLast;
643
+ offsetMin = 1;
644
+ constructor(options) {
645
+ if (options) {
646
+ Object.entries(options).forEach(([k, v]) => {
647
+ Object.assign(this, { [k]: v });
648
+ });
649
+ }
650
+ this.url = parseUrl(options?.url || this.doc.URL);
651
+ }
652
+ getPaginationElement() {
653
+ return this.doc.querySelector(this.paginationSelector);
654
+ }
655
+ get hasPagination() {
656
+ return !!this.getPaginationElement();
436
657
  }
437
- function timeToSeconds(timeStr) {
438
- const normalized = /[a-zA-Z]/.test(timeStr) ? formatTimeToHHMMSS(timeStr) : timeStr;
439
- return normalized.split(":").reverse().reduce((total, unit, index) => total + parseInt(unit, 10) * 60 ** index, 0);
658
+ getPaginationOffset() {
659
+ return this.offsetMin;
440
660
  }
661
+ getPaginationLast() {
662
+ if (this.overwritePaginationLast) return this.overwritePaginationLast(1);
663
+ return 1;
664
+ }
665
+ getPaginationUrlGenerator() {
666
+ return (_) => this.url.href;
667
+ }
668
+ }
441
669
 
442
- function parseDataParams(str) {
443
- const paramsStr = decodeURI(str.trim()).split(";");
444
- return paramsStr.reduce(
445
- (acc, s) => {
446
- const parsed = s.match(/([+\w]+):([\w\- ]+)?/);
447
- if (parsed) {
448
- const [, key, value] = parsed;
449
- if (value) {
450
- key.split("+").forEach((p) => {
451
- acc[p] = value;
452
- });
453
- }
670
+ function formatTimeToHHMMSS(timeStr) {
671
+ const pad = (num) => num.toString().padStart(2, "0");
672
+ const h = timeStr.match(/(\d+)\s*h/)?.[1] || "0";
673
+ const m = timeStr.match(/(\d+)\s*mi?n/)?.[1] || "0";
674
+ const s = timeStr.match(/(\d+)\s*sec/)?.[1] || "0";
675
+ return `${pad(+h)}:${pad(+m)}:${pad(+s)}`;
676
+ }
677
+ function timeToSeconds(timeStr) {
678
+ const normalized = /[a-zA-Z]/.test(timeStr) ? formatTimeToHHMMSS(timeStr) : timeStr;
679
+ return normalized.split(":").reverse().reduce((total, unit, index) => total + parseInt(unit, 10) * 60 ** index, 0);
680
+ }
681
+
682
+ function parseDataParams(str) {
683
+ const paramsStr = decodeURI(str.trim()).split(";");
684
+ return paramsStr.reduce(
685
+ (acc, s) => {
686
+ const parsed = s.match(/([+\w]+):([\w\- ]+)?/);
687
+ if (parsed) {
688
+ const [, key, value] = parsed;
689
+ if (value) {
690
+ key.split("+").forEach((p) => {
691
+ acc[p] = value;
692
+ });
454
693
  }
455
- return acc;
456
- },
457
- {}
458
- );
459
- }
694
+ }
695
+ return acc;
696
+ },
697
+ {}
698
+ );
699
+ }
460
700
 
461
- class PaginationStrategyDataParams extends PaginationStrategy {
462
- getPaginationLast() {
463
- const links = this.getPaginationElement()?.querySelectorAll(this.dataparamSelector);
464
- const pages = Array.from(links || [], (l) => {
465
- const p = l.getAttribute("data-parameters");
466
- const v = p?.match(/from\w*:(\d+)/)?.[1] || this.offsetMin.toString();
467
- return parseInt(v);
468
- });
469
- const lastPage = Math.max(...pages, this.offsetMin);
470
- if (this.overwritePaginationLast) return this.overwritePaginationLast(lastPage);
471
- return lastPage;
472
- }
473
- getPaginationOffset() {
474
- const link = this.getPaginationElement()?.querySelector(
475
- ".prev[data-parameters *= from], .prev [data-parameters *= from]"
476
- );
477
- if (!link) return this.offsetMin;
478
- const p = link.getAttribute("data-parameters");
701
+ class PaginationStrategyDataParams extends PaginationStrategy {
702
+ getPaginationLast() {
703
+ const links = this.getPaginationElement()?.querySelectorAll(this.dataparamSelector);
704
+ const pages = Array.from(links || [], (l) => {
705
+ const p = l.getAttribute("data-parameters");
479
706
  const v = p?.match(/from\w*:(\d+)/)?.[1] || this.offsetMin.toString();
480
707
  return parseInt(v);
481
- }
482
- getPaginationUrlGenerator() {
483
- const url = new URL(this.url.href);
484
- const parametersElement = this.getPaginationElement()?.querySelector(
485
- "a[data-block-id][data-parameters]"
486
- );
487
- const block_id = parametersElement?.getAttribute("data-block-id") || "";
488
- const parameters = parseDataParams(
489
- parametersElement?.getAttribute("data-parameters") || ""
490
- );
491
- const attrs = {
492
- block_id,
493
- function: "get_block",
494
- mode: "async",
495
- ...parameters
496
- };
708
+ });
709
+ const lastPage = Math.max(...pages, this.offsetMin);
710
+ if (this.overwritePaginationLast) return this.overwritePaginationLast(lastPage);
711
+ return lastPage;
712
+ }
713
+ getPaginationOffset() {
714
+ const link = this.getPaginationElement()?.querySelector(
715
+ ".prev[data-parameters *= from], .prev [data-parameters *= from]"
716
+ );
717
+ if (!link) return this.offsetMin;
718
+ const p = link.getAttribute("data-parameters");
719
+ const v = p?.match(/from\w*:(\d+)/)?.[1] || this.offsetMin.toString();
720
+ return parseInt(v);
721
+ }
722
+ getPaginationUrlGenerator() {
723
+ const url = new URL(this.url.href);
724
+ const parametersElement = this.getPaginationElement()?.querySelector(
725
+ "a[data-block-id][data-parameters]"
726
+ );
727
+ const block_id = parametersElement?.getAttribute("data-block-id") || "";
728
+ const parameters = parseDataParams(
729
+ parametersElement?.getAttribute("data-parameters") || ""
730
+ );
731
+ const attrs = {
732
+ block_id,
733
+ function: "get_block",
734
+ mode: "async",
735
+ ...parameters
736
+ };
737
+ Object.keys(attrs).forEach((k) => {
738
+ url.searchParams.set(k, attrs[k]);
739
+ });
740
+ const paginationUrlGenerator = (n) => {
497
741
  Object.keys(attrs).forEach((k) => {
498
- url.searchParams.set(k, attrs[k]);
742
+ k.includes("from") && url.searchParams.set(k, n.toString());
499
743
  });
500
- const paginationUrlGenerator = (n) => {
501
- Object.keys(attrs).forEach((k) => {
502
- k.includes("from") && url.searchParams.set(k, n.toString());
503
- });
504
- url.searchParams.set("_", Date.now().toString());
505
- return url.href;
506
- };
507
- return paginationUrlGenerator;
508
- }
509
- static testLinks(doc = document) {
510
- const dataParamLinks = Array.from(
511
- doc.querySelectorAll("[data-parameters *= from]")
512
- );
513
- return dataParamLinks.length > 0;
514
- }
744
+ url.searchParams.set("_", Date.now().toString());
745
+ return url.href;
746
+ };
747
+ return paginationUrlGenerator;
515
748
  }
749
+ static testLinks(doc = document) {
750
+ const dataParamLinks = Array.from(
751
+ doc.querySelectorAll("[data-parameters *= from]")
752
+ );
753
+ return dataParamLinks.length > 0;
754
+ }
755
+ }
516
756
 
517
- class PaginationStrategyPathnameParams extends PaginationStrategy {
518
- extractPage = (a) => {
519
- const href = typeof a === "string" ? a : a.href;
520
- const { pathname } = new URL(href, this.doc.baseURI || this.url.origin);
521
- return parseInt(
522
- pathname.match(this.pathnameSelector)?.pop() || this.offsetMin.toString()
523
- );
524
- };
525
- static checkLink(link, pathnameSelector = PaginationStrategy._pathnameSelector) {
526
- return pathnameSelector.test(link.pathname);
527
- }
528
- static testLinks(links, options) {
529
- const result = links.some(
757
+ class PaginationStrategyPathnameParams extends PaginationStrategy {
758
+ extractPage = (a) => {
759
+ const href = typeof a === "string" ? a : a.href;
760
+ const { pathname } = new URL(href, this.doc.baseURI || this.url.origin);
761
+ return parseInt(
762
+ pathname.match(this.pathnameSelector)?.pop() || this.offsetMin.toString()
763
+ );
764
+ };
765
+ static checkLink(link, pathnameSelector = PaginationStrategy._pathnameSelector) {
766
+ return pathnameSelector.test(link.pathname);
767
+ }
768
+ static testLinks(links, options) {
769
+ const result = links.some(
770
+ (h) => PaginationStrategyPathnameParams.checkLink(h, options.pathnameSelector)
771
+ );
772
+ if (result) {
773
+ const pathnamesMatched = links.filter(
530
774
  (h) => PaginationStrategyPathnameParams.checkLink(h, options.pathnameSelector)
531
775
  );
532
- if (result) {
533
- const pathnamesMatched = links.filter(
534
- (h) => PaginationStrategyPathnameParams.checkLink(h, options.pathnameSelector)
535
- );
536
- options.url = upgradePathname(
537
- parseUrl(options.url),
538
- pathnamesMatched
539
- );
540
- }
541
- return result;
542
- }
543
- getPaginationLast() {
544
- const links = getPaginationLinks(
545
- this.getPaginationElement() || document,
546
- this.url.href,
547
- this.pathnameSelector
776
+ options.url = upgradePathname(
777
+ parseUrl(options.url),
778
+ pathnamesMatched
548
779
  );
549
- const pages = Array.from(links, this.extractPage);
550
- const lastPage = Math.max(...pages, this.offsetMin);
551
- if (this.overwritePaginationLast) return this.overwritePaginationLast(lastPage);
552
- return lastPage;
553
- }
554
- getPaginationOffset() {
555
- return this.extractPage(this.url.href);
556
- }
557
- getPaginationUrlGenerator(url_ = this.url) {
558
- const url = new URL(url_.href);
559
- const pathnameSelectorPlaceholder = this.pathnameSelector.toString().replace(/[/|\\|$|?|(|)]+/g, "/");
560
- if (!this.pathnameSelector.test(url.pathname)) {
561
- url.pathname = url.pathname.concat(pathnameSelectorPlaceholder.replace(/d\+/, this.offsetMin.toString())).replace(/\/{2,}/g, "/");
562
- }
563
- const paginationUrlGenerator = (offset) => {
564
- url.pathname = url.pathname.replace(
565
- this.pathnameSelector,
566
- pathnameSelectorPlaceholder.replace(/d\+/, offset.toString())
567
- );
568
- return url.href;
569
- };
570
- return paginationUrlGenerator;
571
780
  }
781
+ return result;
572
782
  }
573
-
574
- class PaginationStrategySearchParams extends PaginationStrategy {
575
- extractPage = (a) => {
576
- const href = typeof a === "string" ? a : a.href;
577
- const p = new URL(href).searchParams.get(this.searchParamSelector);
578
- return parseInt(p) || this.offsetMin;
579
- };
580
- getPaginationLast() {
581
- const links = getPaginationLinks(
582
- this.getPaginationElement() || document,
583
- this.url.href
584
- ).filter(
585
- (h) => PaginationStrategySearchParams.checkLink(new URL(h), this.searchParamSelector)
586
- );
587
- const pages = links.map(this.extractPage);
588
- const lastPage = Math.max(...pages, this.offsetMin);
589
- if (this.overwritePaginationLast) return this.overwritePaginationLast(lastPage);
590
- return lastPage;
591
- }
592
- getPaginationOffset() {
593
- if (this.doc === document) {
594
- return this.extractPage(this.url);
595
- }
596
- const link = this.getPaginationElement()?.querySelector(
597
- `a.active[href *= "${this.searchParamSelector}="]`
598
- );
599
- return this.extractPage(link);
600
- }
601
- getPaginationUrlGenerator() {
602
- const url = new URL(this.url.href);
603
- const paginationUrlGenerator = (offset) => {
604
- url.searchParams.set(this.searchParamSelector, offset.toString());
605
- return url.href;
606
- };
607
- return paginationUrlGenerator;
608
- }
609
- static checkLink(link, searchParamSelector) {
610
- const searchParamSelectors = ["page", "p"];
611
- if (searchParamSelector) searchParamSelectors.push(searchParamSelector);
612
- return searchParamSelectors.some((p) => link.searchParams.get(p) !== null);
613
- }
614
- static testLinks(links, searchParamSelector) {
615
- return links.some(
616
- (h) => PaginationStrategySearchParams.checkLink(h, searchParamSelector)
783
+ getPaginationLast() {
784
+ const links = getPaginationLinks(
785
+ this.getPaginationElement() || document,
786
+ this.url.href,
787
+ this.pathnameSelector
788
+ );
789
+ const pages = Array.from(links, this.extractPage);
790
+ const lastPage = Math.max(...pages, this.offsetMin);
791
+ if (this.overwritePaginationLast) return this.overwritePaginationLast(lastPage);
792
+ return lastPage;
793
+ }
794
+ getPaginationOffset() {
795
+ return this.extractPage(this.url.href);
796
+ }
797
+ getPaginationUrlGenerator(url_ = this.url) {
798
+ const url = new URL(url_.href);
799
+ const pathnameSelectorPlaceholder = this.pathnameSelector.toString().replace(/[/|\\|$|?|(|)]+/g, "/");
800
+ if (!this.pathnameSelector.test(url.pathname)) {
801
+ url.pathname = url.pathname.concat(pathnameSelectorPlaceholder.replace(/d\+/, this.offsetMin.toString())).replace(/\/{2,}/g, "/");
802
+ }
803
+ const paginationUrlGenerator = (offset) => {
804
+ url.pathname = url.pathname.replace(
805
+ this.pathnameSelector,
806
+ pathnameSelectorPlaceholder.replace(/d\+/, offset.toString())
617
807
  );
618
- }
808
+ return url.href;
809
+ };
810
+ return paginationUrlGenerator;
619
811
  }
812
+ }
620
813
 
621
- function getPaginationStrategy(options) {
622
- const _paginationStrategy = new PaginationStrategy(options);
623
- const pagination = _paginationStrategy.getPaginationElement();
624
- Object.assign(options, { ..._paginationStrategy });
625
- const { url, searchParamSelector } = options;
626
- if (!pagination) {
627
- return _paginationStrategy;
628
- }
629
- if (typeof options.getPaginationUrlGenerator === "function") {
630
- return new PaginationStrategy(options);
814
+ class PaginationStrategySearchParams extends PaginationStrategy {
815
+ extractPage = (a) => {
816
+ const href = typeof a === "string" ? a : a.href;
817
+ const p = new URL(href).searchParams.get(this.searchParamSelector);
818
+ return parseInt(p) || this.offsetMin;
819
+ };
820
+ getPaginationLast() {
821
+ const links = getPaginationLinks(
822
+ this.getPaginationElement() || document,
823
+ this.url.href
824
+ ).filter(
825
+ (h) => PaginationStrategySearchParams.checkLink(new URL(h), this.searchParamSelector)
826
+ );
827
+ const pages = links.map(this.extractPage);
828
+ const lastPage = Math.max(...pages, this.offsetMin);
829
+ if (this.overwritePaginationLast) return this.overwritePaginationLast(lastPage);
830
+ return lastPage;
831
+ }
832
+ getPaginationOffset() {
833
+ if (this.doc === document) {
834
+ return this.extractPage(this.url);
631
835
  }
632
- const pageLinks = getPaginationLinks(pagination, url).map((l) => new URL(l));
633
- const selectStrategy = () => {
634
- if (PaginationStrategyDataParams.testLinks(pagination)) {
635
- return PaginationStrategyDataParams;
636
- }
637
- if (PaginationStrategySearchParams.testLinks(pageLinks, searchParamSelector)) {
638
- return PaginationStrategySearchParams;
639
- }
640
- if (PaginationStrategyPathnameParams.testLinks(pageLinks, options)) {
641
- return PaginationStrategyPathnameParams;
642
- }
643
- console.error("Found No Strategy");
644
- return PaginationStrategy;
836
+ const link = this.getPaginationElement()?.querySelector(
837
+ `a.active[href *= "${this.searchParamSelector}="]`
838
+ );
839
+ return this.extractPage(link);
840
+ }
841
+ getPaginationUrlGenerator() {
842
+ const url = new URL(this.url.href);
843
+ const paginationUrlGenerator = (offset) => {
844
+ url.searchParams.set(this.searchParamSelector, offset.toString());
845
+ return url.href;
645
846
  };
646
- const PaginationStrategyConstructor = selectStrategy();
647
- const paginationStrategy = new PaginationStrategyConstructor(options);
648
- return paginationStrategy;
847
+ return paginationUrlGenerator;
649
848
  }
650
-
651
- function memoize(fn) {
652
- const cache = new Map();
653
- const memoizedFunction = ((...args) => {
654
- const key = JSON.stringify(args);
655
- if (cache.has(key)) {
656
- return cache.get(key);
657
- }
658
- const result = fn(...args);
659
- cache.set(key, result);
660
- return result;
661
- });
662
- return memoizedFunction;
849
+ static checkLink(link, searchParamSelector) {
850
+ const searchParamSelectors = ["page", "p"];
851
+ if (searchParamSelector) searchParamSelectors.push(searchParamSelector);
852
+ return searchParamSelectors.some((p) => link.searchParams.get(p) !== null);
663
853
  }
664
-
665
- function objectToFormData(obj) {
666
- const formData = new FormData();
667
- Object.entries(obj).forEach(([k, v]) => {
668
- formData.append(k, v);
669
- });
670
- return formData;
854
+ static testLinks(links, searchParamSelector) {
855
+ return links.some(
856
+ (h) => PaginationStrategySearchParams.checkLink(h, searchParamSelector)
857
+ );
671
858
  }
859
+ }
672
860
 
673
- class RegexFilter {
674
- regexes;
675
- constructor(str, flags = "gi") {
676
- this.regexes = memoize(this.compileSearchRegex)(str, flags);
677
- }
678
- compileSearchRegex(str, flags) {
679
- try {
680
- if (str.startsWith("r:")) return [new RegExp(str.slice(2), flags)];
681
- const regexes = splitWith(str).map(
682
- (s) => s.replace(/f:(\w+)/g, (_, w) => `(^|\\ |,)${w}($|\\ |,)`)
683
- ).map((_) => new RegExp(_, flags));
684
- return regexes;
685
- } catch (_) {
686
- return [];
687
- }
861
+ function getPaginationStrategy(options) {
862
+ const _paginationStrategy = new PaginationStrategy(options);
863
+ const pagination = _paginationStrategy.getPaginationElement();
864
+ Object.assign(options, { ..._paginationStrategy });
865
+ const { url, searchParamSelector } = options;
866
+ if (!pagination) {
867
+ return _paginationStrategy;
868
+ }
869
+ if (typeof options.getPaginationUrlGenerator === "function") {
870
+ return new PaginationStrategy(options);
871
+ }
872
+ const pageLinks = getPaginationLinks(pagination, url).map((l) => new URL(l));
873
+ const selectStrategy = () => {
874
+ if (PaginationStrategyDataParams.testLinks(pagination)) {
875
+ return PaginationStrategyDataParams;
688
876
  }
689
- hasEvery(str) {
690
- return this.regexes.every((r) => r.test(str));
877
+ if (PaginationStrategySearchParams.testLinks(pageLinks, searchParamSelector)) {
878
+ return PaginationStrategySearchParams;
691
879
  }
692
- hasNone(str) {
693
- return this.regexes.every((r) => !r.test(str));
880
+ if (PaginationStrategyPathnameParams.testLinks(pageLinks, options)) {
881
+ return PaginationStrategyPathnameParams;
694
882
  }
695
- }
883
+ console.error("Found No Strategy");
884
+ return PaginationStrategy;
885
+ };
886
+ const PaginationStrategyConstructor = selectStrategy();
887
+ const paginationStrategy = new PaginationStrategyConstructor(options);
888
+ return paginationStrategy;
889
+ }
696
890
 
697
- class DataFilter {
698
- constructor(rules) {
699
- this.rules = rules;
700
- this.registerFilters(rules.customDataSelectorFns);
701
- this.applyCSSFilters();
702
- }
703
- filters = new Map();
704
- static isFiltered(el) {
705
- return el.className.includes("filter-");
706
- }
707
- applyCSSFilters(wrapper) {
708
- this.filters.forEach((_, name) => {
709
- const cssRule = `.filter-${name} { display: none !important; }`;
710
- if (wrapper) {
711
- _GM_addStyle(wrapper(cssRule));
712
- } else {
713
- _GM_addStyle(cssRule);
891
+ const DefaultScheme = [
892
+ {
893
+ title: "Text Filter",
894
+ collapsed: true,
895
+ content: [
896
+ { filterExclude: false, label: "exclude" },
897
+ {
898
+ filterExcludeWords: "",
899
+ label: "keywords",
900
+ watch: "filterExclude",
901
+ placeholder: "word, f:full_word, r:RegEx..."
902
+ },
903
+ { filterInclude: false, label: "include" },
904
+ {
905
+ filterIncludeWords: "",
906
+ label: "keywords",
907
+ watch: "filterInclude",
908
+ placeholder: "word, f:full_word, r:RegEx..."
909
+ }
910
+ ]
911
+ },
912
+ {
913
+ title: "Duration Filter",
914
+ collapsed: true,
915
+ content: [
916
+ { filterDuration: false, label: "enable" },
917
+ {
918
+ filterDurationFrom: 0,
919
+ watch: "filterDuration",
920
+ label: "from",
921
+ type: "time"
922
+ },
923
+ {
924
+ filterDurationTo: 600,
925
+ watch: "filterDuration",
926
+ label: "to",
927
+ type: "time"
928
+ }
929
+ ]
930
+ },
931
+ {
932
+ title: "Sort By",
933
+ content: [
934
+ {
935
+ "sort by views": () => {
714
936
  }
715
- });
716
- }
717
- customDataSelectorFns = {};
718
- registerFilters(customFilters) {
719
- customFilters.forEach((o) => {
720
- if (typeof o === "string") {
721
- this.customDataSelectorFns[o] = DataFilter.customDataSelectorFnsDefault[o];
722
- this.registerFilter(o);
723
- } else {
724
- const k = Object.keys(o)[0];
725
- this.customDataSelectorFns[k] = o[k];
726
- this.registerFilter(k);
937
+ },
938
+ {
939
+ "sort by duration": () => {
727
940
  }
728
- });
729
- }
730
- customSelectorParser(name, selector) {
731
- if ("handle" in selector) {
732
- return selector;
733
- } else {
734
- return { handle: selector, deps: [name] };
735
941
  }
736
- }
737
- registerFilter(customSelectorName) {
738
- const handler = this.customSelectorParser(
739
- customSelectorName,
740
- this.customDataSelectorFns[customSelectorName]
741
- );
742
- const tag = `filter-${customSelectorName}`;
743
- [customSelectorName, ...handler.deps || []]?.forEach((name) => {
744
- Object.assign(this.filterMapping, { [name]: customSelectorName });
745
- });
746
- const fn = () => {
747
- const preDefined = handler.$preDefine?.(this.rules.store.state);
748
- return (v) => {
749
- const condition = handler.handle(v, this.rules.store.state, preDefined);
750
- return {
751
- condition,
752
- tag
753
- };
754
- };
755
- };
756
- this.filters.set(customSelectorName, fn);
757
- }
758
- filterMapping = {};
759
- selectFilters(filters) {
760
- const selectedFilters = Object.keys(filters).filter((k) => k in this.filterMapping).map((k) => this.filterMapping[k]).map((k) => this.filters.get(k));
761
- return selectedFilters;
762
- }
763
- static customDataSelectorFnsDefault = {
764
- filterDuration: {
765
- handle(el, state, notInRange) {
766
- return state.filterDuration && notInRange(el.duration);
767
- },
768
- $preDefine: (state) => {
769
- const from = state.filterDurationFrom;
770
- const to = state.filterDurationTo;
771
- function notInRange(d) {
772
- return d < from || d > to;
773
- }
774
- return notInRange;
775
- },
776
- deps: ["filterDurationFrom", "filterDurationTo"]
942
+ ]
943
+ },
944
+ {
945
+ title: "Privacy Filter",
946
+ content: [
947
+ { filterPrivate: false, label: "private" },
948
+ { filterPublic: false, label: "public" },
949
+ { "check access 🔓": () => {
950
+ } }
951
+ ]
952
+ },
953
+ {
954
+ title: "Advanced",
955
+ content: [
956
+ {
957
+ infiniteScrollEnabled: true,
958
+ label: "infinite scroll"
777
959
  },
778
- filterExclude: {
779
- handle(el, state, searchFilter) {
780
- if (!state.filterExclude) return false;
781
- return !searchFilter.hasNone(el.title);
782
- },
783
- $preDefine: (state) => new RegexFilter(state.filterExcludeWords),
784
- deps: ["filterExcludeWords"]
960
+ {
961
+ autoScroll: false,
962
+ label: "auto scroll"
785
963
  },
786
- filterInclude: {
787
- handle(el, state, searchFilter) {
788
- if (!state.filterInclude) return false;
789
- return !searchFilter.hasEvery(el.title);
790
- },
791
- $preDefine: (state) => new RegexFilter(state.filterIncludeWords),
792
- deps: ["filterIncludeWords"]
964
+ {
965
+ delay: 250,
966
+ label: "scroll delay"
967
+ },
968
+ {
969
+ writeHistory: false,
970
+ label: "write history"
793
971
  }
794
- };
972
+ ]
973
+ },
974
+ {
975
+ title: "Badge",
976
+ content: [
977
+ {
978
+ text: "return `${state.$paginationOffset}/${state.$paginationLast}`",
979
+ vif: "return state.$paginationLast > 1"
980
+ }
981
+ ]
795
982
  }
983
+ ];
796
984
 
797
- class DataManager {
798
- constructor(rules) {
799
- this.rules = rules;
800
- this.dataFilter = new DataFilter(this.rules);
985
+ const StoreStateDefault = {
986
+ enabled: true,
987
+ collapsed: false,
988
+ darkmode: true,
989
+ $paginationLast: 1,
990
+ $paginationOffset: 1
991
+ };
992
+
993
+ class RulesGlobal {
994
+ delay;
995
+ customGenerator;
996
+ getThumbUrl(thumb) {
997
+ return (thumb.querySelector("a[href]") || thumb).href;
998
+ }
999
+ titleSelector;
1000
+ uploaderSelector;
1001
+ durationSelector;
1002
+ customThumbDataSelectors;
1003
+ getThumbDataStrategy = "default";
1004
+ getThumbDataCallback;
1005
+ getThumbData(thumb) {
1006
+ let { titleSelector, uploaderSelector, durationSelector } = this;
1007
+ const thumbData = { title: "" };
1008
+ if (this.getThumbDataStrategy === "auto-text") {
1009
+ const text = sanitizeStr(thumb.innerText);
1010
+ thumbData.title = text;
1011
+ thumbData.duration = timeToSeconds(text.match(/\d+m|\d+:\d+/)?.[0] || "");
1012
+ return thumbData;
801
1013
  }
802
- data = new Map();
803
- lazyImgLoader = new LazyImgLoader(
804
- (target) => !DataFilter.isFiltered(target)
805
- );
806
- dataFilter;
807
- applyFilters = async (filters = {}, offset = 0) => {
808
- const filtersToApply = this.dataFilter.selectFilters(filters);
809
- if (filtersToApply.length === 0) return;
810
- const iterator = this.data.values().drop(offset);
811
- let finished = false;
812
- await new Promise((resolve) => {
813
- function runBatch(deadline) {
814
- const updates = [];
815
- while (deadline.timeRemaining() > 0) {
816
- const { value, done } = iterator.next();
817
- finished = !!done;
818
- if (done) break;
819
- for (const f of filtersToApply) {
820
- const { tag, condition } = f()(value);
821
- updates.push({ e: value.element, tag, condition });
822
- }
823
- }
824
- if (updates.length > 0) {
825
- requestAnimationFrame(() => {
826
- updates.forEach((u) => {
827
- u.e.classList.toggle(u.tag, u.condition);
828
- });
829
- });
830
- }
831
- if (!finished) {
832
- requestIdleCallback(runBatch);
833
- } else {
834
- resolve(true);
835
- }
836
- }
837
- requestIdleCallback(runBatch);
838
- });
839
- };
840
- filterAll = async (offset) => {
841
- const keys = Array.from(this.dataFilter.filters.keys());
842
- const filters = Object.fromEntries(
843
- keys.map((k) => [k, this.rules.store.state[k]])
844
- );
845
- await this.applyFilters(filters, offset);
846
- };
847
- parseDataParentHomogenity;
848
- parseData = (html, container, removeDuplicates = false, shouldLazify = true) => {
849
- const thumbs = this.rules.getThumbs(html);
850
- const dataOffset = this.data.size;
851
- const fragment = document.createDocumentFragment();
852
- const parent = container || this.rules.container;
853
- const homogenity = !!this.parseDataParentHomogenity;
854
- for (const thumbElement of thumbs) {
855
- const url = this.rules.getThumbUrl(thumbElement);
856
- if (!url || this.data.has(url) || parent !== container && parent?.contains(thumbElement) || homogenity && !checkHomogenity(
857
- parent,
858
- thumbElement.parentElement,
859
- this.parseDataParentHomogenity
860
- )) {
861
- if (removeDuplicates) thumbElement.remove();
862
- continue;
863
- }
864
- const data = this.rules.getThumbData(thumbElement);
865
- this.data.set(url, { element: thumbElement, ...data });
866
- if (shouldLazify) {
867
- const { img, imgSrc } = this.rules.getThumbImgData(thumbElement);
868
- this.lazyImgLoader.lazify(thumbElement, img, imgSrc);
869
- }
870
- fragment.append(thumbElement);
1014
+ if (this.getThumbDataStrategy === "auto-select") {
1015
+ titleSelector = "[class *= title],[title]";
1016
+ durationSelector = "[class *= duration]";
1017
+ uploaderSelector = "[class *= uploader], [class *= user], [class *= name]";
1018
+ }
1019
+ if (this.getThumbDataStrategy === "auto-select") {
1020
+ const selected = querySelectorLast(thumb, titleSelector);
1021
+ if (selected) {
1022
+ thumbData.title = sanitizeStr(selected.innerText);
1023
+ } else {
1024
+ thumbData.title = sanitizeStr(thumb.innerText);
871
1025
  }
872
- this.filterAll(dataOffset).then(() => {
873
- requestAnimationFrame(() => {
874
- parent.appendChild(fragment);
875
- });
876
- });
877
- };
878
- sortBy(key, direction = true) {
879
- if (this.data.size < 2) return;
880
- let sorted = this.data.values().toArray().sort((a, b) => {
881
- return a[key] - b[key];
882
- });
883
- if (!direction) sorted = sorted.reverse();
884
- const container = sorted[0].element.parentElement;
885
- container.style.visibility = "hidden";
886
- sorted.forEach((s) => {
887
- container.append(s.element);
888
- });
889
- container.style.visibility = "visible";
1026
+ } else {
1027
+ thumbData.title = querySelectorText(thumb, titleSelector);
890
1028
  }
891
- }
892
-
893
- const DefaultScheme = [
894
- {
895
- title: "Text Filter",
896
- collapsed: true,
897
- content: [
898
- { filterExclude: false, label: "exclude" },
899
- {
900
- filterExcludeWords: "",
901
- label: "keywords",
902
- watch: "filterExclude",
903
- placeholder: "word, f:full_word, r:RegEx..."
904
- },
905
- { filterInclude: false, label: "include" },
906
- {
907
- filterIncludeWords: "",
908
- label: "keywords",
909
- watch: "filterInclude",
910
- placeholder: "word, f:full_word, r:RegEx..."
911
- }
912
- ]
913
- },
914
- {
915
- title: "Duration Filter",
916
- collapsed: true,
917
- content: [
918
- { filterDuration: false, label: "enable" },
919
- {
920
- filterDurationFrom: 0,
921
- watch: "filterDuration",
922
- label: "from",
923
- type: "time"
924
- },
925
- {
926
- filterDurationTo: 600,
927
- watch: "filterDuration",
928
- label: "to",
929
- type: "time"
930
- }
931
- ]
932
- },
933
- {
934
- title: "Sort By",
935
- content: [
936
- {
937
- "sort by views": () => {
938
- }
939
- },
940
- {
941
- "sort by duration": () => {
942
- }
943
- }
944
- ]
945
- },
946
- {
947
- title: "Privacy Filter",
948
- content: [
949
- { filterPrivate: false, label: "private" },
950
- { filterPublic: false, label: "public" },
951
- { "check access 🔓": () => {
952
- } }
953
- ]
954
- },
955
- {
956
- title: "Advanced",
957
- content: [
958
- {
959
- infiniteScrollEnabled: true,
960
- label: "infinite scroll"
961
- },
962
- {
963
- autoScroll: false,
964
- label: "auto scroll"
965
- },
966
- {
967
- delay: 250,
968
- label: "scroll delay"
969
- },
970
- {
971
- writeHistory: false,
972
- label: "write history"
973
- }
974
- ]
975
- },
976
- {
977
- title: "Badge",
978
- content: [
979
- {
980
- text: "return `${state.$paginationOffset}/${state.$paginationLast}`",
981
- vif: "return state.$paginationLast > 1"
982
- }
983
- ]
1029
+ if (uploaderSelector) {
1030
+ const uploader = querySelectorText(thumb, uploaderSelector);
1031
+ thumbData.title = `${thumbData.title} user:${uploader}`;
984
1032
  }
985
- ];
986
-
987
- const StoreStateDefault = {
988
- enabled: true,
989
- collapsed: false,
990
- darkmode: true,
991
- $paginationLast: 1,
992
- $paginationOffset: 1
993
- };
994
-
995
- class RulesGlobal {
996
- delay;
997
- customGenerator;
998
- getThumbUrl(thumb) {
999
- return (thumb.querySelector("a[href]") || thumb).href;
1033
+ if (durationSelector) {
1034
+ const duration = timeToSeconds(querySelectorText(thumb, durationSelector));
1035
+ thumbData.duration = duration;
1000
1036
  }
1001
- titleSelector;
1002
- uploaderSelector;
1003
- durationSelector;
1004
- customThumbDataSelectors;
1005
- getThumbDataStrategy = "default";
1006
- getThumbDataCallback;
1007
- getThumbData(thumb) {
1008
- let { titleSelector, uploaderSelector, durationSelector } = this;
1009
- const thumbData = { title: "" };
1010
- if (this.getThumbDataStrategy === "auto-text") {
1011
- const text = sanitizeStr(thumb.innerText);
1012
- thumbData.title = text;
1013
- thumbData.duration = timeToSeconds(text.match(/\d+m|\d+:\d+/)?.[0] || "");
1014
- return thumbData;
1037
+ this.getThumbDataCallback?.(thumb, thumbData);
1038
+ function getCustomThumbData(selector, type) {
1039
+ if (type === "boolean") {
1040
+ return !!thumb.querySelector(selector);
1015
1041
  }
1016
- if (this.getThumbDataStrategy === "auto-select") {
1017
- titleSelector = "[class *= title],[title]";
1018
- durationSelector = "[class *= duration]";
1019
- uploaderSelector = "[class *= uploader], [class *= user], [class *= name]";
1042
+ if (type === "string") {
1043
+ return querySelectorText(thumb, selector);
1020
1044
  }
1021
- if (this.getThumbDataStrategy === "auto-select") {
1022
- const selected = querySelectorLast(thumb, titleSelector);
1023
- if (selected) {
1024
- thumbData.title = sanitizeStr(selected.innerText);
1025
- } else {
1026
- thumbData.title = sanitizeStr(thumb.innerText);
1027
- }
1045
+ return Number.parseInt(querySelectorText(thumb, selector));
1046
+ }
1047
+ if (this.customThumbDataSelectors) {
1048
+ Object.entries(this.customThumbDataSelectors).forEach(([name, x]) => {
1049
+ const data = getCustomThumbData(x.selector, x.type);
1050
+ Object.assign(thumbData, { [name]: data });
1051
+ });
1052
+ }
1053
+ return thumbData;
1054
+ }
1055
+ getThumbImgDataAttrSelector;
1056
+ getThumbImgDataAttrDelete;
1057
+ getThumbImgDataStrategy = "default";
1058
+ getThumbImgData(thumb) {
1059
+ const result = {};
1060
+ if (this.getThumbImgDataStrategy === "auto") {
1061
+ const img = thumb.querySelector("img");
1062
+ if (!img) return {};
1063
+ result.img = img;
1064
+ if (typeof this.getThumbImgDataAttrSelector === "function") {
1065
+ result.imgSrc = this.getThumbImgDataAttrSelector(img);
1028
1066
  } else {
1029
- thumbData.title = querySelectorText(thumb, titleSelector);
1030
- }
1031
- if (uploaderSelector) {
1032
- const uploader = querySelectorText(thumb, uploaderSelector);
1033
- thumbData.title = `${thumbData.title} user:${uploader}`;
1034
- }
1035
- if (durationSelector) {
1036
- const duration = timeToSeconds(querySelectorText(thumb, durationSelector));
1037
- thumbData.duration = duration;
1038
- }
1039
- this.getThumbDataCallback?.(thumb, thumbData);
1040
- function getCustomThumbData(selector, type) {
1041
- if (type === "boolean") {
1042
- return !!thumb.querySelector(selector);
1043
- }
1044
- if (type === "string") {
1045
- return querySelectorText(thumb, selector);
1067
+ const possibleAttrs = this.getThumbImgDataAttrSelector ? [this.getThumbImgDataAttrSelector].flat() : ["data-src", "src"];
1068
+ for (const attr of possibleAttrs) {
1069
+ const imgSrc = img.getAttribute(attr);
1070
+ if (imgSrc) {
1071
+ result.imgSrc = imgSrc;
1072
+ img.removeAttribute(attr);
1073
+ break;
1074
+ }
1046
1075
  }
1047
- return Number.parseInt(querySelectorText(thumb, selector));
1048
- }
1049
- if (this.customThumbDataSelectors) {
1050
- Object.entries(this.customThumbDataSelectors).forEach(([name, x]) => {
1051
- const data = getCustomThumbData(x.selector, x.type);
1052
- Object.assign(thumbData, { [name]: data });
1053
- });
1054
1076
  }
1055
- return thumbData;
1056
- }
1057
- getThumbImgDataAttrSelector;
1058
- getThumbImgDataAttrDelete;
1059
- getThumbImgDataStrategy = "default";
1060
- getThumbImgData(thumb) {
1061
- const result = {};
1062
- if (this.getThumbImgDataStrategy === "auto") {
1063
- const img = thumb.querySelector("img");
1064
- if (!img) return {};
1065
- result.img = img;
1066
- if (typeof this.getThumbImgDataAttrSelector === "function") {
1067
- result.imgSrc = this.getThumbImgDataAttrSelector(img);
1077
+ if (this.getThumbImgDataAttrDelete) {
1078
+ if (this.getThumbImgDataAttrDelete === "auto") {
1079
+ removeClassesAndDataAttributes(img, "lazy");
1068
1080
  } else {
1069
- const possibleAttrs = this.getThumbImgDataAttrSelector ? [this.getThumbImgDataAttrSelector].flat() : ["data-src", "src"];
1070
- for (const attr of possibleAttrs) {
1071
- const imgSrc = img.getAttribute(attr);
1072
- if (imgSrc) {
1073
- result.imgSrc = imgSrc;
1074
- img.removeAttribute(attr);
1075
- break;
1076
- }
1077
- }
1078
- }
1079
- if (this.getThumbImgDataAttrDelete) {
1080
- if (this.getThumbImgDataAttrDelete === "auto") {
1081
- removeClassesAndDataAttributes(img, "lazy");
1081
+ if (this.getThumbImgDataAttrDelete.startsWith(".")) {
1082
+ img.classList.remove(this.getThumbImgDataAttrDelete.slice(1));
1082
1083
  } else {
1083
- if (this.getThumbImgDataAttrDelete.startsWith(".")) {
1084
- img.classList.remove(this.getThumbImgDataAttrDelete.slice(1));
1085
- } else {
1086
- img.removeAttribute(this.getThumbImgDataAttrDelete);
1087
- }
1088
- }
1089
- if (img.src.includes("data:image")) {
1090
- result.img.src = "";
1091
- }
1092
- if (img.complete && img.naturalWidth > 0) {
1093
- return {};
1084
+ img.removeAttribute(this.getThumbImgDataAttrDelete);
1094
1085
  }
1095
1086
  }
1087
+ if (img.src.includes("data:image")) {
1088
+ result.img.src = "";
1089
+ }
1090
+ if (img.complete && img.naturalWidth > 0) {
1091
+ return {};
1092
+ }
1096
1093
  }
1097
- return result;
1098
- }
1099
- containerSelector = ".container";
1100
- containerSelectorLast;
1101
- intersectionObservableSelector;
1102
- get intersectionObservable() {
1103
- return this.intersectionObservableSelector && document.querySelector(this.intersectionObservableSelector);
1104
- }
1105
- get observable() {
1106
- return this.intersectionObservable || this.paginationStrategy.getPaginationElement();
1107
- }
1108
- get container() {
1109
- if (typeof this.containerSelectorLast === "string") {
1110
- return querySelectorLast(document, this.containerSelectorLast);
1111
- }
1112
- if (typeof this.containerSelector === "string") {
1113
- return document.querySelector(this.containerSelector);
1114
- }
1115
- return this.containerSelector();
1116
- }
1117
- thumbsSelector = ".thumb";
1118
- getThumbsStrategy = "default";
1119
- getThumbsTransform;
1120
- getThumbs(html) {
1121
- if (!html) return [];
1122
- let thumbs;
1123
- if (this.getThumbsStrategy === "auto") {
1124
- if (typeof this.containerSelector !== "string") return [];
1125
- const container = html.querySelector(this.containerSelector);
1126
- thumbs = [...container?.children || []];
1127
- }
1128
- thumbs = Array.from(html.querySelectorAll(this.thumbsSelector));
1129
- if (typeof this.getThumbsTransform === "function") {
1130
- thumbs.forEach(this.getThumbsTransform);
1131
- }
1132
- return thumbs;
1133
1094
  }
1134
- paginationStrategyOptions = {};
1135
- paginationStrategy;
1136
- customDataSelectorFns = [
1137
- "filterInclude",
1138
- "filterExclude",
1139
- "filterDuration"
1140
- ];
1141
- animatePreview;
1142
- storeOptions;
1143
- createStore() {
1144
- const config = { ...StoreStateDefault, ...this.storeOptions };
1145
- this.store = new jabroniOutfit.JabronioStore(config);
1146
- return this.store;
1095
+ return result;
1096
+ }
1097
+ containerSelector = ".container";
1098
+ containerSelectorLast;
1099
+ intersectionObservableSelector;
1100
+ get intersectionObservable() {
1101
+ return this.intersectionObservableSelector && document.querySelector(this.intersectionObservableSelector);
1102
+ }
1103
+ get observable() {
1104
+ return this.intersectionObservable || this.paginationStrategy.getPaginationElement();
1105
+ }
1106
+ get container() {
1107
+ if (typeof this.containerSelectorLast === "string") {
1108
+ return querySelectorLast(document, this.containerSelectorLast);
1147
1109
  }
1148
- schemeOptions = [];
1149
- createGui() {
1150
- const scheme = jabroniOutfit.setupScheme(
1151
- this.schemeOptions,
1152
- DefaultScheme
1153
- );
1154
- this.gui = new jabroniOutfit.JabronioGUI(scheme, this.store);
1155
- return this.gui;
1110
+ if (typeof this.containerSelector === "string") {
1111
+ return document.querySelector(this.containerSelector);
1156
1112
  }
1157
- store;
1158
- gui;
1159
- dataManager;
1160
- infiniteScroller;
1161
- getPaginationData;
1162
- resetInfiniteScroller() {
1163
- this.infiniteScroller?.dispose();
1164
- if (!this.paginationStrategy.hasPagination) return;
1165
- this.infiniteScroller = InfiniteScroller.create(this);
1113
+ return this.containerSelector();
1114
+ }
1115
+ thumbsSelector = ".thumb";
1116
+ getThumbsStrategy = "default";
1117
+ getThumbsTransform;
1118
+ getThumbs(html) {
1119
+ if (!html) return [];
1120
+ let thumbs;
1121
+ if (this.getThumbsStrategy === "auto") {
1122
+ if (typeof this.containerSelector !== "string") return [];
1123
+ const container = html.querySelector(this.containerSelector);
1124
+ thumbs = [...container?.children || []];
1125
+ }
1126
+ thumbs = Array.from(html.querySelectorAll(this.thumbsSelector));
1127
+ if (typeof this.getThumbsTransform === "function") {
1128
+ thumbs.forEach(this.getThumbsTransform);
1129
+ }
1130
+ return thumbs;
1131
+ }
1132
+ paginationStrategyOptions = {};
1133
+ paginationStrategy;
1134
+ customDataSelectorFns = [
1135
+ "filterInclude",
1136
+ "filterExclude",
1137
+ "filterDuration"
1138
+ ];
1139
+ animatePreview;
1140
+ storeOptions;
1141
+ createStore() {
1142
+ const config = { ...StoreStateDefault, ...this.storeOptions };
1143
+ this.store = new jabroniOutfit.JabronioStore(config);
1144
+ return this.store;
1145
+ }
1146
+ schemeOptions = [];
1147
+ createGui() {
1148
+ const scheme = jabroniOutfit.setupScheme(
1149
+ this.schemeOptions,
1150
+ DefaultScheme
1151
+ );
1152
+ this.gui = new jabroniOutfit.JabronioGUI(scheme, this.store);
1153
+ return this.gui;
1154
+ }
1155
+ store;
1156
+ gui;
1157
+ dataManager;
1158
+ infiniteScroller;
1159
+ getPaginationData;
1160
+ resetInfiniteScroller() {
1161
+ this.infiniteScroller?.dispose();
1162
+ if (!this.paginationStrategy.hasPagination) return;
1163
+ this.infiniteScroller = InfiniteScroller.create(this);
1164
+ }
1165
+ gropeStrategy = "all-in-one";
1166
+ gropeInit() {
1167
+ if (!this.gropeStrategy) return;
1168
+ if (this.gropeStrategy === "all-in-one") {
1169
+ this.dataManager?.parseData(this.container, this.container);
1170
+ }
1171
+ if (this.gropeStrategy === "all-in-all") {
1172
+ getCommonParents(this.getThumbs(document.body)).forEach((c) => {
1173
+ this.dataManager.parseData(c, c, true);
1174
+ });
1166
1175
  }
1167
- gropeStrategy = "all-in-one";
1168
- gropeInit() {
1169
- if (!this.gropeStrategy) return;
1170
- if (this.gropeStrategy === "all-in-one") {
1171
- this.dataManager?.parseData(this.container, this.container);
1176
+ }
1177
+ get isEmbedded() {
1178
+ return window.self !== window.top;
1179
+ }
1180
+ setupStoreListeners() {
1181
+ const eventsMap = {
1182
+ "sort by duration": {
1183
+ action: (direction2) => this.dataManager.sortBy("duration", direction2)
1172
1184
  }
1173
- if (this.gropeStrategy === "all-in-all") {
1174
- getCommonParents(this.getThumbs(document.body)).forEach((c) => {
1175
- this.dataManager.parseData(c, c, true);
1176
- });
1185
+ };
1186
+ let lastEvent;
1187
+ let direction = true;
1188
+ this.store.eventSubject.subscribe((event) => {
1189
+ if (event === lastEvent) {
1190
+ direction = !direction;
1191
+ } else {
1192
+ lastEvent = event;
1193
+ direction = true;
1177
1194
  }
1178
- }
1179
- get isEmbedded() {
1180
- return window.self !== window.top;
1181
- }
1182
- setupStoreListeners() {
1183
- const eventsMap = {
1184
- "sort by duration": {
1185
- action: (direction2) => this.dataManager.sortBy("duration", direction2)
1186
- }
1187
- };
1188
- let lastEvent;
1189
- let direction = true;
1190
- this.store.eventSubject.subscribe((event) => {
1191
- if (event === lastEvent) {
1192
- direction = !direction;
1193
- } else {
1194
- lastEvent = event;
1195
- direction = true;
1196
- }
1197
- if (event in eventsMap) {
1198
- const ev = eventsMap[event];
1199
- ev?.action(direction);
1200
- }
1201
- });
1202
- this.store.stateSubject.subscribe((a) => {
1203
- this.dataManager.applyFilters(a);
1204
- });
1205
- }
1206
- dataManagerOptions = {};
1207
- setupDataManager() {
1208
- this.dataManager = new DataManager(this);
1209
- if (this.dataManagerOptions) {
1210
- Object.assign(this.dataManager, this.dataManagerOptions);
1195
+ if (event in eventsMap) {
1196
+ const ev = eventsMap[event];
1197
+ ev?.action(direction);
1211
1198
  }
1212
- return this.dataManager;
1213
- }
1214
- mutationObservers = [];
1215
- resetOnPaginationOrContainerDeath = true;
1216
- resetOn() {
1217
- if (!this.resetOnPaginationOrContainerDeath) return;
1218
- const observables = [
1219
- this.container,
1220
- this.intersectionObservable || this.paginationStrategy.getPaginationElement()
1221
- ].filter(Boolean);
1222
- if (observables.length === 0) return;
1223
- observables.forEach((o) => {
1224
- const observer = waitForElementToDisappear(o, () => {
1225
- this.reset();
1226
- });
1227
- this.mutationObservers.push(observer);
1228
- });
1199
+ });
1200
+ this.store.stateSubject.subscribe((a) => {
1201
+ this.dataManager.applyFilters(a);
1202
+ });
1203
+ }
1204
+ dataManagerOptions = {};
1205
+ setupDataManager() {
1206
+ this.dataManager = new DataManager(this);
1207
+ if (this.dataManagerOptions) {
1208
+ Object.assign(this.dataManager, this.dataManagerOptions);
1229
1209
  }
1230
- onResetCallback;
1231
- reset() {
1232
- this.mutationObservers.forEach((o) => {
1233
- o.disconnect();
1210
+ return this.dataManager;
1211
+ }
1212
+ mutationObservers = [];
1213
+ resetOnPaginationOrContainerDeath = true;
1214
+ resetOn() {
1215
+ if (!this.resetOnPaginationOrContainerDeath) return;
1216
+ const observables = [
1217
+ this.container,
1218
+ this.intersectionObservable || this.paginationStrategy.getPaginationElement()
1219
+ ].filter(Boolean);
1220
+ if (observables.length === 0) return;
1221
+ observables.forEach((o) => {
1222
+ const observer = waitForElementToDisappear(o, () => {
1223
+ this.reset();
1234
1224
  });
1235
- this.mutationObservers = [];
1236
- this.paginationStrategy = getPaginationStrategy(this.paginationStrategyOptions);
1237
- this.setupDataManager();
1238
- this.setupStoreListeners();
1239
- this.resetInfiniteScroller();
1240
- this.container && this.animatePreview?.(this.container);
1241
- this.gropeInit();
1242
- this.onResetCallback?.();
1243
- this.resetOn();
1244
- }
1245
- constructor(options) {
1246
- if (this.isEmbedded) throw Error("Embedded is not supported");
1247
- Object.assign(this, options);
1248
- this.paginationStrategy = getPaginationStrategy(this.paginationStrategyOptions);
1249
- this.store = this.createStore();
1250
- this.gui = this.createGui();
1251
- this.dataManager = this.setupDataManager();
1252
- this.reset();
1253
- }
1225
+ this.mutationObservers.push(observer);
1226
+ });
1227
+ }
1228
+ onResetCallback;
1229
+ reset() {
1230
+ this.mutationObservers.forEach((o) => {
1231
+ o.disconnect();
1232
+ });
1233
+ this.mutationObservers = [];
1234
+ this.paginationStrategy = getPaginationStrategy(this.paginationStrategyOptions);
1235
+ this.setupDataManager();
1236
+ this.setupStoreListeners();
1237
+ this.resetInfiniteScroller();
1238
+ this.container && this.animatePreview?.(this.container);
1239
+ this.gropeInit();
1240
+ this.onResetCallback?.();
1241
+ this.resetOn();
1242
+ }
1243
+ constructor(options) {
1244
+ if (this.isEmbedded) throw Error("Embedded is not supported");
1245
+ Object.assign(this, options);
1246
+ this.paginationStrategy = getPaginationStrategy(this.paginationStrategyOptions);
1247
+ this.store = this.createStore();
1248
+ this.gui = this.createGui();
1249
+ this.dataManager = this.setupDataManager();
1250
+ this.reset();
1254
1251
  }
1252
+ }
1255
1253
 
1256
- function onPointerOverAndLeave(container, subjectSelector, onOver, onLeave) {
1257
- let target;
1258
- let onOverFinally;
1259
- function handleLeaveEvent() {
1260
- onLeave?.(target);
1261
- onOverFinally?.();
1262
- target = void 0;
1263
- }
1264
- function handleEvent(e) {
1265
- const currentTarget = e.target;
1266
- if (!subjectSelector(currentTarget) || target === currentTarget) return;
1267
- target = currentTarget;
1268
- const result = onOver(target);
1269
- onOverFinally = result?.onOverCallback;
1270
- const leaveSubject = result?.leaveTarget || target;
1271
- leaveSubject.addEventListener("pointerleave", handleLeaveEvent, {
1272
- once: true
1273
- });
1274
- }
1275
- container.addEventListener("pointerover", handleEvent);
1254
+ function onPointerOverAndLeave(container, subjectSelector, onOver, onLeave) {
1255
+ let target;
1256
+ let onOverFinally;
1257
+ function handleLeaveEvent() {
1258
+ onLeave?.(target);
1259
+ onOverFinally?.();
1260
+ target = void 0;
1276
1261
  }
1262
+ function handleEvent(e) {
1263
+ const currentTarget = e.target;
1264
+ if (!subjectSelector(currentTarget) || target === currentTarget) return;
1265
+ target = currentTarget;
1266
+ const result = onOver(target);
1267
+ onOverFinally = result?.onOverCallback;
1268
+ const leaveSubject = result?.leaveTarget || target;
1269
+ leaveSubject.addEventListener("pointerleave", handleLeaveEvent, {
1270
+ once: true
1271
+ });
1272
+ }
1273
+ container.addEventListener("pointerover", handleEvent);
1274
+ }
1277
1275
 
1278
- class Tick {
1279
- constructor(delay, startImmediate = true) {
1280
- this.delay = delay;
1281
- this.startImmediate = startImmediate;
1282
- }
1283
- tick;
1284
- callbackFinal;
1285
- start(callback, callbackFinal) {
1286
- this.stop();
1287
- this.callbackFinal = callbackFinal;
1288
- if (this.startImmediate) callback();
1289
- this.tick = window.setInterval(callback, this.delay);
1276
+ class Tick {
1277
+ constructor(delay, startImmediate = true) {
1278
+ this.delay = delay;
1279
+ this.startImmediate = startImmediate;
1280
+ }
1281
+ tick;
1282
+ callbackFinal;
1283
+ start(callback, callbackFinal) {
1284
+ this.stop();
1285
+ this.callbackFinal = callbackFinal;
1286
+ if (this.startImmediate) callback();
1287
+ this.tick = window.setInterval(callback, this.delay);
1288
+ }
1289
+ stop() {
1290
+ if (this.tick !== void 0) {
1291
+ clearInterval(this.tick);
1292
+ this.tick = void 0;
1290
1293
  }
1291
- stop() {
1292
- if (this.tick !== void 0) {
1293
- clearInterval(this.tick);
1294
- this.tick = void 0;
1295
- }
1296
- if (this.callbackFinal) {
1297
- this.callbackFinal();
1298
- this.callbackFinal = void 0;
1299
- }
1294
+ if (this.callbackFinal) {
1295
+ this.callbackFinal();
1296
+ this.callbackFinal = void 0;
1300
1297
  }
1301
1298
  }
1299
+ }
1302
1300
 
1303
- function circularShift(n, c = 6, s = 1) {
1304
- return (n + s) % c || c;
1305
- }
1301
+ function circularShift(n, c = 6, s = 1) {
1302
+ return (n + s) % c || c;
1303
+ }
1306
1304
 
1307
- const $ = _unsafeWindow.$;
1308
- _GM_addStyle(`
1305
+ const $ = _unsafeWindow.$;
1306
+ _GM_addStyle(`
1309
1307
  .item.private .thumb, .item .thumb.private { opacity: 1 !important; }
1310
1308
  .haveNoAccess { background: linear-gradient(to bottom, #b50000 0%, #2c2c2c 100%) red !important; }
1311
1309
  .haveAccess { background: linear-gradient(to bottom, #4e9299 0%, #2c2c2c 100%) green !important; }
1312
1310
  .friend-button { background: radial-gradient(#5ccbf4, #e1ccb1) !important; }
1313
1311
  `);
1314
- const IS_MEMBER_PAGE = /^(\/members\/\d+\/|\/my\/)$/.test(location.pathname);
1315
- const IS_MESSAGES = /^\/my\/messages\//.test(location.pathname);
1316
- const IS_COMMUNITY_LIST = /\/members\/$/.test(location.pathname);
1317
- const IS_VIDEO_PAGE = /^(\/videos)?\/\d+\//.test(location.pathname);
1318
- const IS_LOGGED_IN = document.cookie.includes("kt_member");
1319
- const rules = new RulesGlobal({
1320
- containerSelector: '[id*="playlist"]:has(> .item .title),[id*="videos"]:has(> .item .title),form:has(>.item .title)',
1321
- paginationStrategyOptions: {
1322
- paginationSelector: ".pagination:not([id *= member])",
1323
- overwritePaginationLast: IS_MEMBER_PAGE ? () => 1 : (x) => x === 9 ? 9999 : x
1324
- },
1325
- getThumbImgDataAttrSelector: "data-original",
1326
- getThumbImgDataStrategy: "auto",
1327
- thumbsSelector: ".list-videos .item, .playlist .item, .list-playlists > div > .item, .item:has(.title)",
1328
- gropeStrategy: "all-in-all",
1329
- getThumbDataStrategy: "auto-select",
1330
- customThumbDataSelectors: {
1331
- private: { type: "boolean", selector: "[class*=private]" }
1312
+ const IS_MEMBER_PAGE = /^(\/members\/\d+\/|\/my\/)$/.test(location.pathname);
1313
+ const IS_MESSAGES = /^\/my\/messages\//.test(location.pathname);
1314
+ const IS_COMMUNITY_LIST = /\/members\/$/.test(location.pathname);
1315
+ const IS_VIDEO_PAGE = /^(\/videos)?\/\d+\//.test(location.pathname);
1316
+ const IS_LOGGED_IN = document.cookie.includes("kt_member");
1317
+ const rules = new RulesGlobal({
1318
+ containerSelector: '[id*="playlist"]:has(> .item .title),[id*="videos"]:has(> .item .title),form:has(>.item .title)',
1319
+ paginationStrategyOptions: {
1320
+ paginationSelector: ".pagination:not([id *= member])",
1321
+ overwritePaginationLast: IS_MEMBER_PAGE ? () => 1 : (x) => x === 9 ? 9999 : x
1322
+ },
1323
+ getThumbImgDataAttrSelector: "data-original",
1324
+ getThumbImgDataStrategy: "auto",
1325
+ thumbsSelector: ".list-videos .item, .playlist .item, .list-playlists > div > .item, .item:has(.title)",
1326
+ gropeStrategy: "all-in-all",
1327
+ getThumbDataStrategy: "auto-select",
1328
+ customThumbDataSelectors: {
1329
+ private: { type: "boolean", selector: "[class*=private]" }
1330
+ },
1331
+ customDataSelectorFns: [
1332
+ "filterInclude",
1333
+ "filterExclude",
1334
+ "filterDuration",
1335
+ {
1336
+ filterPrivate: (e, state) => state.filterPrivate && e.private
1332
1337
  },
1333
- customDataSelectorFns: [
1334
- "filterInclude",
1335
- "filterExclude",
1336
- "filterDuration",
1337
- {
1338
- filterPrivate: (e, state) => state.filterPrivate && e.private
1339
- },
1340
- {
1341
- filterPublic: (e, state) => state.filterPublic && !e.private
1342
- }
1343
- ],
1344
- schemeOptions: [
1345
- "Text Filter",
1346
- "Duration Filter",
1347
- "Privacy Filter",
1348
- "Badge",
1349
- {
1350
- title: "Advanced",
1351
- content: [
1352
- {
1353
- autoRequestAccess: false,
1354
- label: "auto send friend request on check access"
1355
- }
1356
- ]
1357
- }
1358
- ],
1359
- animatePreview
1360
- });
1361
- function animatePreview(container) {
1362
- const tick = new Tick(500);
1363
- function killjquery(n = 10) {
1364
- if (n > 0) {
1365
- n--;
1366
- $("img[data-cnt]").off();
1367
- setTimeout(() => killjquery(n), 250);
1368
- }
1338
+ {
1339
+ filterPublic: (e, state) => state.filterPublic && !e.private
1340
+ }
1341
+ ],
1342
+ schemeOptions: [
1343
+ "Text Filter",
1344
+ "Duration Filter",
1345
+ "Privacy Filter",
1346
+ "Badge",
1347
+ {
1348
+ title: "Advanced",
1349
+ content: [
1350
+ {
1351
+ autoRequestAccess: false,
1352
+ label: "auto send friend request on check access"
1353
+ }
1354
+ ]
1369
1355
  }
1370
- killjquery();
1371
- function rotateImg(src, count) {
1372
- return src.replace(/(\d)(?=\.jpg$)/, (_, n) => `${circularShift(parseInt(n), count)}`);
1356
+ ],
1357
+ animatePreview
1358
+ });
1359
+ function animatePreview(container) {
1360
+ const tick = new Tick(500);
1361
+ function killjquery(n = 10) {
1362
+ if (n > 0) {
1363
+ n--;
1364
+ $("img[data-cnt]").off();
1365
+ setTimeout(() => killjquery(n), 250);
1373
1366
  }
1374
- onPointerOverAndLeave(
1375
- container,
1376
- (target) => target.tagName === "IMG" && target.classList.contains("thumb") && !!target.getAttribute("src") && !/data:image|avatar/.test(target.getAttribute("src")),
1377
- (_target) => {
1378
- const target = _target;
1379
- const origin = target.src;
1380
- const count = parseInt(target.getAttribute("data-cnt")) || 5;
1381
- tick.start(
1382
- () => {
1383
- target.src = rotateImg(target.src, count);
1384
- },
1385
- () => {
1386
- target.src = origin;
1387
- }
1388
- );
1389
- return {
1390
- leaveTarget: target.closest(".item")
1391
- };
1392
- },
1393
- () => tick.stop()
1394
- );
1395
1367
  }
1396
- const createDownloadButton = () => downloader({
1397
- append: ".tabs-menu > ul",
1398
- after: "",
1399
- button: '<li><a href="#tab_comments" class="toggle-button" style="text-decoration: none;">download 📼</a></li>',
1400
- cbBefore: () => $(".fp-ui").click()
1401
- });
1402
- const DEFAULT_FRIEND_REQUEST_FORMDATA = objectToFormData({
1403
- message: "",
1404
- action: "add_to_friends_complete",
1405
- function: "get_block",
1406
- block_id: "member_profile_view_view_profile",
1407
- format: "json",
1408
- mode: "async"
1368
+ killjquery();
1369
+ function rotateImg(src, count) {
1370
+ return src.replace(/(\d)(?=\.jpg$)/, (_, n) => `${circularShift(parseInt(n), count)}`);
1371
+ }
1372
+ onPointerOverAndLeave(
1373
+ container,
1374
+ (target) => target.tagName === "IMG" && target.classList.contains("thumb") && !!target.getAttribute("src") && !/data:image|avatar/.test(target.getAttribute("src")),
1375
+ (_target) => {
1376
+ const target = _target;
1377
+ const origin = target.src;
1378
+ const count = parseInt(target.getAttribute("data-cnt")) || 5;
1379
+ tick.start(
1380
+ () => {
1381
+ target.src = rotateImg(target.src, count);
1382
+ },
1383
+ () => {
1384
+ target.src = origin;
1385
+ }
1386
+ );
1387
+ return {
1388
+ leaveTarget: target.closest(".item")
1389
+ };
1390
+ },
1391
+ () => tick.stop()
1392
+ );
1393
+ }
1394
+ const createDownloadButton = () => downloader({
1395
+ append: ".tabs-menu > ul",
1396
+ after: "",
1397
+ button: '<li><a href="#tab_comments" class="toggle-button" style="text-decoration: none;">download 📼</a></li>',
1398
+ cbBefore: () => $(".fp-ui").click()
1399
+ });
1400
+ const DEFAULT_FRIEND_REQUEST_FORMDATA = objectToFormData({
1401
+ message: "",
1402
+ action: "add_to_friends_complete",
1403
+ function: "get_block",
1404
+ block_id: "member_profile_view_view_profile",
1405
+ format: "json",
1406
+ mode: "async"
1407
+ });
1408
+ const lskdb = new LSKDB();
1409
+ async function friendRequest(id) {
1410
+ const url = Number.isInteger(id) ? `${location.origin}/members/${id}/` : id;
1411
+ await fetch(url, { body: DEFAULT_FRIEND_REQUEST_FORMDATA, method: "post" });
1412
+ }
1413
+ function getMemberLinks(e) {
1414
+ return Array.from(
1415
+ e?.querySelectorAll(".item > a") || [],
1416
+ (l) => l.href
1417
+ ).filter((l) => /\/members\/\d+\/$/.test(l));
1418
+ }
1419
+ async function getMemberFriends(id) {
1420
+ const url = new URL(
1421
+ IS_COMMUNITY_LIST ? "/members/" : `/members/${id}/friends/`,
1422
+ location.origin
1423
+ );
1424
+ const doc = await fetchHtml(url.href);
1425
+ const paginationStrategy = getPaginationStrategy({
1426
+ doc,
1427
+ url,
1428
+ overwritePaginationLast: (x) => x === 9 ? 999 : x
1409
1429
  });
1410
- const lskdb = new LSKDB();
1411
- async function friendRequest(id) {
1412
- const url = Number.isInteger(id) ? `${location.origin}/members/${id}/` : id;
1413
- await fetch(url, { body: DEFAULT_FRIEND_REQUEST_FORMDATA, method: "post" });
1414
- }
1415
- function getMemberLinks(e) {
1416
- return Array.from(
1417
- e?.querySelectorAll(".item > a") || [],
1418
- (l) => l.href
1419
- ).filter((l) => /\/members\/\d+\/$/.test(l));
1420
- }
1421
- async function getMemberFriends(id) {
1422
- const url = new URL(
1423
- IS_COMMUNITY_LIST ? "/members/" : `/members/${id}/friends/`,
1424
- location.origin
1425
- );
1426
- const doc = await fetchHtml(url.href);
1427
- const paginationStrategy = getPaginationStrategy({
1428
- doc,
1429
- url,
1430
- overwritePaginationLast: (x) => x === 9 ? 999 : x
1430
+ const gen = InfiniteScroller.generatorForPaginationStrategy(paginationStrategy);
1431
+ for (const url2 in gen) {
1432
+ const doc2 = await fetchHtml(url2);
1433
+ getMemberLinks(doc2).forEach((a) => {
1434
+ const id2 = a.match(/\d+/)?.[0];
1435
+ lskdb.setKey(id2);
1431
1436
  });
1432
- const gen = InfiniteScroller.generatorForPaginationStrategy(paginationStrategy);
1433
- for (const url2 in gen) {
1434
- const doc2 = await fetchHtml(url2);
1435
- getMemberLinks(doc2).forEach((a) => {
1436
- const id2 = a.match(/\d+/)?.[0];
1437
- lskdb.setKey(id2);
1437
+ }
1438
+ await processFriendship();
1439
+ }
1440
+ let processFriendshipStarted = false;
1441
+ async function processFriendship(batchSize = 1, interval = 5e3) {
1442
+ if (lskdb.isLocked()) return;
1443
+ const friendlist = lskdb.getKeys(batchSize);
1444
+ if (friendlist?.length < 1) return;
1445
+ if (!processFriendshipStarted) {
1446
+ processFriendshipStarted = true;
1447
+ console.log("processFriendshipStarted");
1448
+ }
1449
+ lskdb.lock(true);
1450
+ const urls = friendlist.map((id) => `${location.origin}/members/${id}/`);
1451
+ for (const url of urls) {
1452
+ await wait(interval);
1453
+ await friendRequest(url);
1454
+ }
1455
+ lskdb.lock(false);
1456
+ await processFriendship();
1457
+ }
1458
+ function createPrivateVideoFriendButton() {
1459
+ if (!document.querySelector(".no-player")) return;
1460
+ const member = document.querySelector(".no-player a")?.href;
1461
+ const button = parseHtml(
1462
+ '<button class="friend-button"><span>Friend Request</span></button>'
1463
+ );
1464
+ document.querySelector(".no-player .message")?.append(button);
1465
+ button.addEventListener("click", () => friendRequest(member), { once: true });
1466
+ }
1467
+ function createFriendButton() {
1468
+ const button = parseHtml(
1469
+ '<a href="#friend_everyone" class="button friend-button"><span>Friend Everyone</span></a>'
1470
+ );
1471
+ document.querySelector(".main-container-user > .headline, .headline")?.append(button);
1472
+ const memberid = location.pathname.match(/\d+/)?.[0];
1473
+ button.addEventListener(
1474
+ "click",
1475
+ () => {
1476
+ button.style.background = "radial-gradient(#ff6114, #5babc4)";
1477
+ button.innerText = "processing requests";
1478
+ getMemberFriends(memberid).then(() => {
1479
+ button.style.background = "radial-gradient(blue, lightgreen)";
1480
+ button.innerText = "friend requests sent";
1438
1481
  });
1482
+ },
1483
+ { once: true }
1484
+ );
1485
+ }
1486
+ async function requestAccess() {
1487
+ checkPrivateVidsAccess();
1488
+ setTimeout(processFriendship, 5e3);
1489
+ }
1490
+ async function checkPrivateVidsAccess() {
1491
+ const checkAccess = async (item) => {
1492
+ const videoURL = item.firstElementChild.href;
1493
+ const doc = await fetchHtml(videoURL);
1494
+ if (!doc.querySelector(".player")) return;
1495
+ const haveAccess = !doc.querySelector(".no-player");
1496
+ if (!haveAccess && rules.store.state.autoRequestAccess) {
1497
+ const anchor = doc.querySelector(".message a");
1498
+ const uid = anchor?.href.match(/\d+/)?.at(-1);
1499
+ lskdb.setKey(uid);
1500
+ }
1501
+ item.classList.add(haveAccess ? "haveAccess" : "haveNoAccess");
1502
+ };
1503
+ const thumbs = document.querySelectorAll(
1504
+ ".item.private:not(.haveAccess,.haveNoAccess)"
1505
+ );
1506
+ for (const thumb of thumbs) {
1507
+ await checkAccess(thumb);
1508
+ }
1509
+ }
1510
+ function getUserInfo(e) {
1511
+ const uploadedCount = querySelectorLastNumber("#list_videos_uploaded_videos strong", e);
1512
+ const friendsCount = querySelectorLastNumber("#list_members_friends .headline", e);
1513
+ return { uploadedCount, friendsCount };
1514
+ }
1515
+ async function acceptFriendRequest(id) {
1516
+ const url = new URL(`/my/messages/${id}/`, location.origin);
1517
+ const memberUrl = new URL(`/members/${id}/`, location.origin);
1518
+ await fetch(url, {
1519
+ headers: {
1520
+ Accept: "*/*",
1521
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
1522
+ },
1523
+ body: `action=confirm_add_to_friends&message_from_user_id=${id}&function=get_block&block_id=list_messages_my_conversation_messages&confirm=Confirm&format=json&mode=async`,
1524
+ method: "POST"
1525
+ });
1526
+ await fetchHtml(memberUrl).then(
1527
+ (doc) => console.log("userInfo", memberUrl.href, getUserInfo(doc))
1528
+ );
1529
+ }
1530
+ async function clearMessages() {
1531
+ const pages = InfiniteScroller.generatorForPaginationStrategy(
1532
+ getPaginationStrategy({
1533
+ overwritePaginationLast: (x) => x === 9 ? 999 : x
1534
+ })
1535
+ );
1536
+ for await (const p of pages) {
1537
+ const doc = await fetchHtml(p.url);
1538
+ const messages = Array.from(
1539
+ doc.querySelectorAll(
1540
+ "#list_members_my_conversations_items .item > a"
1541
+ ) || []
1542
+ ).map((a) => a.href);
1543
+ for (const m of messages) {
1544
+ await checkMessageHistory(m);
1439
1545
  }
1440
- await processFriendship();
1441
- }
1442
- let processFriendshipStarted = false;
1443
- async function processFriendship(batchSize = 1, interval = 5e3) {
1444
- if (lskdb.isLocked()) return;
1445
- const friendlist = lskdb.getKeys(batchSize);
1446
- if (friendlist?.length < 1) return;
1447
- if (!processFriendshipStarted) {
1448
- processFriendshipStarted = true;
1449
- console.log("processFriendshipStarted");
1450
- }
1451
- lskdb.lock(true);
1452
- const urls = friendlist.map((id) => `${location.origin}/members/${id}/`);
1453
- for (const url of urls) {
1454
- await wait(interval);
1455
- await friendRequest(url);
1456
- }
1457
- lskdb.lock(false);
1458
- await processFriendship();
1459
- }
1460
- function createPrivateVideoFriendButton() {
1461
- if (!document.querySelector(".no-player")) return;
1462
- const member = document.querySelector(".no-player a")?.href;
1463
- const button = parseHtml(
1464
- '<button class="friend-button"><span>Friend Request</span></button>'
1465
- );
1466
- document.querySelector(".no-player .message")?.append(button);
1467
- button.addEventListener("click", () => friendRequest(member), { once: true });
1468
1546
  }
1469
- function createFriendButton() {
1470
- const button = parseHtml(
1471
- '<a href="#friend_everyone" class="button friend-button"><span>Friend Everyone</span></a>'
1472
- );
1473
- document.querySelector(".main-container-user > .headline, .headline")?.append(button);
1474
- const memberid = location.pathname.match(/\d+/)?.[0];
1475
- button.addEventListener(
1476
- "click",
1477
- () => {
1478
- button.style.background = "radial-gradient(#ff6114, #5babc4)";
1479
- button.innerText = "processing requests";
1480
- getMemberFriends(memberid).then(() => {
1481
- button.style.background = "radial-gradient(blue, lightgreen)";
1482
- button.innerText = "friend requests sent";
1483
- });
1484
- },
1485
- { once: true }
1486
- );
1547
+ async function deleteMessage(url, id) {
1548
+ const deleteURL = `${url}?mode=async&format=json&function=get_block&block_id=list_messages_my_conversation_messages&action=delete_conversation&conversation_user_id=${id}`;
1549
+ await fetch(deleteURL);
1487
1550
  }
1488
- async function requestAccess() {
1489
- checkPrivateVidsAccess();
1490
- setTimeout(processFriendship, 5e3);
1491
- }
1492
- async function checkPrivateVidsAccess() {
1493
- const checkAccess = async (item) => {
1494
- const videoURL = item.firstElementChild.href;
1495
- const doc = await fetchHtml(videoURL);
1496
- if (!doc.querySelector(".player")) return;
1497
- const haveAccess = !doc.querySelector(".no-player");
1498
- if (!haveAccess && rules.store.state.autoRequestAccess) {
1499
- const anchor = doc.querySelector(".message a");
1500
- const uid = anchor?.href.match(/\d+/)?.at(-1);
1501
- lskdb.setKey(uid);
1502
- }
1503
- item.classList.add(haveAccess ? "haveAccess" : "haveNoAccess");
1551
+ async function getConversation(url) {
1552
+ const doc = await fetchHtml(url);
1553
+ const hasFriendRequest = !!doc.querySelector("input[value=confirm_add_to_friends]");
1554
+ const originalText = querySelectorText(doc, ".original-text");
1555
+ const id = url.match(/\d+/)?.[0];
1556
+ const messages = querySelectorText(doc, ".list-messages");
1557
+ return {
1558
+ id,
1559
+ hasFriendRequest,
1560
+ originalText,
1561
+ messages
1504
1562
  };
1505
- const thumbs = document.querySelectorAll(
1506
- ".item.private:not(.haveAccess,.haveNoAccess)"
1507
- );
1508
- for (const thumb of thumbs) {
1509
- await checkAccess(thumb);
1510
- }
1511
- }
1512
- function getUserInfo(e) {
1513
- const uploadedCount = querySelectorLastNumber("#list_videos_uploaded_videos strong", e);
1514
- const friendsCount = querySelectorLastNumber("#list_members_friends .headline", e);
1515
- return { uploadedCount, friendsCount };
1516
- }
1517
- async function acceptFriendRequest(id) {
1518
- const url = new URL(`/my/messages/${id}/`, location.origin);
1519
- const memberUrl = new URL(`/members/${id}/`, location.origin);
1520
- await fetch(url, {
1521
- headers: {
1522
- Accept: "*/*",
1523
- "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
1524
- },
1525
- body: `action=confirm_add_to_friends&message_from_user_id=${id}&function=get_block&block_id=list_messages_my_conversation_messages&confirm=Confirm&format=json&mode=async`,
1526
- method: "POST"
1527
- });
1528
- await fetchHtml(memberUrl).then(
1529
- (doc) => console.log("userInfo", memberUrl.href, getUserInfo(doc))
1530
- );
1531
1563
  }
1532
- async function clearMessages() {
1533
- const pages = InfiniteScroller.generatorForPaginationStrategy(
1534
- getPaginationStrategy({
1535
- overwritePaginationLast: (x) => x === 9 ? 999 : x
1536
- })
1537
- );
1538
- for await (const p of pages) {
1539
- const doc = await fetchHtml(p.url);
1540
- const messages = Array.from(
1541
- doc.querySelectorAll(
1542
- "#list_members_my_conversations_items .item > a"
1543
- ) || []
1544
- ).map((a) => a.href);
1545
- for (const m of messages) {
1546
- await checkMessageHistory(m);
1564
+ async function checkMessageHistory(url) {
1565
+ const { originalText, hasFriendRequest, id, messages } = await getConversation(url);
1566
+ if (!(originalText || hasFriendRequest)) {
1567
+ await deleteMessage(url, id);
1568
+ } else {
1569
+ console.log({ originalText, url, messages });
1570
+ if (hasFriendRequest) {
1571
+ await acceptFriendRequest(id);
1547
1572
  }
1548
1573
  }
1549
- async function deleteMessage(url, id) {
1550
- const deleteURL = `${url}?mode=async&format=json&function=get_block&block_id=list_messages_my_conversation_messages&action=delete_conversation&conversation_user_id=${id}`;
1551
- await fetch(deleteURL);
1552
- }
1553
- async function getConversation(url) {
1554
- const doc = await fetchHtml(url);
1555
- const hasFriendRequest = !!doc.querySelector("input[value=confirm_add_to_friends]");
1556
- const originalText = querySelectorText(doc, ".original-text");
1557
- const id = url.match(/\d+/)?.[0];
1558
- const messages = querySelectorText(doc, ".list-messages");
1559
- return {
1560
- id,
1561
- hasFriendRequest,
1562
- originalText,
1563
- messages
1564
- };
1565
- }
1566
- async function checkMessageHistory(url) {
1567
- const { originalText, hasFriendRequest, id, messages } = await getConversation(url);
1568
- if (!(originalText || hasFriendRequest)) {
1569
- await deleteMessage(url, id);
1570
- } else {
1571
- console.log({ originalText, url, messages });
1572
- if (hasFriendRequest) {
1573
- await acceptFriendRequest(id);
1574
- }
1575
- }
1576
- }
1577
- }
1578
- const FRIEND_REQUEST_INTERVAL = 5e3;
1579
- if (IS_LOGGED_IN) {
1580
- setTimeout(processFriendship, FRIEND_REQUEST_INTERVAL);
1581
- if (IS_MEMBER_PAGE || IS_COMMUNITY_LIST) {
1582
- createFriendButton();
1583
- }
1584
1574
  }
1585
- if (IS_VIDEO_PAGE) {
1586
- createDownloadButton();
1587
- createPrivateVideoFriendButton();
1575
+ }
1576
+ const FRIEND_REQUEST_INTERVAL = 5e3;
1577
+ if (IS_LOGGED_IN) {
1578
+ setTimeout(processFriendship, FRIEND_REQUEST_INTERVAL);
1579
+ if (IS_MEMBER_PAGE || IS_COMMUNITY_LIST) {
1580
+ createFriendButton();
1588
1581
  }
1589
- if (IS_MESSAGES) {
1590
- const button = parseHtml("<button>clear messages</button>");
1591
- document.querySelector(".headline")?.append(button);
1592
- button.addEventListener("click", clearMessages);
1582
+ }
1583
+ if (IS_VIDEO_PAGE) {
1584
+ createDownloadButton();
1585
+ createPrivateVideoFriendButton();
1586
+ }
1587
+ if (IS_MESSAGES) {
1588
+ const button = parseHtml("<button>clear messages</button>");
1589
+ document.querySelector(".headline")?.append(button);
1590
+ button.addEventListener("click", clearMessages);
1591
+ }
1592
+ rules.store.eventSubject.subscribe((event) => {
1593
+ if (event.includes("check access")) {
1594
+ requestAccess();
1593
1595
  }
1594
- rules.store.eventSubject.subscribe((event) => {
1595
- if (event.includes("check access")) {
1596
- requestAccess();
1597
- }
1598
- });
1599
-
1600
- })(jabronioutfit);
1596
+ });
1601
1597
 
1602
- })();
1598
+ })(jabronioutfit);