@sv443-network/userutils 6.3.0 → 7.0.0
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 +17 -0
- package/README.md +308 -127
- package/dist/index.global.js +236 -59
- package/dist/index.js +235 -58
- package/dist/index.mjs +234 -58
- package/dist/lib/DataStore.d.ts +46 -17
- package/dist/lib/DataStoreSerializer.d.ts +44 -0
- package/dist/lib/SelectorObserver.d.ts +10 -5
- package/dist/lib/dom.d.ts +3 -11
- package/dist/lib/index.d.ts +9 -7
- package/dist/lib/math.d.ts +0 -8
- package/dist/lib/misc.d.ts +19 -18
- package/dist/lib/translation.d.ts +1 -1
- package/dist/lib/types.d.ts +17 -0
- package/package.json +2 -1
package/dist/index.global.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// ==UserLibrary==
|
|
9
9
|
// @name UserUtils
|
|
10
10
|
// @description Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and more
|
|
11
|
-
// @version
|
|
11
|
+
// @version 7.0.0
|
|
12
12
|
// @license MIT
|
|
13
13
|
// @copyright Sv443 (https://github.com/Sv443)
|
|
14
14
|
|
|
@@ -99,14 +99,6 @@ var UserUtils = (function (exports) {
|
|
|
99
99
|
throw new TypeError(`Parameter "min" can't be bigger than "max"`);
|
|
100
100
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
101
101
|
}
|
|
102
|
-
function randomId(length = 16, radix = 16) {
|
|
103
|
-
const arr = new Uint8Array(length);
|
|
104
|
-
crypto.getRandomValues(arr);
|
|
105
|
-
return Array.from(
|
|
106
|
-
arr,
|
|
107
|
-
(v) => mapRange(v, 0, 255, 0, radix).toString(radix).substring(0, 1)
|
|
108
|
-
).join("");
|
|
109
|
-
}
|
|
110
102
|
|
|
111
103
|
// lib/array.ts
|
|
112
104
|
function randomItem(array) {
|
|
@@ -142,28 +134,32 @@ var UserUtils = (function (exports) {
|
|
|
142
134
|
* Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
|
|
143
135
|
* Supports migrating data from older versions to newer ones and populating the cache with default data if no persistent data is found.
|
|
144
136
|
*
|
|
145
|
-
* ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue`
|
|
137
|
+
* ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue` if the storageMethod is left as the default of `"GM"`
|
|
146
138
|
* ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultData`
|
|
147
139
|
*
|
|
148
|
-
* @template TData The type of the data that is saved in persistent storage (will be automatically inferred from `
|
|
140
|
+
* @template TData The type of the data that is saved in persistent storage for the currently set format version (will be automatically inferred from `defaultData` if not provided) - **This has to be a JSON-compatible object!** (no undefined, circular references, etc.)
|
|
149
141
|
* @param options The options for this DataStore instance
|
|
150
|
-
|
|
142
|
+
*/
|
|
151
143
|
constructor(options) {
|
|
152
144
|
__publicField(this, "id");
|
|
153
145
|
__publicField(this, "formatVersion");
|
|
154
146
|
__publicField(this, "defaultData");
|
|
155
|
-
__publicField(this, "cachedData");
|
|
156
|
-
__publicField(this, "migrations");
|
|
157
147
|
__publicField(this, "encodeData");
|
|
158
148
|
__publicField(this, "decodeData");
|
|
149
|
+
__publicField(this, "storageMethod");
|
|
150
|
+
__publicField(this, "cachedData");
|
|
151
|
+
__publicField(this, "migrations");
|
|
152
|
+
var _a;
|
|
159
153
|
this.id = options.id;
|
|
160
154
|
this.formatVersion = options.formatVersion;
|
|
161
155
|
this.defaultData = options.defaultData;
|
|
162
156
|
this.cachedData = options.defaultData;
|
|
163
157
|
this.migrations = options.migrations;
|
|
158
|
+
this.storageMethod = (_a = options.storageMethod) != null ? _a : "GM";
|
|
164
159
|
this.encodeData = options.encodeData;
|
|
165
160
|
this.decodeData = options.decodeData;
|
|
166
161
|
}
|
|
162
|
+
//#region public
|
|
167
163
|
/**
|
|
168
164
|
* Loads the data saved in persistent storage into the in-memory cache and also returns it.
|
|
169
165
|
* Automatically populates persistent storage with default data if it doesn't contain any data yet.
|
|
@@ -172,19 +168,25 @@ var UserUtils = (function (exports) {
|
|
|
172
168
|
loadData() {
|
|
173
169
|
return __async(this, null, function* () {
|
|
174
170
|
try {
|
|
175
|
-
const gmData = yield
|
|
176
|
-
let gmFmtVer = Number(yield
|
|
171
|
+
const gmData = yield this.getValue(`_uucfg-${this.id}`, JSON.stringify(this.defaultData));
|
|
172
|
+
let gmFmtVer = Number(yield this.getValue(`_uucfgver-${this.id}`, NaN));
|
|
177
173
|
if (typeof gmData !== "string") {
|
|
178
174
|
yield this.saveDefaultData();
|
|
179
175
|
return __spreadValues({}, this.defaultData);
|
|
180
176
|
}
|
|
181
|
-
const isEncoded = yield
|
|
182
|
-
|
|
183
|
-
|
|
177
|
+
const isEncoded = Boolean(yield this.getValue(`_uucfgenc-${this.id}`, false));
|
|
178
|
+
let saveData = false;
|
|
179
|
+
if (isNaN(gmFmtVer)) {
|
|
180
|
+
yield this.setValue(`_uucfgver-${this.id}`, gmFmtVer = this.formatVersion);
|
|
181
|
+
saveData = true;
|
|
182
|
+
}
|
|
184
183
|
let parsed = yield this.deserializeData(gmData, isEncoded);
|
|
185
184
|
if (gmFmtVer < this.formatVersion && this.migrations)
|
|
186
185
|
parsed = yield this.runMigrations(parsed, gmFmtVer);
|
|
187
|
-
|
|
186
|
+
if (saveData)
|
|
187
|
+
yield this.setData(parsed);
|
|
188
|
+
this.cachedData = __spreadValues({}, parsed);
|
|
189
|
+
return this.cachedData;
|
|
188
190
|
} catch (err) {
|
|
189
191
|
console.warn("Error while parsing JSON data, resetting it to the default value.", err);
|
|
190
192
|
yield this.saveDefaultData();
|
|
@@ -195,19 +197,20 @@ var UserUtils = (function (exports) {
|
|
|
195
197
|
/**
|
|
196
198
|
* Returns a copy of the data from the in-memory cache.
|
|
197
199
|
* Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).
|
|
200
|
+
* @param deepCopy Whether to return a deep copy of the data (default: `false`) - only necessary if your data object is nested and may have a bigger performance impact if enabled
|
|
198
201
|
*/
|
|
199
|
-
getData() {
|
|
200
|
-
return this.deepCopy(this.cachedData);
|
|
202
|
+
getData(deepCopy = false) {
|
|
203
|
+
return deepCopy ? this.deepCopy(this.cachedData) : __spreadValues({}, this.cachedData);
|
|
201
204
|
}
|
|
202
205
|
/** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
|
|
203
206
|
setData(data) {
|
|
204
207
|
this.cachedData = data;
|
|
205
|
-
const useEncoding =
|
|
208
|
+
const useEncoding = this.encodingEnabled();
|
|
206
209
|
return new Promise((resolve) => __async(this, null, function* () {
|
|
207
210
|
yield Promise.all([
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
+
this.setValue(`_uucfg-${this.id}`, yield this.serializeData(data, useEncoding)),
|
|
212
|
+
this.setValue(`_uucfgver-${this.id}`, this.formatVersion),
|
|
213
|
+
this.setValue(`_uucfgenc-${this.id}`, useEncoding)
|
|
211
214
|
]);
|
|
212
215
|
resolve();
|
|
213
216
|
}));
|
|
@@ -216,12 +219,12 @@ var UserUtils = (function (exports) {
|
|
|
216
219
|
saveDefaultData() {
|
|
217
220
|
return __async(this, null, function* () {
|
|
218
221
|
this.cachedData = this.defaultData;
|
|
219
|
-
const useEncoding =
|
|
222
|
+
const useEncoding = this.encodingEnabled();
|
|
220
223
|
return new Promise((resolve) => __async(this, null, function* () {
|
|
221
224
|
yield Promise.all([
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
+
this.setValue(`_uucfg-${this.id}`, yield this.serializeData(this.defaultData, useEncoding)),
|
|
226
|
+
this.setValue(`_uucfgver-${this.id}`, this.formatVersion),
|
|
227
|
+
this.setValue(`_uucfgenc-${this.id}`, useEncoding)
|
|
225
228
|
]);
|
|
226
229
|
resolve();
|
|
227
230
|
}));
|
|
@@ -237,14 +240,25 @@ var UserUtils = (function (exports) {
|
|
|
237
240
|
deleteData() {
|
|
238
241
|
return __async(this, null, function* () {
|
|
239
242
|
yield Promise.all([
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
+
this.deleteValue(`_uucfg-${this.id}`),
|
|
244
|
+
this.deleteValue(`_uucfgver-${this.id}`),
|
|
245
|
+
this.deleteValue(`_uucfgenc-${this.id}`)
|
|
243
246
|
]);
|
|
244
247
|
});
|
|
245
248
|
}
|
|
246
|
-
/**
|
|
247
|
-
|
|
249
|
+
/** Returns whether encoding and decoding are enabled for this DataStore instance */
|
|
250
|
+
encodingEnabled() {
|
|
251
|
+
return Boolean(this.encodeData && this.decodeData);
|
|
252
|
+
}
|
|
253
|
+
//#region migrations
|
|
254
|
+
/**
|
|
255
|
+
* Runs all necessary migration functions consecutively and saves the result to the in-memory cache and persistent storage and also returns it.
|
|
256
|
+
* This method is automatically called by {@linkcode loadData()} if the data format has changed since the last time the data was saved.
|
|
257
|
+
* Though calling this method manually is not necessary, it can be useful if you want to run migrations for special occasions like a user importing potentially outdated data that has been previously exported.
|
|
258
|
+
*
|
|
259
|
+
* If one of the migrations fails, the data will be reset to the default value if `resetOnError` is set to `true` (default). Otherwise, an error will be thrown and no data will be saved.
|
|
260
|
+
*/
|
|
261
|
+
runMigrations(oldData, oldFmtVer, resetOnError = true) {
|
|
248
262
|
return __async(this, null, function* () {
|
|
249
263
|
if (!this.migrations)
|
|
250
264
|
return oldData;
|
|
@@ -259,6 +273,8 @@ var UserUtils = (function (exports) {
|
|
|
259
273
|
newData = migRes instanceof Promise ? yield migRes : migRes;
|
|
260
274
|
lastFmtVer = oldFmtVer = ver;
|
|
261
275
|
} catch (err) {
|
|
276
|
+
if (!resetOnError)
|
|
277
|
+
throw new Error(`Error while running migration function for format version '${fmtVer}'`);
|
|
262
278
|
console.error(`Error while running migration function for format version '${fmtVer}' - resetting to the default value.`, err);
|
|
263
279
|
yield this.saveDefaultData();
|
|
264
280
|
return this.getData();
|
|
@@ -266,18 +282,19 @@ var UserUtils = (function (exports) {
|
|
|
266
282
|
}
|
|
267
283
|
}
|
|
268
284
|
yield Promise.all([
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
285
|
+
this.setValue(`_uucfg-${this.id}`, yield this.serializeData(newData)),
|
|
286
|
+
this.setValue(`_uucfgver-${this.id}`, lastFmtVer),
|
|
287
|
+
this.setValue(`_uucfgenc-${this.id}`, this.encodingEnabled())
|
|
272
288
|
]);
|
|
273
|
-
return newData;
|
|
289
|
+
return this.cachedData = __spreadValues({}, newData);
|
|
274
290
|
});
|
|
275
291
|
}
|
|
292
|
+
//#region serialization
|
|
276
293
|
/** Serializes the data using the optional this.encodeData() and returns it as a string */
|
|
277
294
|
serializeData(data, useEncoding = true) {
|
|
278
295
|
return __async(this, null, function* () {
|
|
279
296
|
const stringData = JSON.stringify(data);
|
|
280
|
-
if (!this.
|
|
297
|
+
if (!this.encodingEnabled() || !useEncoding)
|
|
281
298
|
return stringData;
|
|
282
299
|
const encRes = this.encodeData(stringData);
|
|
283
300
|
if (encRes instanceof Promise)
|
|
@@ -288,16 +305,131 @@ var UserUtils = (function (exports) {
|
|
|
288
305
|
/** Deserializes the data using the optional this.decodeData() and returns it as a JSON object */
|
|
289
306
|
deserializeData(data, useEncoding = true) {
|
|
290
307
|
return __async(this, null, function* () {
|
|
291
|
-
let decRes = this.
|
|
308
|
+
let decRes = this.encodingEnabled() && useEncoding ? this.decodeData(data) : void 0;
|
|
292
309
|
if (decRes instanceof Promise)
|
|
293
310
|
decRes = yield decRes;
|
|
294
311
|
return JSON.parse(decRes != null ? decRes : data);
|
|
295
312
|
});
|
|
296
313
|
}
|
|
297
|
-
|
|
314
|
+
//#region misc
|
|
315
|
+
/** Copies a JSON-compatible object and loses all its internal references in the process */
|
|
298
316
|
deepCopy(obj) {
|
|
299
317
|
return JSON.parse(JSON.stringify(obj));
|
|
300
318
|
}
|
|
319
|
+
//#region storage
|
|
320
|
+
/** Gets a value from persistent storage - can be overwritten in a subclass if you want to use something other than GM storage */
|
|
321
|
+
getValue(name, defaultValue) {
|
|
322
|
+
return __async(this, null, function* () {
|
|
323
|
+
var _a, _b;
|
|
324
|
+
switch (this.storageMethod) {
|
|
325
|
+
case "localStorage":
|
|
326
|
+
return (_a = localStorage.getItem(name)) != null ? _a : defaultValue;
|
|
327
|
+
case "sessionStorage":
|
|
328
|
+
return (_b = sessionStorage.getItem(name)) != null ? _b : defaultValue;
|
|
329
|
+
default:
|
|
330
|
+
return GM.getValue(name, defaultValue);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Sets a value in persistent storage - can be overwritten in a subclass if you want to use something other than GM storage.
|
|
336
|
+
* The default storage engines will stringify all passed values like numbers or booleans, so be aware of that.
|
|
337
|
+
*/
|
|
338
|
+
setValue(name, value) {
|
|
339
|
+
return __async(this, null, function* () {
|
|
340
|
+
switch (this.storageMethod) {
|
|
341
|
+
case "localStorage":
|
|
342
|
+
return localStorage.setItem(name, String(value));
|
|
343
|
+
case "sessionStorage":
|
|
344
|
+
return sessionStorage.setItem(name, String(value));
|
|
345
|
+
default:
|
|
346
|
+
return GM.setValue(name, String(value));
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
/** Deletes a value from persistent storage - can be overwritten in a subclass if you want to use something other than GM storage */
|
|
351
|
+
deleteValue(name) {
|
|
352
|
+
return __async(this, null, function* () {
|
|
353
|
+
switch (this.storageMethod) {
|
|
354
|
+
case "localStorage":
|
|
355
|
+
return localStorage.removeItem(name);
|
|
356
|
+
case "sessionStorage":
|
|
357
|
+
return sessionStorage.removeItem(name);
|
|
358
|
+
default:
|
|
359
|
+
return GM.deleteValue(name);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// lib/DataStoreSerializer.ts
|
|
366
|
+
var DataStoreSerializer = class {
|
|
367
|
+
constructor(stores, options = {}) {
|
|
368
|
+
__publicField(this, "stores");
|
|
369
|
+
__publicField(this, "options");
|
|
370
|
+
if (!getUnsafeWindow().crypto || !getUnsafeWindow().crypto.subtle)
|
|
371
|
+
throw new Error("DataStoreSerializer has to run in a secure context (HTTPS)!");
|
|
372
|
+
this.stores = stores;
|
|
373
|
+
this.options = __spreadValues({
|
|
374
|
+
addChecksum: true,
|
|
375
|
+
ensureIntegrity: true
|
|
376
|
+
}, options);
|
|
377
|
+
}
|
|
378
|
+
/** Calculates the checksum of a string */
|
|
379
|
+
calcChecksum(input) {
|
|
380
|
+
return __async(this, null, function* () {
|
|
381
|
+
return computeHash(input, "SHA-256");
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
/** Serializes a DataStore instance */
|
|
385
|
+
serializeStore(storeInst) {
|
|
386
|
+
return __async(this, null, function* () {
|
|
387
|
+
const data = storeInst.encodingEnabled() ? yield storeInst.encodeData(JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
|
|
388
|
+
const checksum = this.options.addChecksum ? yield this.calcChecksum(data) : void 0;
|
|
389
|
+
return {
|
|
390
|
+
id: storeInst.id,
|
|
391
|
+
data,
|
|
392
|
+
formatVersion: storeInst.formatVersion,
|
|
393
|
+
encoded: storeInst.encodingEnabled(),
|
|
394
|
+
checksum
|
|
395
|
+
};
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
/** Serializes the data stores into a string */
|
|
399
|
+
serialize() {
|
|
400
|
+
return __async(this, null, function* () {
|
|
401
|
+
const serData = [];
|
|
402
|
+
for (const store of this.stores)
|
|
403
|
+
serData.push(yield this.serializeStore(store));
|
|
404
|
+
return JSON.stringify(serData);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Deserializes the data exported via {@linkcode serialize()} and imports it into the DataStore instances.
|
|
409
|
+
* Also triggers the migration process if the data format has changed.
|
|
410
|
+
*/
|
|
411
|
+
deserialize(serializedData) {
|
|
412
|
+
return __async(this, null, function* () {
|
|
413
|
+
const deserStores = JSON.parse(serializedData);
|
|
414
|
+
for (const storeData of deserStores) {
|
|
415
|
+
const storeInst = this.stores.find((s) => s.id === storeData.id);
|
|
416
|
+
if (!storeInst)
|
|
417
|
+
throw new Error(`DataStore instance with ID "${storeData.id}" not found! Make sure to provide it in the DataStoreSerializer constructor.`);
|
|
418
|
+
if (this.options.ensureIntegrity && typeof storeData.checksum === "string") {
|
|
419
|
+
const checksum = yield this.calcChecksum(storeData.data);
|
|
420
|
+
if (checksum !== storeData.checksum)
|
|
421
|
+
throw new Error(`Checksum mismatch for DataStore with ID "${storeData.id}"!
|
|
422
|
+
Expected: ${storeData.checksum}
|
|
423
|
+
Has: ${checksum}`);
|
|
424
|
+
}
|
|
425
|
+
const decodedData = storeData.encoded && storeInst.encodingEnabled() ? yield storeInst.decodeData(storeData.data) : storeData.data;
|
|
426
|
+
if (storeData.formatVersion && !isNaN(Number(storeData.formatVersion)) && Number(storeData.formatVersion) < storeInst.formatVersion)
|
|
427
|
+
yield storeInst.runMigrations(JSON.parse(decodedData), Number(storeData.formatVersion), false);
|
|
428
|
+
else
|
|
429
|
+
yield storeInst.setData(JSON.parse(decodedData));
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
301
433
|
};
|
|
302
434
|
|
|
303
435
|
// lib/dom.ts
|
|
@@ -308,11 +440,6 @@ var UserUtils = (function (exports) {
|
|
|
308
440
|
return window;
|
|
309
441
|
}
|
|
310
442
|
}
|
|
311
|
-
function insertAfter(beforeElement, afterElement) {
|
|
312
|
-
var _a;
|
|
313
|
-
(_a = beforeElement.parentNode) == null ? void 0 : _a.insertBefore(afterElement, beforeElement.nextSibling);
|
|
314
|
-
return afterElement;
|
|
315
|
-
}
|
|
316
443
|
function addParent(element, newParent) {
|
|
317
444
|
const oldParent = element.parentNode;
|
|
318
445
|
if (!oldParent)
|
|
@@ -459,9 +586,14 @@ var UserUtils = (function (exports) {
|
|
|
459
586
|
id = setTimeout(() => controller.abort(), timeout);
|
|
460
587
|
signalOpts = { signal: controller.signal };
|
|
461
588
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
589
|
+
try {
|
|
590
|
+
const res = yield fetch(input, __spreadValues(__spreadValues({}, options), signalOpts));
|
|
591
|
+
id && clearTimeout(id);
|
|
592
|
+
return res;
|
|
593
|
+
} catch (err) {
|
|
594
|
+
id && clearTimeout(id);
|
|
595
|
+
throw err;
|
|
596
|
+
}
|
|
465
597
|
});
|
|
466
598
|
}
|
|
467
599
|
function insertValues(input, ...values) {
|
|
@@ -501,8 +633,38 @@ var UserUtils = (function (exports) {
|
|
|
501
633
|
function str2ab(str) {
|
|
502
634
|
return Uint8Array.from(getUnsafeWindow().atob(str), (c) => c.charCodeAt(0));
|
|
503
635
|
}
|
|
636
|
+
function computeHash(input, algorithm = "SHA-256") {
|
|
637
|
+
return __async(this, null, function* () {
|
|
638
|
+
let data;
|
|
639
|
+
if (typeof input === "string") {
|
|
640
|
+
const encoder = new TextEncoder();
|
|
641
|
+
data = encoder.encode(input);
|
|
642
|
+
} else
|
|
643
|
+
data = input;
|
|
644
|
+
const hashBuffer = yield crypto.subtle.digest(algorithm, data);
|
|
645
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
646
|
+
const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
647
|
+
return hashHex;
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
function randomId(length = 16, radix = 16, enhancedEntropy = false) {
|
|
651
|
+
if (enhancedEntropy) {
|
|
652
|
+
const arr = new Uint8Array(length);
|
|
653
|
+
crypto.getRandomValues(arr);
|
|
654
|
+
return Array.from(
|
|
655
|
+
arr,
|
|
656
|
+
(v) => mapRange(v, 0, 255, 0, radix).toString(radix).substring(0, 1)
|
|
657
|
+
).join("");
|
|
658
|
+
}
|
|
659
|
+
return Array.from(
|
|
660
|
+
{ length },
|
|
661
|
+
() => Math.floor(Math.random() * radix).toString(radix)
|
|
662
|
+
).join("");
|
|
663
|
+
}
|
|
504
664
|
|
|
505
665
|
// lib/SelectorObserver.ts
|
|
666
|
+
var domLoaded = false;
|
|
667
|
+
document.addEventListener("DOMContentLoaded", () => domLoaded = true);
|
|
506
668
|
var SelectorObserver = class {
|
|
507
669
|
constructor(baseElement, options = {}) {
|
|
508
670
|
__publicField(this, "enabled", false);
|
|
@@ -513,7 +675,6 @@ var UserUtils = (function (exports) {
|
|
|
513
675
|
__publicField(this, "listenerMap");
|
|
514
676
|
this.baseElement = baseElement;
|
|
515
677
|
this.listenerMap = /* @__PURE__ */ new Map();
|
|
516
|
-
this.observer = new MutationObserver(() => this.checkAllSelectors());
|
|
517
678
|
const _a = options, {
|
|
518
679
|
defaultDebounce,
|
|
519
680
|
defaultDebounceEdge,
|
|
@@ -535,11 +696,21 @@ var UserUtils = (function (exports) {
|
|
|
535
696
|
disableOnNoListeners: disableOnNoListeners != null ? disableOnNoListeners : false,
|
|
536
697
|
enableOnAddListener: enableOnAddListener != null ? enableOnAddListener : true
|
|
537
698
|
};
|
|
699
|
+
if (typeof this.customOptions.checkInterval !== "number") {
|
|
700
|
+
this.observer = new MutationObserver(() => this.checkAllSelectors());
|
|
701
|
+
} else {
|
|
702
|
+
this.checkAllSelectors();
|
|
703
|
+
setInterval(() => this.checkAllSelectors(), this.customOptions.checkInterval);
|
|
704
|
+
}
|
|
538
705
|
}
|
|
706
|
+
/** Call to check all selectors in the {@linkcode listenerMap} using {@linkcode checkSelector()} */
|
|
539
707
|
checkAllSelectors() {
|
|
708
|
+
if (!this.enabled || !domLoaded)
|
|
709
|
+
return;
|
|
540
710
|
for (const [selector, listeners] of this.listenerMap.entries())
|
|
541
711
|
this.checkSelector(selector, listeners);
|
|
542
712
|
}
|
|
713
|
+
/** Checks if the element(s) with the given {@linkcode selector} exist in the DOM and calls the respective {@linkcode listeners} accordingly */
|
|
543
714
|
checkSelector(selector, listeners) {
|
|
544
715
|
var _a;
|
|
545
716
|
if (!this.enabled)
|
|
@@ -571,9 +742,6 @@ var UserUtils = (function (exports) {
|
|
|
571
742
|
this.disable();
|
|
572
743
|
}
|
|
573
744
|
}
|
|
574
|
-
debounce(func, time, edge = "falling") {
|
|
575
|
-
return debounce(func, time, edge);
|
|
576
|
-
}
|
|
577
745
|
/**
|
|
578
746
|
* Starts observing the children of the base element for changes to the given {@linkcode selector} according to the set {@linkcode options}
|
|
579
747
|
* @param selector The selector to observe
|
|
@@ -582,11 +750,16 @@ var UserUtils = (function (exports) {
|
|
|
582
750
|
* @param [options.all] Whether to use `querySelectorAll()` instead - default is false
|
|
583
751
|
* @param [options.continuous] Whether to call the listener continuously instead of just once - default is false
|
|
584
752
|
* @param [options.debounce] Whether to debounce the listener to reduce calls to `querySelector` or `querySelectorAll` - set undefined or <=0 to disable (default)
|
|
753
|
+
* @returns Returns a function that can be called to remove this listener more easily
|
|
585
754
|
*/
|
|
586
755
|
addListener(selector, options) {
|
|
587
|
-
options = __spreadValues({
|
|
756
|
+
options = __spreadValues({
|
|
757
|
+
all: false,
|
|
758
|
+
continuous: false,
|
|
759
|
+
debounce: 0
|
|
760
|
+
}, options);
|
|
588
761
|
if (options.debounce && options.debounce > 0 || this.customOptions.defaultDebounce && this.customOptions.defaultDebounce > 0) {
|
|
589
|
-
options.listener =
|
|
762
|
+
options.listener = debounce(
|
|
590
763
|
options.listener,
|
|
591
764
|
options.debounce || this.customOptions.defaultDebounce,
|
|
592
765
|
options.debounceEdge || this.customOptions.defaultDebounceEdge
|
|
@@ -599,13 +772,15 @@ var UserUtils = (function (exports) {
|
|
|
599
772
|
if (this.enabled === false && this.customOptions.enableOnAddListener)
|
|
600
773
|
this.enable();
|
|
601
774
|
this.checkSelector(selector, [options]);
|
|
775
|
+
return () => this.removeListener(selector, options);
|
|
602
776
|
}
|
|
603
777
|
/** Disables the observation of the child elements */
|
|
604
778
|
disable() {
|
|
779
|
+
var _a;
|
|
605
780
|
if (!this.enabled)
|
|
606
781
|
return;
|
|
607
782
|
this.enabled = false;
|
|
608
|
-
this.observer.disconnect();
|
|
783
|
+
(_a = this.observer) == null ? void 0 : _a.disconnect();
|
|
609
784
|
}
|
|
610
785
|
/**
|
|
611
786
|
* Enables or reenables the observation of the child elements.
|
|
@@ -613,11 +788,12 @@ var UserUtils = (function (exports) {
|
|
|
613
788
|
* @returns Returns true when the observation was enabled, false otherwise (e.g. when the base element wasn't found)
|
|
614
789
|
*/
|
|
615
790
|
enable(immediatelyCheckSelectors = true) {
|
|
791
|
+
var _a;
|
|
616
792
|
const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
|
|
617
793
|
if (this.enabled || !baseElement)
|
|
618
794
|
return false;
|
|
619
795
|
this.enabled = true;
|
|
620
|
-
this.observer.observe(baseElement, this.observerOptions);
|
|
796
|
+
(_a = this.observer) == null ? void 0 : _a.observe(baseElement, this.observerOptions);
|
|
621
797
|
if (immediatelyCheckSelectors)
|
|
622
798
|
this.checkAllSelectors();
|
|
623
799
|
return true;
|
|
@@ -688,18 +864,19 @@ var UserUtils = (function (exports) {
|
|
|
688
864
|
};
|
|
689
865
|
|
|
690
866
|
exports.DataStore = DataStore;
|
|
867
|
+
exports.DataStoreSerializer = DataStoreSerializer;
|
|
691
868
|
exports.SelectorObserver = SelectorObserver;
|
|
692
869
|
exports.addGlobalStyle = addGlobalStyle;
|
|
693
870
|
exports.addParent = addParent;
|
|
694
871
|
exports.autoPlural = autoPlural;
|
|
695
872
|
exports.clamp = clamp;
|
|
696
873
|
exports.compress = compress;
|
|
874
|
+
exports.computeHash = computeHash;
|
|
697
875
|
exports.debounce = debounce;
|
|
698
876
|
exports.decompress = decompress;
|
|
699
877
|
exports.fetchAdvanced = fetchAdvanced;
|
|
700
878
|
exports.getSiblingsFrame = getSiblingsFrame;
|
|
701
879
|
exports.getUnsafeWindow = getUnsafeWindow;
|
|
702
|
-
exports.insertAfter = insertAfter;
|
|
703
880
|
exports.insertValues = insertValues;
|
|
704
881
|
exports.interceptEvent = interceptEvent;
|
|
705
882
|
exports.interceptWindowEvent = interceptWindowEvent;
|