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.
- package/README.md +0 -3
- package/dist/core/pervertmonkey.core.es.d.ts +4 -4
- package/dist/core/pervertmonkey.core.es.js +24 -1
- package/dist/core/pervertmonkey.core.es.js.map +1 -1
- package/dist/core/pervertmonkey.core.umd.js +24 -1
- package/dist/core/pervertmonkey.core.umd.js.map +1 -1
- package/dist/userscripts/3hentai.user.js +1045 -1049
- package/dist/userscripts/camgirlfinder.user.js +38 -42
- package/dist/userscripts/camwhores.user.js +1429 -1433
- package/dist/userscripts/e-hentai.user.js +1077 -1081
- package/dist/userscripts/ebalka.user.js +1092 -1096
- package/dist/userscripts/eporner.user.js +1129 -1133
- package/dist/userscripts/erome.user.js +1109 -1113
- package/dist/userscripts/eroprofile.user.js +1062 -1066
- package/dist/userscripts/javhdporn.user.js +1046 -1050
- package/dist/userscripts/missav.user.js +1047 -1051
- package/dist/userscripts/motherless.user.js +1223 -1227
- package/dist/userscripts/namethatporn.user.js +1083 -1087
- package/dist/userscripts/nhentai.user.js +1123 -1127
- package/dist/userscripts/pornhub.user.js +1065 -1069
- package/dist/userscripts/spankbang.user.js +1097 -1101
- package/dist/userscripts/thisvid.user.js +2660 -0
- package/dist/userscripts/xhamster.user.js +1224 -1228
- package/dist/userscripts/xvideos.user.js +1116 -1120
- package/package.json +9 -9
- package/src/core/index.ts +4 -0
- package/src/index.ts +2 -42
- package/src/userscripts/scripts/3hentai.ts +1 -1
- package/src/userscripts/scripts/camwhores.ts +8 -9
- package/src/userscripts/scripts/e-hentai.ts +2 -2
- package/src/userscripts/scripts/ebalka.ts +2 -3
- package/src/userscripts/scripts/eporner.ts +2 -2
- package/src/userscripts/scripts/erome.ts +1 -1
- package/src/userscripts/scripts/eroprofile.ts +1 -1
- package/src/userscripts/scripts/javhdporn.ts +1 -1
- package/src/userscripts/scripts/missav.ts +1 -1
- package/src/userscripts/scripts/motherless.ts +2 -4
- package/src/userscripts/scripts/namethatporn.ts +1 -1
- package/src/userscripts/scripts/nhentai.ts +2 -2
- package/src/userscripts/scripts/pornhub.ts +1 -1
- package/src/userscripts/scripts/spankbang.ts +4 -5
- package/src/userscripts/scripts/xhamster.ts +5 -5
- package/src/userscripts/scripts/xvideos.ts +2 -3
- package/src/utils/index.ts +39 -0
- package/src/vite-env.d.ts +1 -1
- 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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
81
|
-
|
|
78
|
+
var _GM_addStyle = (() => typeof GM_addStyle != "undefined" ? GM_addStyle : undefined)();
|
|
79
|
+
var _unsafeWindow = (() => typeof unsafeWindow != "undefined" ? unsafeWindow : undefined)();
|
|
82
80
|
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
88
|
-
|
|
110
|
+
class RegexFilter {
|
|
111
|
+
regexes;
|
|
112
|
+
constructor(str, flags = "gi") {
|
|
113
|
+
this.regexes = memoize(this.compileSearchRegex)(str, flags);
|
|
89
114
|
}
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
target.src = target.getAttribute(this.attributeName);
|
|
214
|
-
target.removeAttribute(this.attributeName);
|
|
215
|
-
};
|
|
216
|
-
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
217
233
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
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
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
-
|
|
384
|
-
if (
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
|
|
426
|
-
|
|
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
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
-
|
|
438
|
-
|
|
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
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
694
|
+
}
|
|
695
|
+
return acc;
|
|
696
|
+
},
|
|
697
|
+
{}
|
|
698
|
+
);
|
|
699
|
+
}
|
|
460
700
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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,
|
|
742
|
+
k.includes("from") && url.searchParams.set(k, n.toString());
|
|
499
743
|
});
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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
|
-
|
|
622
|
-
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
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
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
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
|
-
|
|
647
|
-
const paginationStrategy = new PaginationStrategyConstructor(options);
|
|
648
|
-
return paginationStrategy;
|
|
847
|
+
return paginationUrlGenerator;
|
|
649
848
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
-
|
|
666
|
-
|
|
667
|
-
|
|
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
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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
|
-
|
|
690
|
-
return
|
|
877
|
+
if (PaginationStrategySearchParams.testLinks(pageLinks, searchParamSelector)) {
|
|
878
|
+
return PaginationStrategySearchParams;
|
|
691
879
|
}
|
|
692
|
-
|
|
693
|
-
return
|
|
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
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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
|
-
|
|
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
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
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
|
-
|
|
779
|
-
|
|
780
|
-
|
|
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
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
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
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
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
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
const
|
|
809
|
-
if (
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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
|
-
|
|
873
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
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 (
|
|
1017
|
-
|
|
1018
|
-
durationSelector = "[class *= duration]";
|
|
1019
|
-
uploaderSelector = "[class *= uploader], [class *= user], [class *= name]";
|
|
1042
|
+
if (type === "string") {
|
|
1043
|
+
return querySelectorText(thumb, selector);
|
|
1020
1044
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
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
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
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
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
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
|
-
|
|
1070
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
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
|
-
|
|
1149
|
-
|
|
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
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
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
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
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
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
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
|
-
|
|
1180
|
-
|
|
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
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
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
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
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
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
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
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
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
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
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
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
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
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1301
|
+
function circularShift(n, c = 6, s = 1) {
|
|
1302
|
+
return (n + s) % c || c;
|
|
1303
|
+
}
|
|
1306
1304
|
|
|
1307
|
-
|
|
1308
|
-
|
|
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
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
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
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
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
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
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
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
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
|
|
1411
|
-
|
|
1412
|
-
const
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
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
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
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
|
|
1470
|
-
const
|
|
1471
|
-
|
|
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
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
const
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
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
|
|
1533
|
-
const
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
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
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
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
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
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
|
-
|
|
1595
|
-
if (event.includes("check access")) {
|
|
1596
|
-
requestAccess();
|
|
1597
|
-
}
|
|
1598
|
-
});
|
|
1599
|
-
|
|
1600
|
-
})(jabronioutfit);
|
|
1596
|
+
});
|
|
1601
1597
|
|
|
1602
|
-
})();
|
|
1598
|
+
})(jabronioutfit);
|