@sv443-network/userutils 1.1.0 → 1.1.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/CHANGELOG.md +6 -0
- package/README.md +10 -9
- package/dist/index.global.js +443 -0
- package/dist/index.js +426 -28
- package/dist/index.mjs +400 -2
- package/dist/lib/config.d.ts +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -637,8 +637,9 @@ new ConfigManager(options: ConfigManagerOptions)
|
|
|
637
637
|
|
|
638
638
|
A class that manages a userscript's configuration that is persistently saved to and loaded from GM storage.
|
|
639
639
|
Also supports automatic migration of outdated data formats via provided migration functions.
|
|
640
|
+
You may create as many instances as you like as long as they have different IDs.
|
|
640
641
|
|
|
641
|
-
⚠️ The configuration is stored as a JSON string, so only JSON-compatible data can be used.
|
|
642
|
+
⚠️ The configuration is stored as a JSON string, so only JSON-compatible data can be used. Circular structures and complex objects will throw an error on load and save.
|
|
642
643
|
⚠️ The directives `@grant GM.getValue` and `@grant GM.setValue` are required for this to work.
|
|
643
644
|
|
|
644
645
|
The options object has the following properties:
|
|
@@ -670,8 +671,8 @@ Writes the default configuration given in `options.defaultConfig` synchronously
|
|
|
670
671
|
`deleteConfig(): Promise<void>`
|
|
671
672
|
Fully deletes the configuration from persistent storage.
|
|
672
673
|
The internal cache will be left untouched, so any subsequent calls to `getData()` will return the data that was last loaded.
|
|
673
|
-
If `loadData()` or `setData()` are called after this, the persistent storage will be populated again.
|
|
674
|
-
If you want to use this method, the additional directive `@grant GM.deleteValue` is required.
|
|
674
|
+
If `loadData()` or `setData()` are called after this, the persistent storage will be populated with the value of `options.defaultConfig` again.
|
|
675
|
+
⚠️ If you want to use this method, the additional directive `@grant GM.deleteValue` is required.
|
|
675
676
|
|
|
676
677
|
<br>
|
|
677
678
|
|
|
@@ -699,7 +700,7 @@ const formatVersion = 2;
|
|
|
699
700
|
/** Functions that migrate outdated data to the latest format - make sure a function exists for every previously used formatVersion and that no numbers are skipped! */
|
|
700
701
|
const migrations = {
|
|
701
702
|
// migrate from format version 0 to 1
|
|
702
|
-
1: (oldData:
|
|
703
|
+
1: (oldData: Record<string, unknown>) => {
|
|
703
704
|
return {
|
|
704
705
|
foo: oldData.foo,
|
|
705
706
|
bar: oldData.bar,
|
|
@@ -707,7 +708,7 @@ const migrations = {
|
|
|
707
708
|
};
|
|
708
709
|
},
|
|
709
710
|
// asynchronously migrate from format version 1 to 2
|
|
710
|
-
2: async (oldData:
|
|
711
|
+
2: async (oldData: Record<string, unknown>) => {
|
|
711
712
|
// arbitrary async operation required for the new format
|
|
712
713
|
const qux = JSON.parse(await (await fetch("https://api.example.org/some-data")).text());
|
|
713
714
|
return {
|
|
@@ -719,7 +720,7 @@ const migrations = {
|
|
|
719
720
|
},
|
|
720
721
|
};
|
|
721
722
|
|
|
722
|
-
const
|
|
723
|
+
const manager = new ConfigManager({
|
|
723
724
|
/** A unique ID for this configuration - choose wisely as changing it is not supported yet! */
|
|
724
725
|
id: "my-userscript",
|
|
725
726
|
/** Default / fallback configuration data */
|
|
@@ -735,7 +736,7 @@ async function init() {
|
|
|
735
736
|
// wait for the config to be loaded from persistent storage
|
|
736
737
|
// if no data was saved in persistent storage before or getData() is called before loadData(), the value of options.defaultConfig will be returned
|
|
737
738
|
// if the previously saved data needs to be migrated to a newer version, it will happen in this function call
|
|
738
|
-
const configData = await
|
|
739
|
+
const configData = await manager.loadData();
|
|
739
740
|
|
|
740
741
|
console.log(configData.foo); // "hello"
|
|
741
742
|
|
|
@@ -744,12 +745,12 @@ async function init() {
|
|
|
744
745
|
configData.bar = 123;
|
|
745
746
|
|
|
746
747
|
// save the updated config - synchronously to the cache and asynchronously to persistent storage
|
|
747
|
-
|
|
748
|
+
manager.saveData(configData).then(() => {
|
|
748
749
|
console.log("Config saved to persistent storage!");
|
|
749
750
|
});
|
|
750
751
|
|
|
751
752
|
// the internal cache is updated synchronously, so the updated data can be accessed before the Promise resolves:
|
|
752
|
-
console.log(
|
|
753
|
+
console.log(manager.getData().foo); // "world"
|
|
753
754
|
}
|
|
754
755
|
|
|
755
756
|
init();
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
// ==UserScript==
|
|
2
|
+
// @name UserUtils
|
|
3
|
+
// @description Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, manage persistent user configurations, modify the DOM more easily and more
|
|
4
|
+
// @namespace https://github.com/Sv443-Network/UserUtils
|
|
5
|
+
// @version 1.1.1
|
|
6
|
+
// @license MIT
|
|
7
|
+
// @author Sv443
|
|
8
|
+
// @copyright Sv443 (https://github.com/Sv443)
|
|
9
|
+
// @supportURL https://github.com/Sv443-Network/UserUtils/issues
|
|
10
|
+
// @homepageURL https://github.com/Sv443-Network/UserUtils#readme
|
|
11
|
+
// ==/UserScript==
|
|
12
|
+
|
|
13
|
+
var UserUtils = (function (exports) {
|
|
14
|
+
var __defProp = Object.defineProperty;
|
|
15
|
+
var __defProps = Object.defineProperties;
|
|
16
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
17
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
18
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
19
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
20
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
21
|
+
var __spreadValues = (a, b) => {
|
|
22
|
+
for (var prop in b || (b = {}))
|
|
23
|
+
if (__hasOwnProp.call(b, prop))
|
|
24
|
+
__defNormalProp(a, prop, b[prop]);
|
|
25
|
+
if (__getOwnPropSymbols)
|
|
26
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
27
|
+
if (__propIsEnum.call(b, prop))
|
|
28
|
+
__defNormalProp(a, prop, b[prop]);
|
|
29
|
+
}
|
|
30
|
+
return a;
|
|
31
|
+
};
|
|
32
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
33
|
+
var __publicField = (obj, key, value) => {
|
|
34
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
35
|
+
return value;
|
|
36
|
+
};
|
|
37
|
+
var __async = (__this, __arguments, generator) => {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
var fulfilled = (value) => {
|
|
40
|
+
try {
|
|
41
|
+
step(generator.next(value));
|
|
42
|
+
} catch (e) {
|
|
43
|
+
reject(e);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var rejected = (value) => {
|
|
47
|
+
try {
|
|
48
|
+
step(generator.throw(value));
|
|
49
|
+
} catch (e) {
|
|
50
|
+
reject(e);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
54
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// lib/math.ts
|
|
59
|
+
function clamp(value, min, max) {
|
|
60
|
+
return Math.max(Math.min(value, max), min);
|
|
61
|
+
}
|
|
62
|
+
function mapRange(value, range_1_min, range_1_max, range_2_min, range_2_max) {
|
|
63
|
+
if (Number(range_1_min) === 0 && Number(range_2_min) === 0)
|
|
64
|
+
return value * (range_2_max / range_1_max);
|
|
65
|
+
return (value - range_1_min) * ((range_2_max - range_2_min) / (range_1_max - range_1_min)) + range_2_min;
|
|
66
|
+
}
|
|
67
|
+
function randRange(...args) {
|
|
68
|
+
let min, max;
|
|
69
|
+
if (typeof args[0] === "number" && typeof args[1] === "number") {
|
|
70
|
+
[min, max] = args;
|
|
71
|
+
} else if (typeof args[0] === "number" && typeof args[1] !== "number") {
|
|
72
|
+
min = 0;
|
|
73
|
+
max = args[0];
|
|
74
|
+
} else
|
|
75
|
+
throw new TypeError(`Wrong parameter(s) provided - expected: "number" and "number|undefined", got: "${typeof args[0]}" and "${typeof args[1]}"`);
|
|
76
|
+
min = Number(min);
|
|
77
|
+
max = Number(max);
|
|
78
|
+
if (isNaN(min) || isNaN(max))
|
|
79
|
+
throw new TypeError(`Parameters "min" and "max" can't be NaN`);
|
|
80
|
+
if (min > max)
|
|
81
|
+
throw new TypeError(`Parameter "min" can't be bigger than "max"`);
|
|
82
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// lib/array.ts
|
|
86
|
+
function randomItem(array) {
|
|
87
|
+
return randomItemIndex(array)[0];
|
|
88
|
+
}
|
|
89
|
+
function randomItemIndex(array) {
|
|
90
|
+
if (array.length === 0)
|
|
91
|
+
return [void 0, void 0];
|
|
92
|
+
const idx = randRange(array.length - 1);
|
|
93
|
+
return [array[idx], idx];
|
|
94
|
+
}
|
|
95
|
+
function takeRandomItem(arr) {
|
|
96
|
+
const [itm, idx] = randomItemIndex(arr);
|
|
97
|
+
if (idx === void 0)
|
|
98
|
+
return void 0;
|
|
99
|
+
arr.splice(idx, 1);
|
|
100
|
+
return itm;
|
|
101
|
+
}
|
|
102
|
+
function randomizeArray(array) {
|
|
103
|
+
const retArray = [...array];
|
|
104
|
+
if (array.length === 0)
|
|
105
|
+
return array;
|
|
106
|
+
for (let i = retArray.length - 1; i > 0; i--) {
|
|
107
|
+
const j = Math.floor(randRange(0, 1e4) / 1e4 * (i + 1));
|
|
108
|
+
[retArray[i], retArray[j]] = [retArray[j], retArray[i]];
|
|
109
|
+
}
|
|
110
|
+
return retArray;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// lib/config.ts
|
|
114
|
+
var ConfigManager = class {
|
|
115
|
+
/**
|
|
116
|
+
* Creates an instance of ConfigManager to manage a user configuration that is cached in memory and persistently saved across sessions.
|
|
117
|
+
* Supports migrating data from older versions of the configuration to newer ones and populating the cache with default data if no persistent data is found.
|
|
118
|
+
*
|
|
119
|
+
* ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue`
|
|
120
|
+
* ⚠️ Make sure to call `loadData()` at least once after creating an instance, or the returned data will be the same as `options.defaultConfig`
|
|
121
|
+
*
|
|
122
|
+
* @template TData The type of the data that is saved in persistent storage (will be automatically inferred from `config.defaultConfig`) - this should also be the type of the data format associated with the current `options.formatVersion`
|
|
123
|
+
* @param options The options for this ConfigManager instance
|
|
124
|
+
*/
|
|
125
|
+
constructor(options) {
|
|
126
|
+
__publicField(this, "id");
|
|
127
|
+
__publicField(this, "formatVersion");
|
|
128
|
+
__publicField(this, "defaultConfig");
|
|
129
|
+
__publicField(this, "cachedConfig");
|
|
130
|
+
__publicField(this, "migrations");
|
|
131
|
+
this.id = options.id;
|
|
132
|
+
this.formatVersion = options.formatVersion;
|
|
133
|
+
this.defaultConfig = options.defaultConfig;
|
|
134
|
+
this.cachedConfig = options.defaultConfig;
|
|
135
|
+
this.migrations = options.migrations;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Loads the data saved in persistent storage into the in-memory cache and also returns it.
|
|
139
|
+
* Automatically populates persistent storage with default data if it doesn't contain any data yet.
|
|
140
|
+
* Also runs all necessary migration functions if the data format has changed since the last time the data was saved.
|
|
141
|
+
*/
|
|
142
|
+
loadData() {
|
|
143
|
+
return __async(this, null, function* () {
|
|
144
|
+
try {
|
|
145
|
+
const gmData = yield GM.getValue(`_uucfg-${this.id}`, this.defaultConfig);
|
|
146
|
+
let gmFmtVer = Number(yield GM.getValue(`_uucfgver-${this.id}`));
|
|
147
|
+
if (typeof gmData !== "string") {
|
|
148
|
+
yield this.saveDefaultData();
|
|
149
|
+
return this.defaultConfig;
|
|
150
|
+
}
|
|
151
|
+
if (isNaN(gmFmtVer))
|
|
152
|
+
yield GM.setValue(`_uucfgver-${this.id}`, gmFmtVer = this.formatVersion);
|
|
153
|
+
let parsed = JSON.parse(gmData);
|
|
154
|
+
if (gmFmtVer < this.formatVersion && this.migrations)
|
|
155
|
+
parsed = yield this.runMigrations(parsed, gmFmtVer);
|
|
156
|
+
return this.cachedConfig = typeof parsed === "object" ? parsed : void 0;
|
|
157
|
+
} catch (err) {
|
|
158
|
+
yield this.saveDefaultData();
|
|
159
|
+
return this.defaultConfig;
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/** Returns a copy of the data from the in-memory cache. Use `loadData()` to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage). */
|
|
164
|
+
getData() {
|
|
165
|
+
return this.deepCopy(this.cachedConfig);
|
|
166
|
+
}
|
|
167
|
+
/** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
|
|
168
|
+
setData(data) {
|
|
169
|
+
this.cachedConfig = data;
|
|
170
|
+
return new Promise((resolve) => __async(this, null, function* () {
|
|
171
|
+
yield Promise.allSettled([
|
|
172
|
+
GM.setValue(`_uucfg-${this.id}`, JSON.stringify(data)),
|
|
173
|
+
GM.setValue(`_uucfgver-${this.id}`, this.formatVersion)
|
|
174
|
+
]);
|
|
175
|
+
resolve();
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
/** Saves the default configuration data passed in the constructor synchronously to the in-memory cache and asynchronously to persistent storage */
|
|
179
|
+
saveDefaultData() {
|
|
180
|
+
return __async(this, null, function* () {
|
|
181
|
+
this.cachedConfig = this.defaultConfig;
|
|
182
|
+
return new Promise((resolve) => __async(this, null, function* () {
|
|
183
|
+
yield Promise.allSettled([
|
|
184
|
+
GM.setValue(`_uucfg-${this.id}`, JSON.stringify(this.defaultConfig)),
|
|
185
|
+
GM.setValue(`_uucfgver-${this.id}`, this.formatVersion)
|
|
186
|
+
]);
|
|
187
|
+
resolve();
|
|
188
|
+
}));
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Call this method to clear all persistently stored data associated with this ConfigManager instance.
|
|
193
|
+
* The in-memory cache will be left untouched, so you may still access the data with `getData()`.
|
|
194
|
+
* Calling `loadData()` or `setData()` after this method was called will recreate persistent storage with the cached or default data.
|
|
195
|
+
*
|
|
196
|
+
* ⚠️ This requires the additional directive `@grant GM.deleteValue`
|
|
197
|
+
*/
|
|
198
|
+
deleteConfig() {
|
|
199
|
+
return __async(this, null, function* () {
|
|
200
|
+
yield Promise.allSettled([
|
|
201
|
+
GM.deleteValue(`_uucfg-${this.id}`),
|
|
202
|
+
GM.deleteValue(`_uucfgver-${this.id}`)
|
|
203
|
+
]);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/** Runs all necessary migration functions consecutively - may be overwritten in a subclass */
|
|
207
|
+
runMigrations(oldData, oldFmtVer) {
|
|
208
|
+
return __async(this, null, function* () {
|
|
209
|
+
if (!this.migrations)
|
|
210
|
+
return oldData;
|
|
211
|
+
let newData = oldData;
|
|
212
|
+
const sortedMigrations = Object.entries(this.migrations).sort(([a], [b]) => Number(a) - Number(b));
|
|
213
|
+
let lastFmtVer = oldFmtVer;
|
|
214
|
+
for (const [fmtVer, migrationFunc] of sortedMigrations) {
|
|
215
|
+
const ver = Number(fmtVer);
|
|
216
|
+
if (oldFmtVer < this.formatVersion && oldFmtVer < ver) {
|
|
217
|
+
try {
|
|
218
|
+
const migRes = migrationFunc(newData);
|
|
219
|
+
newData = migRes instanceof Promise ? yield migRes : migRes;
|
|
220
|
+
lastFmtVer = oldFmtVer = ver;
|
|
221
|
+
} catch (err) {
|
|
222
|
+
console.error(`Error while running migration function for format version ${fmtVer}:`, err);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
yield Promise.allSettled([
|
|
227
|
+
GM.setValue(`_uucfg-${this.id}`, JSON.stringify(newData)),
|
|
228
|
+
GM.setValue(`_uucfgver-${this.id}`, lastFmtVer)
|
|
229
|
+
]);
|
|
230
|
+
return newData;
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/** Copies a JSON-compatible object and loses its internal references */
|
|
234
|
+
deepCopy(obj) {
|
|
235
|
+
return JSON.parse(JSON.stringify(obj));
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// lib/dom.ts
|
|
240
|
+
function getUnsafeWindow() {
|
|
241
|
+
try {
|
|
242
|
+
return unsafeWindow;
|
|
243
|
+
} catch (e) {
|
|
244
|
+
return window;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function insertAfter(beforeElement, afterElement) {
|
|
248
|
+
var _a;
|
|
249
|
+
(_a = beforeElement.parentNode) == null ? void 0 : _a.insertBefore(afterElement, beforeElement.nextSibling);
|
|
250
|
+
return afterElement;
|
|
251
|
+
}
|
|
252
|
+
function addParent(element, newParent) {
|
|
253
|
+
const oldParent = element.parentNode;
|
|
254
|
+
if (!oldParent)
|
|
255
|
+
throw new Error("Element doesn't have a parent node");
|
|
256
|
+
oldParent.replaceChild(newParent, element);
|
|
257
|
+
newParent.appendChild(element);
|
|
258
|
+
return newParent;
|
|
259
|
+
}
|
|
260
|
+
function addGlobalStyle(style) {
|
|
261
|
+
const styleElem = document.createElement("style");
|
|
262
|
+
styleElem.innerHTML = style;
|
|
263
|
+
document.head.appendChild(styleElem);
|
|
264
|
+
}
|
|
265
|
+
function preloadImages(srcUrls, rejects = false) {
|
|
266
|
+
const promises = srcUrls.map((src) => new Promise((res, rej) => {
|
|
267
|
+
const image = new Image();
|
|
268
|
+
image.src = src;
|
|
269
|
+
image.addEventListener("load", () => res(image));
|
|
270
|
+
image.addEventListener("error", (evt) => rejects && rej(evt));
|
|
271
|
+
}));
|
|
272
|
+
return Promise.allSettled(promises);
|
|
273
|
+
}
|
|
274
|
+
function openInNewTab(href) {
|
|
275
|
+
const openElem = document.createElement("a");
|
|
276
|
+
Object.assign(openElem, {
|
|
277
|
+
className: "userutils-open-in-new-tab",
|
|
278
|
+
target: "_blank",
|
|
279
|
+
rel: "noopener noreferrer",
|
|
280
|
+
href
|
|
281
|
+
});
|
|
282
|
+
openElem.style.display = "none";
|
|
283
|
+
document.body.appendChild(openElem);
|
|
284
|
+
openElem.click();
|
|
285
|
+
setTimeout(openElem.remove, 50);
|
|
286
|
+
}
|
|
287
|
+
function interceptEvent(eventObject, eventName, predicate) {
|
|
288
|
+
if (typeof Error.stackTraceLimit === "number" && Error.stackTraceLimit < 1e3) {
|
|
289
|
+
Error.stackTraceLimit = 1e3;
|
|
290
|
+
}
|
|
291
|
+
(function(original) {
|
|
292
|
+
eventObject.__proto__.addEventListener = function(...args) {
|
|
293
|
+
var _a, _b;
|
|
294
|
+
const origListener = typeof args[1] === "function" ? args[1] : (_b = (_a = args[1]) == null ? void 0 : _a.handleEvent) != null ? _b : () => void 0;
|
|
295
|
+
args[1] = function(...a) {
|
|
296
|
+
if (args[0] === eventName && predicate(Array.isArray(a) ? a[0] : a))
|
|
297
|
+
return;
|
|
298
|
+
else
|
|
299
|
+
return origListener.apply(this, a);
|
|
300
|
+
};
|
|
301
|
+
original.apply(this, args);
|
|
302
|
+
};
|
|
303
|
+
})(eventObject.__proto__.addEventListener);
|
|
304
|
+
}
|
|
305
|
+
function interceptWindowEvent(eventName, predicate) {
|
|
306
|
+
return interceptEvent(getUnsafeWindow(), eventName, predicate);
|
|
307
|
+
}
|
|
308
|
+
function amplifyMedia(mediaElement, multiplier = 1) {
|
|
309
|
+
const context = new (window.AudioContext || window.webkitAudioContext)();
|
|
310
|
+
const result = {
|
|
311
|
+
mediaElement,
|
|
312
|
+
amplify: (multiplier2) => {
|
|
313
|
+
result.gain.gain.value = multiplier2;
|
|
314
|
+
},
|
|
315
|
+
getAmpLevel: () => result.gain.gain.value,
|
|
316
|
+
context,
|
|
317
|
+
source: context.createMediaElementSource(mediaElement),
|
|
318
|
+
gain: context.createGain()
|
|
319
|
+
};
|
|
320
|
+
result.source.connect(result.gain);
|
|
321
|
+
result.gain.connect(context.destination);
|
|
322
|
+
result.amplify(multiplier);
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
325
|
+
function isScrollable(element) {
|
|
326
|
+
const { overflowX, overflowY } = getComputedStyle(element);
|
|
327
|
+
return {
|
|
328
|
+
vertical: (overflowY === "scroll" || overflowY === "auto") && element.scrollHeight > element.clientHeight,
|
|
329
|
+
horizontal: (overflowX === "scroll" || overflowX === "auto") && element.scrollWidth > element.clientWidth
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// lib/misc.ts
|
|
334
|
+
function autoPlural(word, num) {
|
|
335
|
+
if (Array.isArray(num) || num instanceof NodeList)
|
|
336
|
+
num = num.length;
|
|
337
|
+
return `${word}${num === 1 ? "" : "s"}`;
|
|
338
|
+
}
|
|
339
|
+
function pauseFor(time) {
|
|
340
|
+
return new Promise((res) => {
|
|
341
|
+
setTimeout(() => res(), time);
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function debounce(func, timeout = 300) {
|
|
345
|
+
let timer;
|
|
346
|
+
return function(...args) {
|
|
347
|
+
clearTimeout(timer);
|
|
348
|
+
timer = setTimeout(() => func.apply(this, args), timeout);
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function fetchAdvanced(_0) {
|
|
352
|
+
return __async(this, arguments, function* (url, options = {}) {
|
|
353
|
+
const { timeout = 1e4 } = options;
|
|
354
|
+
const controller = new AbortController();
|
|
355
|
+
const id = setTimeout(() => controller.abort(), timeout);
|
|
356
|
+
const res = yield fetch(url, __spreadProps(__spreadValues({}, options), {
|
|
357
|
+
signal: controller.signal
|
|
358
|
+
}));
|
|
359
|
+
clearTimeout(id);
|
|
360
|
+
return res;
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// lib/onSelector.ts
|
|
365
|
+
var selectorMap = /* @__PURE__ */ new Map();
|
|
366
|
+
function onSelector(selector, options) {
|
|
367
|
+
let selectorMapItems = [];
|
|
368
|
+
if (selectorMap.has(selector))
|
|
369
|
+
selectorMapItems = selectorMap.get(selector);
|
|
370
|
+
selectorMapItems.push(options);
|
|
371
|
+
selectorMap.set(selector, selectorMapItems);
|
|
372
|
+
checkSelectorExists(selector, selectorMapItems);
|
|
373
|
+
}
|
|
374
|
+
function removeOnSelector(selector) {
|
|
375
|
+
return selectorMap.delete(selector);
|
|
376
|
+
}
|
|
377
|
+
function checkSelectorExists(selector, options) {
|
|
378
|
+
const deleteIndices = [];
|
|
379
|
+
options.forEach((option, i) => {
|
|
380
|
+
try {
|
|
381
|
+
const elements = option.all ? document.querySelectorAll(selector) : document.querySelector(selector);
|
|
382
|
+
if (elements !== null && elements instanceof NodeList && elements.length > 0 || elements !== null) {
|
|
383
|
+
option.listener(elements);
|
|
384
|
+
if (!option.continuous)
|
|
385
|
+
deleteIndices.push(i);
|
|
386
|
+
}
|
|
387
|
+
} catch (err) {
|
|
388
|
+
console.error(`Couldn't call listener for selector '${selector}'`, err);
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
if (deleteIndices.length > 0) {
|
|
392
|
+
const newOptsArray = options.filter((_, i) => !deleteIndices.includes(i));
|
|
393
|
+
if (newOptsArray.length === 0)
|
|
394
|
+
selectorMap.delete(selector);
|
|
395
|
+
else {
|
|
396
|
+
selectorMap.set(selector, newOptsArray);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function initOnSelector(options = {}) {
|
|
401
|
+
const observer = new MutationObserver(() => {
|
|
402
|
+
for (const [selector, options2] of selectorMap.entries())
|
|
403
|
+
checkSelectorExists(selector, options2);
|
|
404
|
+
});
|
|
405
|
+
observer.observe(document.body, __spreadValues({
|
|
406
|
+
subtree: true,
|
|
407
|
+
childList: true
|
|
408
|
+
}, options));
|
|
409
|
+
}
|
|
410
|
+
function getSelectorMap() {
|
|
411
|
+
return selectorMap;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
exports.ConfigManager = ConfigManager;
|
|
415
|
+
exports.addGlobalStyle = addGlobalStyle;
|
|
416
|
+
exports.addParent = addParent;
|
|
417
|
+
exports.amplifyMedia = amplifyMedia;
|
|
418
|
+
exports.autoPlural = autoPlural;
|
|
419
|
+
exports.clamp = clamp;
|
|
420
|
+
exports.debounce = debounce;
|
|
421
|
+
exports.fetchAdvanced = fetchAdvanced;
|
|
422
|
+
exports.getSelectorMap = getSelectorMap;
|
|
423
|
+
exports.getUnsafeWindow = getUnsafeWindow;
|
|
424
|
+
exports.initOnSelector = initOnSelector;
|
|
425
|
+
exports.insertAfter = insertAfter;
|
|
426
|
+
exports.interceptEvent = interceptEvent;
|
|
427
|
+
exports.interceptWindowEvent = interceptWindowEvent;
|
|
428
|
+
exports.isScrollable = isScrollable;
|
|
429
|
+
exports.mapRange = mapRange;
|
|
430
|
+
exports.onSelector = onSelector;
|
|
431
|
+
exports.openInNewTab = openInNewTab;
|
|
432
|
+
exports.pauseFor = pauseFor;
|
|
433
|
+
exports.preloadImages = preloadImages;
|
|
434
|
+
exports.randRange = randRange;
|
|
435
|
+
exports.randomItem = randomItem;
|
|
436
|
+
exports.randomItemIndex = randomItemIndex;
|
|
437
|
+
exports.randomizeArray = randomizeArray;
|
|
438
|
+
exports.removeOnSelector = removeOnSelector;
|
|
439
|
+
exports.takeRandomItem = takeRandomItem;
|
|
440
|
+
|
|
441
|
+
return exports;
|
|
442
|
+
|
|
443
|
+
})({});
|