billy-herrington-utils 1.4.3 → 1.4.5
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.
- package/dist/billy-herrington-utils.es.js +305 -418
- package/dist/billy-herrington-utils.es.js.map +1 -1
- package/dist/billy-herrington-utils.umd.js +305 -418
- package/dist/billy-herrington-utils.umd.js.map +1 -1
- package/dist/index.d.ts +33 -72
- package/package.json +14 -4
- package/src/index.ts +14 -16
- package/src/types/globals.d.ts +2 -0
- package/src/{data-manager → userscripts/data-manager}/index.ts +34 -30
- package/src/{utils → userscripts}/infinite-scroll/index.ts +6 -7
- package/src/{utils → userscripts}/jabroni-outfit-wrap/index.ts +11 -2
- package/src/userscripts/pagination-parsing/index.ts +51 -0
- package/src/userscripts/pagination-parsing/pagination-strategies/PaginationStrategy.ts +33 -0
- package/src/userscripts/pagination-parsing/pagination-strategies/PaginationStrategyDataParams.ts +57 -0
- package/src/userscripts/pagination-parsing/pagination-strategies/PaginationStrategyPathnameParams.ts +57 -0
- package/src/userscripts/pagination-parsing/pagination-strategies/PaginationStrategySearchParams.ts +47 -0
- package/src/userscripts/pagination-parsing/pagination-strategies/PaginationStrategyTrash.ts +36 -0
- package/src/userscripts/pagination-parsing/pagination-strategies/index.ts +5 -0
- package/src/userscripts/pagination-parsing/pagination-utils/index.ts +27 -0
- package/src/utils/userscript-utils/rules.ts +0 -272
|
@@ -1,46 +1,6 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
function stringToWords(s) {
|
|
5
|
-
return s.split(",").map((s2) => s2.trim().toLowerCase()).filter((_) => _);
|
|
6
|
-
}
|
|
7
|
-
function sanitizeStr(s) {
|
|
8
|
-
return s?.replace(/\n|\t/, " ").replace(/ {2,}/, " ").trim().toLowerCase() || "";
|
|
9
|
-
}
|
|
10
|
-
function formatTimeToHHMMSS(timeString) {
|
|
11
|
-
const regex = /(?:(\d+)\s*h\s*)?(?:(\d+)\s*mi?n?\s*)?(?:(\d+)\s*sec)?/;
|
|
12
|
-
const match = timeString.match(regex);
|
|
13
|
-
const h = parseInt(match?.[1] || "0");
|
|
14
|
-
const m = parseInt(match?.[2] || "0");
|
|
15
|
-
const s = parseInt(match?.[3] || "0");
|
|
16
|
-
const pad = (num) => String(num).padStart(2, "0");
|
|
17
|
-
return `${pad(h)}:${pad(m)}:${pad(s)}`;
|
|
18
|
-
}
|
|
19
|
-
function timeToSeconds(t) {
|
|
20
|
-
const r = /sec|min|h|m/.test(t) ? formatTimeToHHMMSS(t) : t;
|
|
21
|
-
return (r?.match(/\d+/gm) || [0]).reverse().map((s, i) => parseInt(s) * 60 ** i).reduce((a, b) => a + b);
|
|
22
|
-
}
|
|
23
|
-
function parseIntegerOr(n, or) {
|
|
24
|
-
return ((num) => Number.isNaN(num) ? or : num)(parseInt(n));
|
|
25
|
-
}
|
|
26
|
-
function parseDataParams(str) {
|
|
27
|
-
const paramsStr = decodeURI(str.trim()).split(";");
|
|
28
|
-
return paramsStr.reduce((acc, s) => {
|
|
29
|
-
const parsed = s.match(/([\+\w]+):([\w\-\ ]+)?/);
|
|
30
|
-
if (parsed) {
|
|
31
|
-
const [, key, value] = parsed;
|
|
32
|
-
if (value) {
|
|
33
|
-
key.split("+").forEach((p) => {
|
|
34
|
-
acc[p] = value;
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return acc;
|
|
39
|
-
}, {});
|
|
40
|
-
}
|
|
41
|
-
function parseCSSUrl(s) {
|
|
42
|
-
return s.replace(/url\("|\"\).*/g, "");
|
|
43
|
-
}
|
|
44
4
|
class Observer {
|
|
45
5
|
constructor(callback) {
|
|
46
6
|
__publicField(this, "observer");
|
|
@@ -92,8 +52,173 @@ class LazyImgLoader {
|
|
|
92
52
|
this.lazyImgObserver.observe(img);
|
|
93
53
|
}
|
|
94
54
|
}
|
|
95
|
-
function
|
|
96
|
-
return (
|
|
55
|
+
function stringToWords(s) {
|
|
56
|
+
return s.split(",").map((s2) => s2.trim().toLowerCase()).filter((_) => _);
|
|
57
|
+
}
|
|
58
|
+
function sanitizeStr(s) {
|
|
59
|
+
return s?.replace(/\n|\t/, " ").replace(/ {2,}/, " ").trim().toLowerCase() || "";
|
|
60
|
+
}
|
|
61
|
+
class DataFilter {
|
|
62
|
+
constructor(rules, state) {
|
|
63
|
+
__publicField(this, "filters");
|
|
64
|
+
__publicField(this, "filterPublic", () => {
|
|
65
|
+
return (v) => {
|
|
66
|
+
const isPublic = !this.rules.isPrivate(v.element);
|
|
67
|
+
return {
|
|
68
|
+
tag: "filter-public",
|
|
69
|
+
condition: this.state.filterPublic && isPublic
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
__publicField(this, "filterPrivate", () => {
|
|
74
|
+
return (v) => {
|
|
75
|
+
const isPrivate = this.rules.isPrivate(v.element);
|
|
76
|
+
return {
|
|
77
|
+
tag: "filter-private",
|
|
78
|
+
condition: this.state.filterPrivate && isPrivate
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
__publicField(this, "filterHD", () => {
|
|
83
|
+
return (v) => {
|
|
84
|
+
const isHD = this.rules.isHD(v.element);
|
|
85
|
+
return {
|
|
86
|
+
tag: "filter-hd",
|
|
87
|
+
condition: this.state.filterHD && isHD
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
__publicField(this, "filterDuration", () => {
|
|
92
|
+
return (v) => {
|
|
93
|
+
const notInRange = v.duration < this.state.filterDurationFrom || v.duration > this.state.filterDurationTo;
|
|
94
|
+
return {
|
|
95
|
+
tag: "filter-duration",
|
|
96
|
+
condition: this.state.filterDuration && notInRange
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
__publicField(this, "filterExclude", () => {
|
|
101
|
+
const tags = DataManager.filterDSLToRegex(this.state.filterExcludeWords);
|
|
102
|
+
return (v) => {
|
|
103
|
+
const containTags = tags.some((tag) => tag.test(v.title));
|
|
104
|
+
return {
|
|
105
|
+
tag: "filter-exclude",
|
|
106
|
+
condition: this.state.filterExclude && containTags
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
__publicField(this, "filterInclude", () => {
|
|
111
|
+
const tags = DataManager.filterDSLToRegex(this.state.filterIncludeWords);
|
|
112
|
+
return (v) => {
|
|
113
|
+
const containTagsNot = tags.some((tag) => !tag.test(v.title));
|
|
114
|
+
return {
|
|
115
|
+
tag: "filter-include",
|
|
116
|
+
condition: this.state.filterInclude && containTagsNot
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
this.rules = rules;
|
|
121
|
+
this.state = state;
|
|
122
|
+
this.state = state;
|
|
123
|
+
const methods = Object.getOwnPropertyNames(this);
|
|
124
|
+
this.filters = methods.reduce((acc, k) => {
|
|
125
|
+
if (k in this.state) {
|
|
126
|
+
acc[k] = this[k];
|
|
127
|
+
GM_addStyle(`.filter-${k.toLowerCase().slice(6)} { display: none !important; }`);
|
|
128
|
+
}
|
|
129
|
+
return acc;
|
|
130
|
+
}, {});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
class DataManager {
|
|
134
|
+
constructor(rules, state) {
|
|
135
|
+
__publicField(this, "rules");
|
|
136
|
+
__publicField(this, "state");
|
|
137
|
+
__publicField(this, "data");
|
|
138
|
+
__publicField(this, "lazyImgLoader");
|
|
139
|
+
__publicField(this, "dataFilters");
|
|
140
|
+
__publicField(this, "applyFilters", (filters, offset = 0) => {
|
|
141
|
+
const filtersToApply = Object.keys(filters).filter((k) => Object.hasOwn(this.dataFilters, k)).map((k) => this.dataFilters[k]());
|
|
142
|
+
if (filtersToApply.length === 0) return;
|
|
143
|
+
const updates = [];
|
|
144
|
+
let offset_counter = 1;
|
|
145
|
+
for (const v of this.data.values()) {
|
|
146
|
+
if (++offset_counter > offset) {
|
|
147
|
+
for (const f of filtersToApply) {
|
|
148
|
+
const { tag, condition } = f(v);
|
|
149
|
+
updates.push(() => v.element.classList.toggle(tag, condition));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
requestAnimationFrame(() => {
|
|
154
|
+
updates.forEach((update) => {
|
|
155
|
+
update();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
__publicField(this, "filterAll", (offset) => {
|
|
160
|
+
const filters = Object.assign(
|
|
161
|
+
{},
|
|
162
|
+
...Object.keys(this.dataFilters).map((f) => ({
|
|
163
|
+
[f]: this.state[f]
|
|
164
|
+
}))
|
|
165
|
+
);
|
|
166
|
+
this.applyFilters(filters, offset);
|
|
167
|
+
});
|
|
168
|
+
__publicField(this, "parseData", (html, container, removeDuplicates = false, shouldLazify = true) => {
|
|
169
|
+
const thumbs = this.rules.getThumbs(html);
|
|
170
|
+
const data_offset = this.data.size;
|
|
171
|
+
for (const thumbElement of thumbs) {
|
|
172
|
+
const url = this.rules.getThumbUrl(thumbElement);
|
|
173
|
+
if (!url || this.data.has(url)) {
|
|
174
|
+
if (removeDuplicates) thumbElement.remove();
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const data = this.rules.getThumbData(thumbElement);
|
|
178
|
+
this.data.set(url, { element: thumbElement, ...data });
|
|
179
|
+
if (shouldLazify) {
|
|
180
|
+
const { img, imgSrc } = this.rules.getThumbImgData(thumbElement);
|
|
181
|
+
this.lazyImgLoader.lazify(thumbElement, img, imgSrc);
|
|
182
|
+
}
|
|
183
|
+
const parent = container || this.rules.container;
|
|
184
|
+
if (!parent.contains(thumbElement)) parent.appendChild(thumbElement);
|
|
185
|
+
}
|
|
186
|
+
this.filterAll(data_offset);
|
|
187
|
+
});
|
|
188
|
+
this.rules = rules;
|
|
189
|
+
this.state = state;
|
|
190
|
+
this.data = /* @__PURE__ */ new Map();
|
|
191
|
+
this.lazyImgLoader = new LazyImgLoader(
|
|
192
|
+
(target) => !this.isFiltered(target)
|
|
193
|
+
);
|
|
194
|
+
this.dataFilters = new DataFilter(rules, state).filters;
|
|
195
|
+
const targets = [window, globalThis.unsafeWindow].filter(Boolean);
|
|
196
|
+
targets.forEach((w) => {
|
|
197
|
+
Object.assign(w, {
|
|
198
|
+
sortByViews: () => this.sort("view"),
|
|
199
|
+
sortByDuration: () => this.sort("duration")
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
static filterDSLToRegex(str) {
|
|
204
|
+
const toFullWord = (w) => `(^|\\ )${w}($|\\ )`;
|
|
205
|
+
const str_ = str.replace(/f:(\w+)/g, (_, w) => toFullWord(w));
|
|
206
|
+
return stringToWords(str_).map((expr) => new RegExp(expr, "i"));
|
|
207
|
+
}
|
|
208
|
+
isFiltered(el) {
|
|
209
|
+
return el.className.includes("filtered");
|
|
210
|
+
}
|
|
211
|
+
sort(propName) {
|
|
212
|
+
if (this.data.size < 2) return;
|
|
213
|
+
const sorted = Array.from(this.data.keys()).sort((b, a) => {
|
|
214
|
+
return this.data.get(a)[propName] - this.data.get(b)[propName];
|
|
215
|
+
});
|
|
216
|
+
const container = this.data.get(sorted[0]).element.parentElement;
|
|
217
|
+
sorted.forEach((s) => {
|
|
218
|
+
const e = this.data.get(s).element;
|
|
219
|
+
container.append(e);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
97
222
|
}
|
|
98
223
|
function parseDom(html) {
|
|
99
224
|
const parsed = new DOMParser().parseFromString(html, "text/html").body;
|
|
@@ -203,37 +328,94 @@ function objectToFormData(object) {
|
|
|
203
328
|
Object.entries(object).forEach(([k, v]) => formData.append(k, v));
|
|
204
329
|
return formData;
|
|
205
330
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
331
|
+
class InfiniteScroller {
|
|
332
|
+
constructor({
|
|
333
|
+
enabled = true,
|
|
334
|
+
delay = 350,
|
|
335
|
+
writeHistory = false,
|
|
336
|
+
paginationOffset,
|
|
337
|
+
paginationLast,
|
|
338
|
+
paginationElement,
|
|
339
|
+
paginationUrlGenerator,
|
|
340
|
+
handleHtmlCallback,
|
|
341
|
+
alternativeGenerator,
|
|
342
|
+
intersectionObservable
|
|
343
|
+
}) {
|
|
344
|
+
__publicField(this, "paginationGenerator");
|
|
345
|
+
__publicField(this, "enabled");
|
|
346
|
+
__publicField(this, "delay");
|
|
347
|
+
__publicField(this, "paginationOffset");
|
|
348
|
+
__publicField(this, "paginationLast");
|
|
349
|
+
__publicField(this, "writeHistory");
|
|
350
|
+
__publicField(this, "handleHtmlCallback");
|
|
351
|
+
__publicField(this, "onScrollCBs", []);
|
|
352
|
+
__publicField(this, "generatorConsumer", async () => {
|
|
353
|
+
if (!this.enabled) return false;
|
|
354
|
+
const { value: { url, offset } = {}, done } = await this.paginationGenerator.next();
|
|
355
|
+
if (!done) {
|
|
356
|
+
const nextPageHTML = await fetchHtml(url);
|
|
357
|
+
const prevScrollPos = document.documentElement.scrollTop;
|
|
358
|
+
this.paginationOffset = offset;
|
|
359
|
+
this.handleHtmlCallback(nextPageHTML);
|
|
360
|
+
this._onScroll();
|
|
361
|
+
window.scrollTo(0, prevScrollPos);
|
|
362
|
+
if (this.writeHistory) {
|
|
363
|
+
history.replaceState({}, "", url);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return !done;
|
|
367
|
+
});
|
|
368
|
+
this.enabled = enabled;
|
|
215
369
|
this.delay = delay;
|
|
216
|
-
this.
|
|
370
|
+
this.writeHistory = writeHistory;
|
|
371
|
+
this.paginationOffset = paginationOffset;
|
|
372
|
+
this.paginationLast = paginationLast;
|
|
373
|
+
this.handleHtmlCallback = handleHtmlCallback;
|
|
374
|
+
this.paginationGenerator = alternativeGenerator?.() ?? InfiniteScroller.createPaginationGenerator(
|
|
375
|
+
paginationOffset,
|
|
376
|
+
paginationLast,
|
|
377
|
+
paginationUrlGenerator
|
|
378
|
+
);
|
|
379
|
+
const observable = intersectionObservable || paginationElement;
|
|
380
|
+
Observer.observeWhile(observable, this.generatorConsumer, this.delay);
|
|
217
381
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
this.
|
|
221
|
-
|
|
222
|
-
this.tick = window.setInterval(callback, this.delay);
|
|
382
|
+
onScroll(callback, initCall = false) {
|
|
383
|
+
if (initCall) callback(this);
|
|
384
|
+
this.onScrollCBs.push(callback);
|
|
385
|
+
return this;
|
|
223
386
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
387
|
+
_onScroll() {
|
|
388
|
+
this.onScrollCBs.forEach((cb) => {
|
|
389
|
+
cb(this);
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
static *createPaginationGenerator(currentPage, totalPages, generateURL) {
|
|
393
|
+
for (let offset = currentPage + 1; offset <= totalPages; offset++) {
|
|
394
|
+
const url = generateURL(offset);
|
|
395
|
+
yield { url, offset };
|
|
232
396
|
}
|
|
233
397
|
}
|
|
234
398
|
}
|
|
235
|
-
function
|
|
236
|
-
|
|
399
|
+
function createInfiniteScroller(store, handleHtmlCallback, rules) {
|
|
400
|
+
const enabled = store.state.infiniteScrollEnabled;
|
|
401
|
+
const iscroller = new InfiniteScroller({
|
|
402
|
+
enabled,
|
|
403
|
+
handleHtmlCallback,
|
|
404
|
+
...rules
|
|
405
|
+
}).onScroll(({ paginationLast, paginationOffset }) => {
|
|
406
|
+
store.localState.pagIndexLast = paginationLast;
|
|
407
|
+
store.localState.pagIndexCur = paginationOffset;
|
|
408
|
+
}, true);
|
|
409
|
+
store.subscribe(() => {
|
|
410
|
+
iscroller.enabled = store.state.infiniteScrollEnabled;
|
|
411
|
+
});
|
|
412
|
+
return iscroller;
|
|
413
|
+
}
|
|
414
|
+
function chunks(arr, n) {
|
|
415
|
+
return Array.from({ length: Math.ceil(arr.length / n) }, (_, i) => arr.slice(i * n, i * n + n));
|
|
416
|
+
}
|
|
417
|
+
function range(size, startAt = 1, step = 1) {
|
|
418
|
+
return Array.from({ length: size }, (_, index) => startAt + index * step);
|
|
237
419
|
}
|
|
238
420
|
async function computeAsyncOneAtTime(iterable) {
|
|
239
421
|
const res = [];
|
|
@@ -296,368 +478,74 @@ class AsyncPool {
|
|
|
296
478
|
this.pool.push("p" in x ? x : { v: x, p: 0 });
|
|
297
479
|
}
|
|
298
480
|
}
|
|
299
|
-
function
|
|
300
|
-
return
|
|
301
|
-
}
|
|
302
|
-
function range(size, startAt = 1, step = 1) {
|
|
303
|
-
return Array.from({ length: size }, (_, index) => startAt + index * step);
|
|
304
|
-
}
|
|
305
|
-
class DataFilter {
|
|
306
|
-
constructor(rules, state) {
|
|
307
|
-
__publicField(this, "state");
|
|
308
|
-
__publicField(this, "rules");
|
|
309
|
-
__publicField(this, "filters");
|
|
310
|
-
__publicField(this, "filterPublic", () => {
|
|
311
|
-
return (v) => {
|
|
312
|
-
const isPublic = !this.rules.IS_PRIVATE(v.element);
|
|
313
|
-
return {
|
|
314
|
-
tag: "filter-public",
|
|
315
|
-
condition: this.state.filterPublic && isPublic
|
|
316
|
-
};
|
|
317
|
-
};
|
|
318
|
-
});
|
|
319
|
-
__publicField(this, "filterPrivate", () => {
|
|
320
|
-
return (v) => {
|
|
321
|
-
const isPrivate = this.rules.IS_PRIVATE(v.element);
|
|
322
|
-
return {
|
|
323
|
-
tag: "filter-private",
|
|
324
|
-
condition: this.state.filterPrivate && isPrivate
|
|
325
|
-
};
|
|
326
|
-
};
|
|
327
|
-
});
|
|
328
|
-
__publicField(this, "filterHD", () => {
|
|
329
|
-
return (v) => {
|
|
330
|
-
const isHD = this.rules.IS_HD(v.element);
|
|
331
|
-
return {
|
|
332
|
-
tag: "filter-hd",
|
|
333
|
-
condition: this.state.filterHD && isHD
|
|
334
|
-
};
|
|
335
|
-
};
|
|
336
|
-
});
|
|
337
|
-
__publicField(this, "filterDuration", () => {
|
|
338
|
-
return (v) => {
|
|
339
|
-
const notInRange = v.duration < this.state.filterDurationFrom || v.duration > this.state.filterDurationTo;
|
|
340
|
-
return {
|
|
341
|
-
tag: "filter-duration",
|
|
342
|
-
condition: this.state.filterDuration && notInRange
|
|
343
|
-
};
|
|
344
|
-
};
|
|
345
|
-
});
|
|
346
|
-
__publicField(this, "filterExclude", () => {
|
|
347
|
-
const tags = DataManager.filterDSLToRegex(this.state.filterExcludeWords);
|
|
348
|
-
return (v) => {
|
|
349
|
-
const containTags = tags.some((tag) => tag.test(v.title));
|
|
350
|
-
return {
|
|
351
|
-
tag: "filter-exclude",
|
|
352
|
-
condition: this.state.filterExclude && containTags
|
|
353
|
-
};
|
|
354
|
-
};
|
|
355
|
-
});
|
|
356
|
-
__publicField(this, "filterInclude", () => {
|
|
357
|
-
const tags = DataManager.filterDSLToRegex(this.state.filterIncludeWords);
|
|
358
|
-
return (v) => {
|
|
359
|
-
const containTagsNot = tags.some((tag) => !tag.test(v.title));
|
|
360
|
-
return {
|
|
361
|
-
tag: "filter-include",
|
|
362
|
-
condition: this.state.filterInclude && containTagsNot
|
|
363
|
-
};
|
|
364
|
-
};
|
|
365
|
-
});
|
|
366
|
-
this.state = state;
|
|
367
|
-
this.rules = rules;
|
|
368
|
-
const methods = Object.getOwnPropertyNames(this);
|
|
369
|
-
this.filters = methods.reduce((acc, k) => {
|
|
370
|
-
if (k in this.state) {
|
|
371
|
-
acc[k] = this[k];
|
|
372
|
-
GM_addStyle(`.filter-${k.toLowerCase().slice(6)} { display: none !important; }`);
|
|
373
|
-
}
|
|
374
|
-
return acc;
|
|
375
|
-
}, {});
|
|
376
|
-
}
|
|
481
|
+
function isMob() {
|
|
482
|
+
return /iPhone|Android/i.test(navigator.userAgent);
|
|
377
483
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
__publicField(this, "state");
|
|
382
|
-
__publicField(this, "data");
|
|
383
|
-
__publicField(this, "lazyImgLoader");
|
|
384
|
-
__publicField(this, "dataFilters");
|
|
385
|
-
__publicField(this, "applyFilters", (filters, offset = 0) => {
|
|
386
|
-
const filtersToApply = Object.keys(filters).filter((k) => Object.hasOwn(this.dataFilters, k)).map((k) => this.dataFilters[k]());
|
|
387
|
-
if (filtersToApply.length === 0) return;
|
|
388
|
-
const updates = [];
|
|
389
|
-
let offset_counter = 1;
|
|
390
|
-
for (const v of this.data.values()) {
|
|
391
|
-
if (++offset_counter > offset) {
|
|
392
|
-
for (const f of filtersToApply) {
|
|
393
|
-
const { tag, condition } = f(v);
|
|
394
|
-
updates.push(() => v.element.classList.toggle(tag, condition));
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
requestAnimationFrame(() => {
|
|
399
|
-
updates.forEach((update) => update());
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
__publicField(this, "filterAll", (offset) => {
|
|
403
|
-
const filters = Object.assign(
|
|
404
|
-
{},
|
|
405
|
-
...Object.keys(this.dataFilters).map((f) => ({
|
|
406
|
-
[f]: this.state[f]
|
|
407
|
-
}))
|
|
408
|
-
);
|
|
409
|
-
this.applyFilters(filters, offset);
|
|
410
|
-
});
|
|
411
|
-
__publicField(this, "parseData", (html, container, removeDuplicates = false, shouldLazify = true) => {
|
|
412
|
-
const thumbs = this.rules.GET_THUMBS(html);
|
|
413
|
-
const data_offset = this.data.size;
|
|
414
|
-
for (const thumbElement of thumbs) {
|
|
415
|
-
const url = this.rules.THUMB_URL(thumbElement);
|
|
416
|
-
if (!url || this.data.has(url)) {
|
|
417
|
-
if (removeDuplicates) thumbElement.remove();
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
const data = this.rules.THUMB_DATA(thumbElement);
|
|
421
|
-
this.data.set(url, { element: thumbElement, ...data });
|
|
422
|
-
if (shouldLazify) {
|
|
423
|
-
const { img, imgSrc } = this.rules.THUMB_IMG_DATA(thumbElement);
|
|
424
|
-
this.lazyImgLoader.lazify(thumbElement, img, imgSrc);
|
|
425
|
-
}
|
|
426
|
-
const parent = container || this.rules.CONTAINER;
|
|
427
|
-
if (!parent.contains(thumbElement)) parent.appendChild(thumbElement);
|
|
428
|
-
}
|
|
429
|
-
this.filterAll(data_offset);
|
|
430
|
-
});
|
|
431
|
-
this.rules = rules;
|
|
432
|
-
this.state = state;
|
|
433
|
-
this.data = /* @__PURE__ */ new Map();
|
|
434
|
-
this.lazyImgLoader = new LazyImgLoader(
|
|
435
|
-
(target) => !this.isFiltered(target)
|
|
436
|
-
);
|
|
437
|
-
this.dataFilters = new DataFilter(rules, state).filters;
|
|
438
|
-
[window, unsafeWindow || {}].forEach((w) => {
|
|
439
|
-
Object.assign(w, {
|
|
440
|
-
sortByViews: () => this.sort("view"),
|
|
441
|
-
sortByDuration: () => this.sort("duration")
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
static filterDSLToRegex(str) {
|
|
446
|
-
const toFullWord = (w) => `(^|\\ )${w}($|\\ )`;
|
|
447
|
-
const str_ = str.replace(/f\:(\w+)/g, (_, w) => toFullWord(w));
|
|
448
|
-
return stringToWords(str_).map((expr) => new RegExp(expr, "i"));
|
|
449
|
-
}
|
|
450
|
-
isFiltered(el) {
|
|
451
|
-
return el.className.includes("filtered");
|
|
452
|
-
}
|
|
453
|
-
sort(propName) {
|
|
454
|
-
if (this.data.size < 2) return;
|
|
455
|
-
const sorted = Array.from(this.data.keys()).sort((b, a) => {
|
|
456
|
-
return this.data.get(a)[propName] - this.data.get(b)[propName];
|
|
457
|
-
});
|
|
458
|
-
const container = this.data.get(sorted[0]).element.parentElement;
|
|
459
|
-
sorted.forEach((s) => {
|
|
460
|
-
const e = this.data.get(s).element;
|
|
461
|
-
container.append(e);
|
|
462
|
-
});
|
|
484
|
+
function listenEvents(dom, events, callback) {
|
|
485
|
+
for (const e of events) {
|
|
486
|
+
dom.addEventListener(e, callback, true);
|
|
463
487
|
}
|
|
464
488
|
}
|
|
465
|
-
class
|
|
466
|
-
constructor({
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
writeHistory = false,
|
|
470
|
-
paginationOffset,
|
|
471
|
-
paginationLast,
|
|
472
|
-
paginationElement,
|
|
473
|
-
paginationUrlGenerator,
|
|
474
|
-
handleHtmlCallback,
|
|
475
|
-
alternativeGenerator,
|
|
476
|
-
intersectionObservable
|
|
477
|
-
}) {
|
|
478
|
-
__publicField(this, "paginationGenerator");
|
|
479
|
-
__publicField(this, "enabled");
|
|
480
|
-
__publicField(this, "delay");
|
|
481
|
-
__publicField(this, "paginationOffset");
|
|
482
|
-
__publicField(this, "paginationLast");
|
|
483
|
-
__publicField(this, "writeHistory");
|
|
484
|
-
__publicField(this, "handleHtmlCallback");
|
|
485
|
-
__publicField(this, "onScrollCBs", []);
|
|
486
|
-
__publicField(this, "generatorConsumer", async () => {
|
|
487
|
-
if (!this.enabled) return false;
|
|
488
|
-
const {
|
|
489
|
-
value: { url, offset } = {},
|
|
490
|
-
done
|
|
491
|
-
} = await this.paginationGenerator.next();
|
|
492
|
-
if (!done) {
|
|
493
|
-
const nextPageHTML = await fetchHtml(url);
|
|
494
|
-
const prevScrollPos = document.documentElement.scrollTop;
|
|
495
|
-
this.paginationOffset = offset;
|
|
496
|
-
this.handleHtmlCallback(nextPageHTML);
|
|
497
|
-
this._onScroll();
|
|
498
|
-
window.scrollTo(0, prevScrollPos);
|
|
499
|
-
if (this.writeHistory) {
|
|
500
|
-
history.replaceState({}, "", url);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
return !done;
|
|
504
|
-
});
|
|
505
|
-
this.enabled = enabled;
|
|
489
|
+
class Tick {
|
|
490
|
+
constructor(delay, startImmediate = true) {
|
|
491
|
+
__publicField(this, "tick");
|
|
492
|
+
__publicField(this, "callbackFinal");
|
|
506
493
|
this.delay = delay;
|
|
507
|
-
this.
|
|
508
|
-
this.paginationOffset = paginationOffset;
|
|
509
|
-
this.paginationLast = paginationLast;
|
|
510
|
-
this.handleHtmlCallback = handleHtmlCallback;
|
|
511
|
-
this.paginationGenerator = alternativeGenerator?.() ?? InfiniteScroller.createPaginationGenerator(
|
|
512
|
-
paginationOffset,
|
|
513
|
-
paginationLast,
|
|
514
|
-
paginationUrlGenerator
|
|
515
|
-
);
|
|
516
|
-
const observable = intersectionObservable || paginationElement;
|
|
517
|
-
Observer.observeWhile(observable, this.generatorConsumer, this.delay);
|
|
518
|
-
}
|
|
519
|
-
onScroll(callback, initCall = false) {
|
|
520
|
-
if (initCall) callback(this);
|
|
521
|
-
this.onScrollCBs.push(callback);
|
|
522
|
-
return this;
|
|
494
|
+
this.startImmediate = startImmediate;
|
|
523
495
|
}
|
|
524
|
-
|
|
525
|
-
this.
|
|
496
|
+
start(callback, callbackFinal) {
|
|
497
|
+
this.stop();
|
|
498
|
+
this.callbackFinal = callbackFinal;
|
|
499
|
+
if (this.startImmediate) callback();
|
|
500
|
+
this.tick = window.setInterval(callback, this.delay);
|
|
526
501
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
502
|
+
stop() {
|
|
503
|
+
if (this.tick !== void 0) {
|
|
504
|
+
clearInterval(this.tick);
|
|
505
|
+
this.tick = void 0;
|
|
506
|
+
}
|
|
507
|
+
if (this.callbackFinal) {
|
|
508
|
+
this.callbackFinal();
|
|
509
|
+
this.callbackFinal = void 0;
|
|
531
510
|
}
|
|
532
511
|
}
|
|
533
512
|
}
|
|
534
|
-
function
|
|
535
|
-
|
|
536
|
-
const iscroller = new InfiniteScroller({
|
|
537
|
-
enabled,
|
|
538
|
-
handleHtmlCallback,
|
|
539
|
-
...rules
|
|
540
|
-
}).onScroll(({ paginationLast, paginationOffset }) => {
|
|
541
|
-
store.localState.pagIndexLast = paginationLast;
|
|
542
|
-
store.localState.pagIndexCur = paginationOffset;
|
|
543
|
-
}, true);
|
|
544
|
-
store.subscribe(() => {
|
|
545
|
-
iscroller.enabled = store.state.infiniteScrollEnabled;
|
|
546
|
-
});
|
|
547
|
-
return iscroller;
|
|
513
|
+
function circularShift(n, c = 6, s = 1) {
|
|
514
|
+
return (n + s) % c || c;
|
|
548
515
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
return url.href;
|
|
576
|
-
});
|
|
577
|
-
__publicField(this, "_IS_VIDEO_PAGE", () => {
|
|
578
|
-
if (typeof this.options.IS_VIDEO_PAGE === "boolean") {
|
|
579
|
-
return this.options.IS_VIDEO_PAGE;
|
|
580
|
-
}
|
|
581
|
-
return this.options.IS_VIDEO_PAGE.test(location.pathname);
|
|
582
|
-
});
|
|
583
|
-
__publicField(this, "_IS_SEARCH_PAGE", () => {
|
|
584
|
-
if (typeof this.options.IS_SEARCH_PAGE === "boolean") {
|
|
585
|
-
return this.options.IS_SEARCH_PAGE;
|
|
586
|
-
}
|
|
587
|
-
return this.options.IS_SEARCH_PAGE.test(location.pathname);
|
|
588
|
-
});
|
|
589
|
-
__publicField(this, "_paginationElement", (html = document) => {
|
|
590
|
-
if (typeof this.options.paginationElement === "function") {
|
|
591
|
-
return this.options.paginationElement(html);
|
|
592
|
-
}
|
|
593
|
-
return [...html.querySelectorAll(this.options.paginationElement)].pop();
|
|
594
|
-
});
|
|
595
|
-
__publicField(this, "CONTAINER", (html = document) => {
|
|
596
|
-
if (typeof this.options.CONTAINER === "function") {
|
|
597
|
-
return this.options.CONTAINER(html);
|
|
598
|
-
}
|
|
599
|
-
return [...html.querySelectorAll(this.options.CONTAINER)].pop();
|
|
600
|
-
});
|
|
601
|
-
__publicField(this, "THUMB_URL", (thumb) => {
|
|
602
|
-
if (typeof this.options.THUMB_URL === "string") {
|
|
603
|
-
return thumb.querySelector(this.options.THUMB_URL).href || "";
|
|
604
|
-
}
|
|
605
|
-
return this.options.THUMB_URL(thumb);
|
|
606
|
-
});
|
|
607
|
-
__publicField(this, "GET_THUMBS", (html) => {
|
|
608
|
-
if (typeof this.options.GET_THUMBS === "string") {
|
|
609
|
-
return [...html.querySelectorAll(this.options.GET_THUMBS)];
|
|
610
|
-
}
|
|
611
|
-
return this.options.GET_THUMBS(html);
|
|
612
|
-
});
|
|
613
|
-
__publicField(this, "THUMB_DATA", (thumb) => {
|
|
614
|
-
const opt = this.options.THUMB_DATA;
|
|
615
|
-
if (typeof opt === "function") return opt(thumb);
|
|
616
|
-
let title = sanitizeStr(thumb.querySelector(opt.title)?.innerText || "");
|
|
617
|
-
if (opt.uploader) {
|
|
618
|
-
const uploader = sanitizeStr(
|
|
619
|
-
thumb.querySelector(opt.title)?.innerText || ""
|
|
620
|
-
);
|
|
621
|
-
title = `${title} user:${uploader}`;
|
|
516
|
+
function formatTimeToHHMMSS(timeString) {
|
|
517
|
+
const regex = /(?:(\d+)\s*h\s*)?(?:(\d+)\s*mi?n?\s*)?(?:(\d+)\s*sec)?/;
|
|
518
|
+
const match = timeString.match(regex);
|
|
519
|
+
const h = parseInt(match?.[1] || "0");
|
|
520
|
+
const m = parseInt(match?.[2] || "0");
|
|
521
|
+
const s = parseInt(match?.[3] || "0");
|
|
522
|
+
const pad = (num) => String(num).padStart(2, "0");
|
|
523
|
+
return `${pad(h)}:${pad(m)}:${pad(s)}`;
|
|
524
|
+
}
|
|
525
|
+
function timeToSeconds(t) {
|
|
526
|
+
const r = /sec|min|h|m/.test(t) ? formatTimeToHHMMSS(t) : t;
|
|
527
|
+
return (r?.match(/\d+/gm) || [0]).reverse().map((s, i) => parseInt(s) * 60 ** i).reduce((a, b) => a + b);
|
|
528
|
+
}
|
|
529
|
+
function parseIntegerOr(n, or) {
|
|
530
|
+
return ((num) => Number.isNaN(num) ? or : num)(parseInt(n));
|
|
531
|
+
}
|
|
532
|
+
function parseDataParams(str) {
|
|
533
|
+
const paramsStr = decodeURI(str.trim()).split(";");
|
|
534
|
+
return paramsStr.reduce((acc, s) => {
|
|
535
|
+
const parsed = s.match(/([\+\w]+):([\w\-\ ]+)?/);
|
|
536
|
+
if (parsed) {
|
|
537
|
+
const [, key, value] = parsed;
|
|
538
|
+
if (value) {
|
|
539
|
+
key.split("+").forEach((p) => {
|
|
540
|
+
acc[p] = value;
|
|
541
|
+
});
|
|
622
542
|
}
|
|
623
|
-
const duration = !opt.duration ? 0 : timeToSeconds(
|
|
624
|
-
sanitizeStr(thumb.querySelector(opt.duration)?.innerText || "")
|
|
625
|
-
);
|
|
626
|
-
return { title, duration };
|
|
627
|
-
});
|
|
628
|
-
__publicField(this, "THUMB_IMG_DATA", (thumb) => {
|
|
629
|
-
const opt = this.options.THUMB_IMG_DATA;
|
|
630
|
-
if (typeof opt === "function") return opt(thumb);
|
|
631
|
-
const result = {};
|
|
632
|
-
if (opt.img) {
|
|
633
|
-
const img = thumb.querySelector(opt.img);
|
|
634
|
-
const imgSrc = img.getAttribute(opt.imgSrc || "data-src") || img.getAttribute("src");
|
|
635
|
-
if (opt.lazyloading) {
|
|
636
|
-
img.classList.remove(opt.lazyloading);
|
|
637
|
-
}
|
|
638
|
-
Object.assign(result, { img, imgSrc });
|
|
639
|
-
if (img.complete && img.getAttribute("src") && !img.src.includes("data:image")) {
|
|
640
|
-
return {};
|
|
641
|
-
}
|
|
642
|
-
} else return {};
|
|
643
|
-
});
|
|
644
|
-
this.options = options;
|
|
645
|
-
this.delay = options?.delay || this.delay;
|
|
646
|
-
this.paginationOffset = this.options.paginationOffset;
|
|
647
|
-
this.paginationLast = this.options.paginationLast;
|
|
648
|
-
this.IS_VIDEO_PAGE = this._IS_VIDEO_PAGE();
|
|
649
|
-
this.IS_SEARCH_PAGE = this._IS_SEARCH_PAGE();
|
|
650
|
-
this.paginationElement = this._paginationElement();
|
|
651
|
-
if (options.URL_DATA) {
|
|
652
|
-
this.URL_DATA = options.URL_DATA;
|
|
653
|
-
Object.assign(this, this.URL_DATA());
|
|
654
543
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
}
|
|
544
|
+
return acc;
|
|
545
|
+
}, {});
|
|
546
|
+
}
|
|
547
|
+
function parseCSSUrl(s) {
|
|
548
|
+
return s.replace(/url\("|\"\).*/g, "");
|
|
661
549
|
}
|
|
662
550
|
export {
|
|
663
551
|
AsyncPool,
|
|
@@ -666,7 +554,6 @@ export {
|
|
|
666
554
|
LazyImgLoader,
|
|
667
555
|
MOBILE_UA,
|
|
668
556
|
Observer,
|
|
669
|
-
RulesHelper,
|
|
670
557
|
Tick,
|
|
671
558
|
chunks,
|
|
672
559
|
circularShift,
|