billy-herrington-utils 1.4.4 → 1.4.6
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 +457 -263
- package/dist/billy-herrington-utils.es.js.map +1 -1
- package/dist/billy-herrington-utils.umd.js +457 -263
- package/dist/billy-herrington-utils.umd.js.map +1 -1
- package/dist/index.d.ts +38 -4
- package/package.json +14 -4
- package/src/index.ts +21 -14
- package/src/types/globals.d.ts +2 -0
- package/src/{data-manager → userscripts/data-manager}/index.ts +19 -12
- package/src/{utils → userscripts}/infinite-scroll/index.ts +6 -7
- package/src/{utils → userscripts}/jabroni-outfit-wrap/index.ts +6 -12
- 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/pagination-strategies.ts +0 -59
|
@@ -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,215 +52,11 @@ class LazyImgLoader {
|
|
|
92
52
|
this.lazyImgObserver.observe(img);
|
|
93
53
|
}
|
|
94
54
|
}
|
|
95
|
-
function
|
|
96
|
-
return (
|
|
97
|
-
}
|
|
98
|
-
function parseDom(html) {
|
|
99
|
-
const parsed = new DOMParser().parseFromString(html, "text/html").body;
|
|
100
|
-
return parsed.children.length > 1 ? parsed : parsed.firstElementChild;
|
|
101
|
-
}
|
|
102
|
-
function copyAttributes(target, source) {
|
|
103
|
-
for (const attr of source.attributes) {
|
|
104
|
-
attr.nodeValue && target.setAttribute(attr.nodeName, attr.nodeValue);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
function replaceElementTag(e, tagName) {
|
|
108
|
-
const newTagElement = document.createElement(tagName);
|
|
109
|
-
copyAttributes(newTagElement, e);
|
|
110
|
-
newTagElement.innerHTML = e.innerHTML;
|
|
111
|
-
e.parentNode?.replaceChild(newTagElement, e);
|
|
112
|
-
return newTagElement;
|
|
113
|
-
}
|
|
114
|
-
function getAllUniqueParents(elements) {
|
|
115
|
-
return Array.from(elements).reduce((acc, v) => {
|
|
116
|
-
if (v.parentElement && !acc.includes(v.parentElement)) {
|
|
117
|
-
acc.push(v.parentElement);
|
|
118
|
-
}
|
|
119
|
-
return acc;
|
|
120
|
-
}, []);
|
|
121
|
-
}
|
|
122
|
-
function findNextSibling(el) {
|
|
123
|
-
if (el.nextElementSibling) return el.nextElementSibling;
|
|
124
|
-
if (el.parentElement) return findNextSibling(el.parentElement);
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
function waitForElementExists(parent, selector, callback) {
|
|
128
|
-
const observer = new MutationObserver((_mutations) => {
|
|
129
|
-
const el = parent.querySelector(selector);
|
|
130
|
-
if (el) {
|
|
131
|
-
observer.disconnect();
|
|
132
|
-
callback(el);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
136
|
-
}
|
|
137
|
-
function watchElementChildrenCount(element, callback) {
|
|
138
|
-
let count = element.children.length;
|
|
139
|
-
const observer = new MutationObserver((mutationList, observer2) => {
|
|
140
|
-
for (const mutation of mutationList) {
|
|
141
|
-
if (mutation.type === "childList") {
|
|
142
|
-
if (count !== element.children.length) {
|
|
143
|
-
count = element.children.length;
|
|
144
|
-
callback(observer2, count);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
observer.observe(element, { childList: true });
|
|
150
|
-
}
|
|
151
|
-
function watchDomChangesWithThrottle(element, callback, throttle = 1e3, times = Infinity, options = { childList: true, subtree: true, attributes: true }) {
|
|
152
|
-
let lastMutationTime;
|
|
153
|
-
let timeout;
|
|
154
|
-
let times_ = times;
|
|
155
|
-
const observer = new MutationObserver((_mutationList, _observer) => {
|
|
156
|
-
if (times_ !== Infinity && times_ < 1) {
|
|
157
|
-
observer.disconnect();
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
times_--;
|
|
161
|
-
const now = Date.now();
|
|
162
|
-
if (lastMutationTime && now - lastMutationTime < throttle) {
|
|
163
|
-
timeout && clearTimeout(timeout);
|
|
164
|
-
}
|
|
165
|
-
timeout = setTimeout(callback, throttle);
|
|
166
|
-
lastMutationTime = now;
|
|
167
|
-
});
|
|
168
|
-
observer.observe(element, options);
|
|
169
|
-
return observer;
|
|
170
|
-
}
|
|
171
|
-
function downloader(options = { append: "", after: "", button: "", cbBefore: () => {
|
|
172
|
-
} }) {
|
|
173
|
-
const btn = parseDom(options.button);
|
|
174
|
-
if (options.append) document.querySelector(options.append)?.append(btn);
|
|
175
|
-
if (options.after) document.querySelector(options.after)?.after(btn);
|
|
176
|
-
btn.addEventListener("click", (e) => {
|
|
177
|
-
e.preventDefault();
|
|
178
|
-
if (options.cbBefore) options.cbBefore();
|
|
179
|
-
waitForElementExists(document.body, "video", (video) => {
|
|
180
|
-
window.location.href = video.getAttribute("src");
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
function exterminateVideo(video) {
|
|
185
|
-
video.removeAttribute("src");
|
|
186
|
-
video.load();
|
|
187
|
-
video.remove();
|
|
188
|
-
}
|
|
189
|
-
const MOBILE_UA = [
|
|
190
|
-
"Mozilla/5.0 (Linux; Android 10; K)",
|
|
191
|
-
"AppleWebKit/537.36 (KHTML, like Gecko)",
|
|
192
|
-
"Chrome/114.0.0.0 Mobile Safari/537.36"
|
|
193
|
-
].join(" ");
|
|
194
|
-
function fetchWith(url, options = { html: false, mobile: false }) {
|
|
195
|
-
const reqOpts = {};
|
|
196
|
-
if (options.mobile) Object.assign(reqOpts, { headers: new Headers({ "User-Agent": MOBILE_UA }) });
|
|
197
|
-
return fetch(url, reqOpts).then((r) => r.text()).then((r) => options.html ? parseDom(r) : r);
|
|
198
|
-
}
|
|
199
|
-
const fetchHtml = (url) => fetchWith(url, { html: true });
|
|
200
|
-
const fetchText = (url) => fetchWith(url);
|
|
201
|
-
function objectToFormData(object) {
|
|
202
|
-
const formData = new FormData();
|
|
203
|
-
Object.entries(object).forEach(([k, v]) => formData.append(k, v));
|
|
204
|
-
return formData;
|
|
205
|
-
}
|
|
206
|
-
function listenEvents(dom, events, callback) {
|
|
207
|
-
for (const e of events) {
|
|
208
|
-
dom.addEventListener(e, callback, true);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
class Tick {
|
|
212
|
-
constructor(delay, startImmediate = true) {
|
|
213
|
-
__publicField(this, "tick");
|
|
214
|
-
__publicField(this, "callbackFinal");
|
|
215
|
-
this.delay = delay;
|
|
216
|
-
this.startImmediate = startImmediate;
|
|
217
|
-
}
|
|
218
|
-
start(callback, callbackFinal) {
|
|
219
|
-
this.stop();
|
|
220
|
-
this.callbackFinal = callbackFinal;
|
|
221
|
-
if (this.startImmediate) callback();
|
|
222
|
-
this.tick = window.setInterval(callback, this.delay);
|
|
223
|
-
}
|
|
224
|
-
stop() {
|
|
225
|
-
if (this.tick !== void 0) {
|
|
226
|
-
clearInterval(this.tick);
|
|
227
|
-
this.tick = void 0;
|
|
228
|
-
}
|
|
229
|
-
if (this.callbackFinal) {
|
|
230
|
-
this.callbackFinal();
|
|
231
|
-
this.callbackFinal = void 0;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
function isMob() {
|
|
236
|
-
return /iPhone|Android/i.test(navigator.userAgent);
|
|
237
|
-
}
|
|
238
|
-
async function computeAsyncOneAtTime(iterable) {
|
|
239
|
-
const res = [];
|
|
240
|
-
for await (const f of iterable) {
|
|
241
|
-
res.push(await f());
|
|
242
|
-
}
|
|
243
|
-
return res;
|
|
244
|
-
}
|
|
245
|
-
function wait(milliseconds) {
|
|
246
|
-
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
247
|
-
}
|
|
248
|
-
class AsyncPool {
|
|
249
|
-
constructor(max = 1, pool = []) {
|
|
250
|
-
__publicField(this, "cur", 0);
|
|
251
|
-
__publicField(this, "finished");
|
|
252
|
-
__publicField(this, "_resolve");
|
|
253
|
-
this.max = max;
|
|
254
|
-
this.pool = pool;
|
|
255
|
-
this.finished = new Promise((resolve) => {
|
|
256
|
-
this._resolve = resolve;
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
static async doNAsyncAtOnce(max = 1, pool = []) {
|
|
260
|
-
const spool = new AsyncPool(max);
|
|
261
|
-
pool.forEach((f) => spool.push(f));
|
|
262
|
-
return spool.run();
|
|
263
|
-
}
|
|
264
|
-
getHighPriorityFirst(p = 0) {
|
|
265
|
-
if (p > 3 || this.pool.length === 0) return void 0;
|
|
266
|
-
const i = this.pool.findIndex((e) => e.p === p);
|
|
267
|
-
if (i >= 0) {
|
|
268
|
-
const res = this.pool[i].v;
|
|
269
|
-
this.pool.splice(i, 1);
|
|
270
|
-
return res;
|
|
271
|
-
}
|
|
272
|
-
return this.getHighPriorityFirst(p + 1);
|
|
273
|
-
}
|
|
274
|
-
async runTask() {
|
|
275
|
-
this.cur++;
|
|
276
|
-
const f = this.getHighPriorityFirst();
|
|
277
|
-
await f?.();
|
|
278
|
-
this.cur--;
|
|
279
|
-
this.runTasks();
|
|
280
|
-
}
|
|
281
|
-
runTasks() {
|
|
282
|
-
if (!this.pool.length) {
|
|
283
|
-
this._resolve?.(true);
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
if (this.cur < this.max) {
|
|
287
|
-
this.runTask();
|
|
288
|
-
this.runTasks();
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
async run() {
|
|
292
|
-
this.runTasks();
|
|
293
|
-
return this.finished;
|
|
294
|
-
}
|
|
295
|
-
push(x) {
|
|
296
|
-
this.pool.push("p" in x ? x : { v: x, p: 0 });
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
function chunks(arr, n) {
|
|
300
|
-
return Array.from({ length: Math.ceil(arr.length / n) }, (_, i) => arr.slice(i * n, i * n + n));
|
|
55
|
+
function stringToWords(s) {
|
|
56
|
+
return s.split(",").map((s2) => s2.trim().toLowerCase()).filter((_) => _);
|
|
301
57
|
}
|
|
302
|
-
function
|
|
303
|
-
return
|
|
58
|
+
function sanitizeStr(s) {
|
|
59
|
+
return s?.replace(/\n|\t/, " ").replace(/ {2,}/, " ").trim().toLowerCase() || "";
|
|
304
60
|
}
|
|
305
61
|
class DataFilter {
|
|
306
62
|
constructor(rules, state) {
|
|
@@ -395,7 +151,9 @@ class DataManager {
|
|
|
395
151
|
}
|
|
396
152
|
}
|
|
397
153
|
requestAnimationFrame(() => {
|
|
398
|
-
updates.forEach((update) =>
|
|
154
|
+
updates.forEach((update) => {
|
|
155
|
+
update();
|
|
156
|
+
});
|
|
399
157
|
});
|
|
400
158
|
});
|
|
401
159
|
__publicField(this, "filterAll", (offset) => {
|
|
@@ -434,7 +192,8 @@ class DataManager {
|
|
|
434
192
|
(target) => !this.isFiltered(target)
|
|
435
193
|
);
|
|
436
194
|
this.dataFilters = new DataFilter(rules, state).filters;
|
|
437
|
-
[window, unsafeWindow
|
|
195
|
+
const targets = [window, globalThis.unsafeWindow].filter(Boolean);
|
|
196
|
+
targets.forEach((w) => {
|
|
438
197
|
Object.assign(w, {
|
|
439
198
|
sortByViews: () => this.sort("view"),
|
|
440
199
|
sortByDuration: () => this.sort("duration")
|
|
@@ -443,7 +202,7 @@ class DataManager {
|
|
|
443
202
|
}
|
|
444
203
|
static filterDSLToRegex(str) {
|
|
445
204
|
const toFullWord = (w) => `(^|\\ )${w}($|\\ )`;
|
|
446
|
-
const str_ = str.replace(/f
|
|
205
|
+
const str_ = str.replace(/f:(\w+)/g, (_, w) => toFullWord(w));
|
|
447
206
|
return stringToWords(str_).map((expr) => new RegExp(expr, "i"));
|
|
448
207
|
}
|
|
449
208
|
isFiltered(el) {
|
|
@@ -461,9 +220,117 @@ class DataManager {
|
|
|
461
220
|
});
|
|
462
221
|
}
|
|
463
222
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
223
|
+
function parseDom(html) {
|
|
224
|
+
const parsed = new DOMParser().parseFromString(html, "text/html").body;
|
|
225
|
+
return parsed.children.length > 1 ? parsed : parsed.firstElementChild;
|
|
226
|
+
}
|
|
227
|
+
function copyAttributes(target, source) {
|
|
228
|
+
for (const attr of source.attributes) {
|
|
229
|
+
attr.nodeValue && target.setAttribute(attr.nodeName, attr.nodeValue);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function replaceElementTag(e, tagName) {
|
|
233
|
+
const newTagElement = document.createElement(tagName);
|
|
234
|
+
copyAttributes(newTagElement, e);
|
|
235
|
+
newTagElement.innerHTML = e.innerHTML;
|
|
236
|
+
e.parentNode?.replaceChild(newTagElement, e);
|
|
237
|
+
return newTagElement;
|
|
238
|
+
}
|
|
239
|
+
function getAllUniqueParents(elements) {
|
|
240
|
+
return Array.from(elements).reduce((acc, v) => {
|
|
241
|
+
if (v.parentElement && !acc.includes(v.parentElement)) {
|
|
242
|
+
acc.push(v.parentElement);
|
|
243
|
+
}
|
|
244
|
+
return acc;
|
|
245
|
+
}, []);
|
|
246
|
+
}
|
|
247
|
+
function findNextSibling(el) {
|
|
248
|
+
if (el.nextElementSibling) return el.nextElementSibling;
|
|
249
|
+
if (el.parentElement) return findNextSibling(el.parentElement);
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
function waitForElementExists(parent, selector, callback) {
|
|
253
|
+
const observer = new MutationObserver((_mutations) => {
|
|
254
|
+
const el = parent.querySelector(selector);
|
|
255
|
+
if (el) {
|
|
256
|
+
observer.disconnect();
|
|
257
|
+
callback(el);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
261
|
+
}
|
|
262
|
+
function watchElementChildrenCount(element, callback) {
|
|
263
|
+
let count = element.children.length;
|
|
264
|
+
const observer = new MutationObserver((mutationList, observer2) => {
|
|
265
|
+
for (const mutation of mutationList) {
|
|
266
|
+
if (mutation.type === "childList") {
|
|
267
|
+
if (count !== element.children.length) {
|
|
268
|
+
count = element.children.length;
|
|
269
|
+
callback(observer2, count);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
observer.observe(element, { childList: true });
|
|
275
|
+
}
|
|
276
|
+
function watchDomChangesWithThrottle(element, callback, throttle = 1e3, times = Infinity, options = { childList: true, subtree: true, attributes: true }) {
|
|
277
|
+
let lastMutationTime;
|
|
278
|
+
let timeout;
|
|
279
|
+
let times_ = times;
|
|
280
|
+
const observer = new MutationObserver((_mutationList, _observer) => {
|
|
281
|
+
if (times_ !== Infinity && times_ < 1) {
|
|
282
|
+
observer.disconnect();
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
times_--;
|
|
286
|
+
const now = Date.now();
|
|
287
|
+
if (lastMutationTime && now - lastMutationTime < throttle) {
|
|
288
|
+
timeout && clearTimeout(timeout);
|
|
289
|
+
}
|
|
290
|
+
timeout = setTimeout(callback, throttle);
|
|
291
|
+
lastMutationTime = now;
|
|
292
|
+
});
|
|
293
|
+
observer.observe(element, options);
|
|
294
|
+
return observer;
|
|
295
|
+
}
|
|
296
|
+
function downloader(options = { append: "", after: "", button: "", cbBefore: () => {
|
|
297
|
+
} }) {
|
|
298
|
+
const btn = parseDom(options.button);
|
|
299
|
+
if (options.append) document.querySelector(options.append)?.append(btn);
|
|
300
|
+
if (options.after) document.querySelector(options.after)?.after(btn);
|
|
301
|
+
btn.addEventListener("click", (e) => {
|
|
302
|
+
e.preventDefault();
|
|
303
|
+
if (options.cbBefore) options.cbBefore();
|
|
304
|
+
waitForElementExists(document.body, "video", (video) => {
|
|
305
|
+
window.location.href = video.getAttribute("src");
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function exterminateVideo(video) {
|
|
310
|
+
video.removeAttribute("src");
|
|
311
|
+
video.load();
|
|
312
|
+
video.remove();
|
|
313
|
+
}
|
|
314
|
+
const MOBILE_UA = [
|
|
315
|
+
"Mozilla/5.0 (Linux; Android 10; K)",
|
|
316
|
+
"AppleWebKit/537.36 (KHTML, like Gecko)",
|
|
317
|
+
"Chrome/114.0.0.0 Mobile Safari/537.36"
|
|
318
|
+
].join(" ");
|
|
319
|
+
function fetchWith(url, options = { html: false, mobile: false }) {
|
|
320
|
+
const reqOpts = {};
|
|
321
|
+
if (options.mobile) Object.assign(reqOpts, { headers: new Headers({ "User-Agent": MOBILE_UA }) });
|
|
322
|
+
return fetch(url, reqOpts).then((r) => r.text()).then((r) => options.html ? parseDom(r) : r);
|
|
323
|
+
}
|
|
324
|
+
const fetchHtml = (url) => fetchWith(url, { html: true });
|
|
325
|
+
const fetchText = (url) => fetchWith(url);
|
|
326
|
+
function objectToFormData(object) {
|
|
327
|
+
const formData = new FormData();
|
|
328
|
+
Object.entries(object).forEach(([k, v]) => formData.append(k, v));
|
|
329
|
+
return formData;
|
|
330
|
+
}
|
|
331
|
+
class InfiniteScroller {
|
|
332
|
+
constructor({
|
|
333
|
+
enabled = true,
|
|
467
334
|
delay = 350,
|
|
468
335
|
writeHistory = false,
|
|
469
336
|
paginationOffset,
|
|
@@ -484,10 +351,7 @@ class InfiniteScroller {
|
|
|
484
351
|
__publicField(this, "onScrollCBs", []);
|
|
485
352
|
__publicField(this, "generatorConsumer", async () => {
|
|
486
353
|
if (!this.enabled) return false;
|
|
487
|
-
const {
|
|
488
|
-
value: { url, offset } = {},
|
|
489
|
-
done
|
|
490
|
-
} = await this.paginationGenerator.next();
|
|
354
|
+
const { value: { url, offset } = {}, done } = await this.paginationGenerator.next();
|
|
491
355
|
if (!done) {
|
|
492
356
|
const nextPageHTML = await fetchHtml(url);
|
|
493
357
|
const prevScrollPos = document.documentElement.scrollTop;
|
|
@@ -521,7 +385,9 @@ class InfiniteScroller {
|
|
|
521
385
|
return this;
|
|
522
386
|
}
|
|
523
387
|
_onScroll() {
|
|
524
|
-
this.onScrollCBs.forEach((cb) =>
|
|
388
|
+
this.onScrollCBs.forEach((cb) => {
|
|
389
|
+
cb(this);
|
|
390
|
+
});
|
|
525
391
|
}
|
|
526
392
|
static *createPaginationGenerator(currentPage, totalPages, generateURL) {
|
|
527
393
|
for (let offset = currentPage + 1; offset <= totalPages; offset++) {
|
|
@@ -532,10 +398,6 @@ class InfiniteScroller {
|
|
|
532
398
|
}
|
|
533
399
|
function createInfiniteScroller(store, handleHtmlCallback, rules) {
|
|
534
400
|
const enabled = store.state.infiniteScrollEnabled;
|
|
535
|
-
rules.paginationOffset = rules.paginationStrategy.getPaginationOffset();
|
|
536
|
-
rules.paginationLast = rules.paginationStrategy.getPaginationLast();
|
|
537
|
-
rules.paginationUrlGenerator = rules.paginationStrategy.getPaginationUrlGenerator();
|
|
538
|
-
rules.paginationElement = rules.paginationStrategy.getPaginationElement();
|
|
539
401
|
const iscroller = new InfiniteScroller({
|
|
540
402
|
enabled,
|
|
541
403
|
handleHtmlCallback,
|
|
@@ -549,6 +411,333 @@ function createInfiniteScroller(store, handleHtmlCallback, rules) {
|
|
|
549
411
|
});
|
|
550
412
|
return iscroller;
|
|
551
413
|
}
|
|
414
|
+
function getPaginationLinks(doc = document, url = location.href, pathnameSelector = /\/(page\/)?\d+\/?$/) {
|
|
415
|
+
const currentUrl = parseURL(url);
|
|
416
|
+
currentUrl.pathname = currentUrl.pathname.replace(pathnameSelector, "/");
|
|
417
|
+
const pageLinks = Array.from(
|
|
418
|
+
doc.querySelectorAll("a[href]") || [],
|
|
419
|
+
(a) => a.href
|
|
420
|
+
).filter((h) => {
|
|
421
|
+
try {
|
|
422
|
+
const linkUrl = new URL(h.replace(/#$/, ""), doc.baseURI || currentUrl.origin);
|
|
423
|
+
return linkUrl.origin === currentUrl.origin && linkUrl.pathname.startsWith(currentUrl.pathname);
|
|
424
|
+
} catch {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
return pageLinks;
|
|
429
|
+
}
|
|
430
|
+
function parseURL(s) {
|
|
431
|
+
if (typeof s === "string") return new URL(s);
|
|
432
|
+
return new URL(s.href);
|
|
433
|
+
}
|
|
434
|
+
class PaginationStrategy {
|
|
435
|
+
constructor(options) {
|
|
436
|
+
__publicField(this, "doc", document);
|
|
437
|
+
__publicField(this, "url");
|
|
438
|
+
__publicField(this, "paginationSelector", ".pagination");
|
|
439
|
+
__publicField(this, "fixPaginationLast");
|
|
440
|
+
__publicField(this, "offsetMin", 1);
|
|
441
|
+
if (options) {
|
|
442
|
+
Object.entries(options).forEach(([k, v]) => {
|
|
443
|
+
Object.assign(this, { [k]: v });
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
this.url = parseURL(options?.url || this.doc.URL);
|
|
447
|
+
}
|
|
448
|
+
getPaginationElement() {
|
|
449
|
+
return this.doc.querySelector(this.paginationSelector) || this.doc;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
function formatTimeToHHMMSS(timeString) {
|
|
453
|
+
const regex = /(?:(\d+)\s*h\s*)?(?:(\d+)\s*mi?n?\s*)?(?:(\d+)\s*sec)?/;
|
|
454
|
+
const match = timeString.match(regex);
|
|
455
|
+
const h = parseInt(match?.[1] || "0");
|
|
456
|
+
const m = parseInt(match?.[2] || "0");
|
|
457
|
+
const s = parseInt(match?.[3] || "0");
|
|
458
|
+
const pad = (num) => String(num).padStart(2, "0");
|
|
459
|
+
return `${pad(h)}:${pad(m)}:${pad(s)}`;
|
|
460
|
+
}
|
|
461
|
+
function timeToSeconds(t) {
|
|
462
|
+
const r = /sec|min|h|m/.test(t) ? formatTimeToHHMMSS(t) : t;
|
|
463
|
+
return (r?.match(/\d+/gm) || [0]).reverse().map((s, i) => parseInt(s) * 60 ** i).reduce((a, b) => a + b);
|
|
464
|
+
}
|
|
465
|
+
function parseIntegerOr(n, or) {
|
|
466
|
+
return ((num) => Number.isNaN(num) ? or : num)(parseInt(n));
|
|
467
|
+
}
|
|
468
|
+
function parseDataParams(str) {
|
|
469
|
+
const paramsStr = decodeURI(str.trim()).split(";");
|
|
470
|
+
return paramsStr.reduce((acc, s) => {
|
|
471
|
+
const parsed = s.match(/([\+\w]+):([\w\-\ ]+)?/);
|
|
472
|
+
if (parsed) {
|
|
473
|
+
const [, key, value] = parsed;
|
|
474
|
+
if (value) {
|
|
475
|
+
key.split("+").forEach((p) => {
|
|
476
|
+
acc[p] = value;
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return acc;
|
|
481
|
+
}, {});
|
|
482
|
+
}
|
|
483
|
+
function parseCSSUrl(s) {
|
|
484
|
+
return s.replace(/url\("|\"\).*/g, "");
|
|
485
|
+
}
|
|
486
|
+
class PaginationStrategyDataParams extends PaginationStrategy {
|
|
487
|
+
getPaginationLast() {
|
|
488
|
+
const links = this.getPaginationElement()?.querySelectorAll("[data-parameters *= from]");
|
|
489
|
+
const pages = Array.from(links || [], (l) => {
|
|
490
|
+
const p = l.getAttribute("data-parameters");
|
|
491
|
+
const v = p?.match(/from\w*:(\d+)/)?.[1] || this.offsetMin.toString();
|
|
492
|
+
return parseInt(v);
|
|
493
|
+
});
|
|
494
|
+
const lastPage = Math.max(...pages, this.offsetMin);
|
|
495
|
+
if (this.fixPaginationLast) return this.fixPaginationLast(lastPage);
|
|
496
|
+
return lastPage;
|
|
497
|
+
}
|
|
498
|
+
getPaginationOffset() {
|
|
499
|
+
const link = this.getPaginationElement()?.querySelector(
|
|
500
|
+
".prev[data-parameters *= from], .prev [data-parameters *= from]"
|
|
501
|
+
);
|
|
502
|
+
if (!link) return this.offsetMin;
|
|
503
|
+
const p = link.getAttribute("data-parameters");
|
|
504
|
+
const v = p?.match(/from\w*:(\d+)/)?.[1] || this.offsetMin.toString();
|
|
505
|
+
return parseInt(v);
|
|
506
|
+
}
|
|
507
|
+
getPaginationUrlGenerator() {
|
|
508
|
+
const url = new URL(this.url.href);
|
|
509
|
+
const parametersElement = this.getPaginationElement()?.querySelector(
|
|
510
|
+
"a[data-block-id][data-parameters]"
|
|
511
|
+
);
|
|
512
|
+
const block_id = parametersElement?.getAttribute("data-block-id") || "";
|
|
513
|
+
const parameters = parseDataParams(parametersElement?.getAttribute("data-parameters") || "");
|
|
514
|
+
const attrs = {
|
|
515
|
+
mode: "async",
|
|
516
|
+
function: "get_block",
|
|
517
|
+
block_id,
|
|
518
|
+
...parameters
|
|
519
|
+
};
|
|
520
|
+
Object.keys(attrs).forEach((k) => {
|
|
521
|
+
url.searchParams.set(k, attrs[k]);
|
|
522
|
+
});
|
|
523
|
+
const paginationUrlGenerator = (n) => {
|
|
524
|
+
Object.keys(attrs).forEach((k) => {
|
|
525
|
+
k.includes("from") && url.searchParams.set(k, n.toString());
|
|
526
|
+
});
|
|
527
|
+
url.searchParams.set("_", Date.now().toString());
|
|
528
|
+
return url.href;
|
|
529
|
+
};
|
|
530
|
+
return paginationUrlGenerator;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
class PaginationStrategyPathnameParams extends PaginationStrategy {
|
|
534
|
+
constructor() {
|
|
535
|
+
super(...arguments);
|
|
536
|
+
__publicField(this, "pathnameSelector", /\/(\d+)\/?$/);
|
|
537
|
+
__publicField(this, "extractPage", (a) => {
|
|
538
|
+
const href = typeof a === "string" ? a : a.href;
|
|
539
|
+
const { pathname } = new URL(href, this.doc.baseURI || this.url.origin);
|
|
540
|
+
return parseInt(pathname.match(this.pathnameSelector)?.pop() || this.offsetMin.toString());
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
getPaginationLast() {
|
|
544
|
+
const links = getPaginationLinks(
|
|
545
|
+
this.getPaginationElement(),
|
|
546
|
+
this.url.href,
|
|
547
|
+
this.pathnameSelector
|
|
548
|
+
);
|
|
549
|
+
const pages = Array.from(links, this.extractPage);
|
|
550
|
+
const lastPage = Math.max(...pages, this.offsetMin);
|
|
551
|
+
if (this.fixPaginationLast) return this.fixPaginationLast(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
|
+
}
|
|
572
|
+
}
|
|
573
|
+
class PaginationStrategySearchParams extends PaginationStrategy {
|
|
574
|
+
constructor() {
|
|
575
|
+
super(...arguments);
|
|
576
|
+
__publicField(this, "searchParamSelector", "page");
|
|
577
|
+
}
|
|
578
|
+
getPaginationElement() {
|
|
579
|
+
return this.doc.querySelector(this.paginationSelector) || this.doc;
|
|
580
|
+
}
|
|
581
|
+
extractPage(a) {
|
|
582
|
+
const href = typeof a === "string" ? a : a.href;
|
|
583
|
+
const p = new URL(href).searchParams.get(this.searchParamSelector);
|
|
584
|
+
return parseInt(p) || this.offsetMin;
|
|
585
|
+
}
|
|
586
|
+
getPaginationLast() {
|
|
587
|
+
const links = getPaginationLinks(this.getPaginationElement(), this.url.href).filter(
|
|
588
|
+
(h) => /(page|p)=\d+/.test(h)
|
|
589
|
+
);
|
|
590
|
+
const pages = links.map(this.extractPage);
|
|
591
|
+
const lastPage = Math.max(...pages, this.offsetMin);
|
|
592
|
+
if (this.fixPaginationLast) return this.fixPaginationLast(lastPage);
|
|
593
|
+
return lastPage;
|
|
594
|
+
}
|
|
595
|
+
getPaginationOffset() {
|
|
596
|
+
if (this.doc === document) {
|
|
597
|
+
return this.extractPage(this.url);
|
|
598
|
+
}
|
|
599
|
+
const link = this.getPaginationElement().querySelector(
|
|
600
|
+
`a.active[href *= "${this.searchParamSelector}="]`
|
|
601
|
+
);
|
|
602
|
+
return this.extractPage(link);
|
|
603
|
+
}
|
|
604
|
+
getPaginationUrlGenerator() {
|
|
605
|
+
const url = new URL(this.url.href);
|
|
606
|
+
const paginationUrlGenerator = (offset) => {
|
|
607
|
+
url.searchParams.set(this.searchParamSelector, offset.toString());
|
|
608
|
+
return url.href;
|
|
609
|
+
};
|
|
610
|
+
return paginationUrlGenerator;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function getPaginationStrategy(options) {
|
|
614
|
+
const { doc = document, url = location.href } = options;
|
|
615
|
+
const pageLinks = getPaginationLinks(doc, url);
|
|
616
|
+
console.log({ pageLinks });
|
|
617
|
+
const getStrategy = () => {
|
|
618
|
+
if (pageLinks.some((h) => /(page|p)=\d+/.test(h))) {
|
|
619
|
+
const l = pageLinks.filter((h) => /(page|p)=\d+/.test(h));
|
|
620
|
+
console.log("PaginationStrategySearchParams", l);
|
|
621
|
+
return PaginationStrategySearchParams;
|
|
622
|
+
}
|
|
623
|
+
if (pageLinks.some((h) => /\/(page\/)?\d+\/?$/.test(h))) {
|
|
624
|
+
const l = pageLinks.filter((h) => /\/(page\/)?\d+\/?$/.test(h));
|
|
625
|
+
console.log("PaginationStrategyPathnameParams", l);
|
|
626
|
+
return PaginationStrategyPathnameParams;
|
|
627
|
+
}
|
|
628
|
+
const type5Links = Array.from(document.querySelectorAll("[data-parameters *= from]"));
|
|
629
|
+
if (type5Links.length > 0) {
|
|
630
|
+
console.log("PaginationStrategyDataParams", type5Links);
|
|
631
|
+
return PaginationStrategyDataParams;
|
|
632
|
+
}
|
|
633
|
+
console.error("Found No Strategy");
|
|
634
|
+
return PaginationStrategy;
|
|
635
|
+
};
|
|
636
|
+
const paginationStrategy = new (getStrategy())(options);
|
|
637
|
+
return paginationStrategy;
|
|
638
|
+
}
|
|
639
|
+
function chunks(arr, n) {
|
|
640
|
+
return Array.from({ length: Math.ceil(arr.length / n) }, (_, i) => arr.slice(i * n, i * n + n));
|
|
641
|
+
}
|
|
642
|
+
function range(size, startAt = 1, step = 1) {
|
|
643
|
+
return Array.from({ length: size }, (_, index) => startAt + index * step);
|
|
644
|
+
}
|
|
645
|
+
async function computeAsyncOneAtTime(iterable) {
|
|
646
|
+
const res = [];
|
|
647
|
+
for await (const f of iterable) {
|
|
648
|
+
res.push(await f());
|
|
649
|
+
}
|
|
650
|
+
return res;
|
|
651
|
+
}
|
|
652
|
+
function wait(milliseconds) {
|
|
653
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
654
|
+
}
|
|
655
|
+
class AsyncPool {
|
|
656
|
+
constructor(max = 1, pool = []) {
|
|
657
|
+
__publicField(this, "cur", 0);
|
|
658
|
+
__publicField(this, "finished");
|
|
659
|
+
__publicField(this, "_resolve");
|
|
660
|
+
this.max = max;
|
|
661
|
+
this.pool = pool;
|
|
662
|
+
this.finished = new Promise((resolve) => {
|
|
663
|
+
this._resolve = resolve;
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
static async doNAsyncAtOnce(max = 1, pool = []) {
|
|
667
|
+
const spool = new AsyncPool(max);
|
|
668
|
+
pool.forEach((f) => spool.push(f));
|
|
669
|
+
return spool.run();
|
|
670
|
+
}
|
|
671
|
+
getHighPriorityFirst(p = 0) {
|
|
672
|
+
if (p > 3 || this.pool.length === 0) return void 0;
|
|
673
|
+
const i = this.pool.findIndex((e) => e.p === p);
|
|
674
|
+
if (i >= 0) {
|
|
675
|
+
const res = this.pool[i].v;
|
|
676
|
+
this.pool.splice(i, 1);
|
|
677
|
+
return res;
|
|
678
|
+
}
|
|
679
|
+
return this.getHighPriorityFirst(p + 1);
|
|
680
|
+
}
|
|
681
|
+
async runTask() {
|
|
682
|
+
this.cur++;
|
|
683
|
+
const f = this.getHighPriorityFirst();
|
|
684
|
+
await f?.();
|
|
685
|
+
this.cur--;
|
|
686
|
+
this.runTasks();
|
|
687
|
+
}
|
|
688
|
+
runTasks() {
|
|
689
|
+
if (!this.pool.length) {
|
|
690
|
+
this._resolve?.(true);
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
if (this.cur < this.max) {
|
|
694
|
+
this.runTask();
|
|
695
|
+
this.runTasks();
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
async run() {
|
|
699
|
+
this.runTasks();
|
|
700
|
+
return this.finished;
|
|
701
|
+
}
|
|
702
|
+
push(x) {
|
|
703
|
+
this.pool.push("p" in x ? x : { v: x, p: 0 });
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
function isMob() {
|
|
707
|
+
return /iPhone|Android/i.test(navigator.userAgent);
|
|
708
|
+
}
|
|
709
|
+
function listenEvents(dom, events, callback) {
|
|
710
|
+
for (const e of events) {
|
|
711
|
+
dom.addEventListener(e, callback, true);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
class Tick {
|
|
715
|
+
constructor(delay, startImmediate = true) {
|
|
716
|
+
__publicField(this, "tick");
|
|
717
|
+
__publicField(this, "callbackFinal");
|
|
718
|
+
this.delay = delay;
|
|
719
|
+
this.startImmediate = startImmediate;
|
|
720
|
+
}
|
|
721
|
+
start(callback, callbackFinal) {
|
|
722
|
+
this.stop();
|
|
723
|
+
this.callbackFinal = callbackFinal;
|
|
724
|
+
if (this.startImmediate) callback();
|
|
725
|
+
this.tick = window.setInterval(callback, this.delay);
|
|
726
|
+
}
|
|
727
|
+
stop() {
|
|
728
|
+
if (this.tick !== void 0) {
|
|
729
|
+
clearInterval(this.tick);
|
|
730
|
+
this.tick = void 0;
|
|
731
|
+
}
|
|
732
|
+
if (this.callbackFinal) {
|
|
733
|
+
this.callbackFinal();
|
|
734
|
+
this.callbackFinal = void 0;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function circularShift(n, c = 6, s = 1) {
|
|
739
|
+
return (n + s) % c || c;
|
|
740
|
+
}
|
|
552
741
|
export {
|
|
553
742
|
AsyncPool,
|
|
554
743
|
DataManager,
|
|
@@ -556,6 +745,10 @@ export {
|
|
|
556
745
|
LazyImgLoader,
|
|
557
746
|
MOBILE_UA,
|
|
558
747
|
Observer,
|
|
748
|
+
PaginationStrategy,
|
|
749
|
+
PaginationStrategyDataParams,
|
|
750
|
+
PaginationStrategyPathnameParams,
|
|
751
|
+
PaginationStrategySearchParams,
|
|
559
752
|
Tick,
|
|
560
753
|
chunks,
|
|
561
754
|
circularShift,
|
|
@@ -569,6 +762,7 @@ export {
|
|
|
569
762
|
fetchWith,
|
|
570
763
|
findNextSibling,
|
|
571
764
|
getAllUniqueParents,
|
|
765
|
+
getPaginationStrategy,
|
|
572
766
|
isMob,
|
|
573
767
|
listenEvents,
|
|
574
768
|
objectToFormData,
|