@sv443-network/userutils 6.2.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 +24 -0
- package/README.md +330 -139
- package/dist/index.global.js +253 -72
- package/dist/index.js +251 -70
- package/dist/index.mjs +250 -70
- 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 +8 -16
- 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 +3 -2
package/dist/index.js
CHANGED
|
@@ -79,14 +79,6 @@ function randRange(...args) {
|
|
|
79
79
|
throw new TypeError(`Parameter "min" can't be bigger than "max"`);
|
|
80
80
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
81
81
|
}
|
|
82
|
-
function randomId(length = 16, radix = 16) {
|
|
83
|
-
const arr = new Uint8Array(length);
|
|
84
|
-
crypto.getRandomValues(arr);
|
|
85
|
-
return Array.from(
|
|
86
|
-
arr,
|
|
87
|
-
(v) => mapRange(v, 0, 255, 0, radix).toString(radix).substring(0, 1)
|
|
88
|
-
).join("");
|
|
89
|
-
}
|
|
90
82
|
|
|
91
83
|
// lib/array.ts
|
|
92
84
|
function randomItem(array) {
|
|
@@ -122,28 +114,32 @@ var DataStore = class {
|
|
|
122
114
|
* Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
|
|
123
115
|
* Supports migrating data from older versions to newer ones and populating the cache with default data if no persistent data is found.
|
|
124
116
|
*
|
|
125
|
-
* ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue`
|
|
117
|
+
* ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue` if the storageMethod is left as the default of `"GM"`
|
|
126
118
|
* ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultData`
|
|
127
119
|
*
|
|
128
|
-
* @template TData The type of the data that is saved in persistent storage (will be automatically inferred from `
|
|
120
|
+
* @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.)
|
|
129
121
|
* @param options The options for this DataStore instance
|
|
130
|
-
|
|
122
|
+
*/
|
|
131
123
|
constructor(options) {
|
|
132
124
|
__publicField(this, "id");
|
|
133
125
|
__publicField(this, "formatVersion");
|
|
134
126
|
__publicField(this, "defaultData");
|
|
135
|
-
__publicField(this, "cachedData");
|
|
136
|
-
__publicField(this, "migrations");
|
|
137
127
|
__publicField(this, "encodeData");
|
|
138
128
|
__publicField(this, "decodeData");
|
|
129
|
+
__publicField(this, "storageMethod");
|
|
130
|
+
__publicField(this, "cachedData");
|
|
131
|
+
__publicField(this, "migrations");
|
|
132
|
+
var _a;
|
|
139
133
|
this.id = options.id;
|
|
140
134
|
this.formatVersion = options.formatVersion;
|
|
141
135
|
this.defaultData = options.defaultData;
|
|
142
136
|
this.cachedData = options.defaultData;
|
|
143
137
|
this.migrations = options.migrations;
|
|
138
|
+
this.storageMethod = (_a = options.storageMethod) != null ? _a : "GM";
|
|
144
139
|
this.encodeData = options.encodeData;
|
|
145
140
|
this.decodeData = options.decodeData;
|
|
146
141
|
}
|
|
142
|
+
//#region public
|
|
147
143
|
/**
|
|
148
144
|
* Loads the data saved in persistent storage into the in-memory cache and also returns it.
|
|
149
145
|
* Automatically populates persistent storage with default data if it doesn't contain any data yet.
|
|
@@ -152,19 +148,25 @@ var DataStore = class {
|
|
|
152
148
|
loadData() {
|
|
153
149
|
return __async(this, null, function* () {
|
|
154
150
|
try {
|
|
155
|
-
const gmData = yield
|
|
156
|
-
let gmFmtVer = Number(yield
|
|
151
|
+
const gmData = yield this.getValue(`_uucfg-${this.id}`, JSON.stringify(this.defaultData));
|
|
152
|
+
let gmFmtVer = Number(yield this.getValue(`_uucfgver-${this.id}`, NaN));
|
|
157
153
|
if (typeof gmData !== "string") {
|
|
158
154
|
yield this.saveDefaultData();
|
|
159
155
|
return __spreadValues({}, this.defaultData);
|
|
160
156
|
}
|
|
161
|
-
const isEncoded = yield
|
|
162
|
-
|
|
163
|
-
|
|
157
|
+
const isEncoded = Boolean(yield this.getValue(`_uucfgenc-${this.id}`, false));
|
|
158
|
+
let saveData = false;
|
|
159
|
+
if (isNaN(gmFmtVer)) {
|
|
160
|
+
yield this.setValue(`_uucfgver-${this.id}`, gmFmtVer = this.formatVersion);
|
|
161
|
+
saveData = true;
|
|
162
|
+
}
|
|
164
163
|
let parsed = yield this.deserializeData(gmData, isEncoded);
|
|
165
164
|
if (gmFmtVer < this.formatVersion && this.migrations)
|
|
166
165
|
parsed = yield this.runMigrations(parsed, gmFmtVer);
|
|
167
|
-
|
|
166
|
+
if (saveData)
|
|
167
|
+
yield this.setData(parsed);
|
|
168
|
+
this.cachedData = __spreadValues({}, parsed);
|
|
169
|
+
return this.cachedData;
|
|
168
170
|
} catch (err) {
|
|
169
171
|
console.warn("Error while parsing JSON data, resetting it to the default value.", err);
|
|
170
172
|
yield this.saveDefaultData();
|
|
@@ -175,19 +177,20 @@ var DataStore = class {
|
|
|
175
177
|
/**
|
|
176
178
|
* Returns a copy of the data from the in-memory cache.
|
|
177
179
|
* Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).
|
|
180
|
+
* @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
|
|
178
181
|
*/
|
|
179
|
-
getData() {
|
|
180
|
-
return this.deepCopy(this.cachedData);
|
|
182
|
+
getData(deepCopy = false) {
|
|
183
|
+
return deepCopy ? this.deepCopy(this.cachedData) : __spreadValues({}, this.cachedData);
|
|
181
184
|
}
|
|
182
185
|
/** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
|
|
183
186
|
setData(data) {
|
|
184
187
|
this.cachedData = data;
|
|
185
|
-
const useEncoding =
|
|
188
|
+
const useEncoding = this.encodingEnabled();
|
|
186
189
|
return new Promise((resolve) => __async(this, null, function* () {
|
|
187
190
|
yield Promise.all([
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
+
this.setValue(`_uucfg-${this.id}`, yield this.serializeData(data, useEncoding)),
|
|
192
|
+
this.setValue(`_uucfgver-${this.id}`, this.formatVersion),
|
|
193
|
+
this.setValue(`_uucfgenc-${this.id}`, useEncoding)
|
|
191
194
|
]);
|
|
192
195
|
resolve();
|
|
193
196
|
}));
|
|
@@ -196,12 +199,12 @@ var DataStore = class {
|
|
|
196
199
|
saveDefaultData() {
|
|
197
200
|
return __async(this, null, function* () {
|
|
198
201
|
this.cachedData = this.defaultData;
|
|
199
|
-
const useEncoding =
|
|
202
|
+
const useEncoding = this.encodingEnabled();
|
|
200
203
|
return new Promise((resolve) => __async(this, null, function* () {
|
|
201
204
|
yield Promise.all([
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
+
this.setValue(`_uucfg-${this.id}`, yield this.serializeData(this.defaultData, useEncoding)),
|
|
206
|
+
this.setValue(`_uucfgver-${this.id}`, this.formatVersion),
|
|
207
|
+
this.setValue(`_uucfgenc-${this.id}`, useEncoding)
|
|
205
208
|
]);
|
|
206
209
|
resolve();
|
|
207
210
|
}));
|
|
@@ -217,14 +220,25 @@ var DataStore = class {
|
|
|
217
220
|
deleteData() {
|
|
218
221
|
return __async(this, null, function* () {
|
|
219
222
|
yield Promise.all([
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
+
this.deleteValue(`_uucfg-${this.id}`),
|
|
224
|
+
this.deleteValue(`_uucfgver-${this.id}`),
|
|
225
|
+
this.deleteValue(`_uucfgenc-${this.id}`)
|
|
223
226
|
]);
|
|
224
227
|
});
|
|
225
228
|
}
|
|
226
|
-
/**
|
|
227
|
-
|
|
229
|
+
/** Returns whether encoding and decoding are enabled for this DataStore instance */
|
|
230
|
+
encodingEnabled() {
|
|
231
|
+
return Boolean(this.encodeData && this.decodeData);
|
|
232
|
+
}
|
|
233
|
+
//#region migrations
|
|
234
|
+
/**
|
|
235
|
+
* Runs all necessary migration functions consecutively and saves the result to the in-memory cache and persistent storage and also returns it.
|
|
236
|
+
* This method is automatically called by {@linkcode loadData()} if the data format has changed since the last time the data was saved.
|
|
237
|
+
* 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.
|
|
238
|
+
*
|
|
239
|
+
* 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.
|
|
240
|
+
*/
|
|
241
|
+
runMigrations(oldData, oldFmtVer, resetOnError = true) {
|
|
228
242
|
return __async(this, null, function* () {
|
|
229
243
|
if (!this.migrations)
|
|
230
244
|
return oldData;
|
|
@@ -239,6 +253,8 @@ var DataStore = class {
|
|
|
239
253
|
newData = migRes instanceof Promise ? yield migRes : migRes;
|
|
240
254
|
lastFmtVer = oldFmtVer = ver;
|
|
241
255
|
} catch (err) {
|
|
256
|
+
if (!resetOnError)
|
|
257
|
+
throw new Error(`Error while running migration function for format version '${fmtVer}'`);
|
|
242
258
|
console.error(`Error while running migration function for format version '${fmtVer}' - resetting to the default value.`, err);
|
|
243
259
|
yield this.saveDefaultData();
|
|
244
260
|
return this.getData();
|
|
@@ -246,18 +262,19 @@ var DataStore = class {
|
|
|
246
262
|
}
|
|
247
263
|
}
|
|
248
264
|
yield Promise.all([
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
265
|
+
this.setValue(`_uucfg-${this.id}`, yield this.serializeData(newData)),
|
|
266
|
+
this.setValue(`_uucfgver-${this.id}`, lastFmtVer),
|
|
267
|
+
this.setValue(`_uucfgenc-${this.id}`, this.encodingEnabled())
|
|
252
268
|
]);
|
|
253
|
-
return newData;
|
|
269
|
+
return this.cachedData = __spreadValues({}, newData);
|
|
254
270
|
});
|
|
255
271
|
}
|
|
272
|
+
//#region serialization
|
|
256
273
|
/** Serializes the data using the optional this.encodeData() and returns it as a string */
|
|
257
274
|
serializeData(data, useEncoding = true) {
|
|
258
275
|
return __async(this, null, function* () {
|
|
259
276
|
const stringData = JSON.stringify(data);
|
|
260
|
-
if (!this.
|
|
277
|
+
if (!this.encodingEnabled() || !useEncoding)
|
|
261
278
|
return stringData;
|
|
262
279
|
const encRes = this.encodeData(stringData);
|
|
263
280
|
if (encRes instanceof Promise)
|
|
@@ -268,16 +285,131 @@ var DataStore = class {
|
|
|
268
285
|
/** Deserializes the data using the optional this.decodeData() and returns it as a JSON object */
|
|
269
286
|
deserializeData(data, useEncoding = true) {
|
|
270
287
|
return __async(this, null, function* () {
|
|
271
|
-
let decRes = this.
|
|
288
|
+
let decRes = this.encodingEnabled() && useEncoding ? this.decodeData(data) : void 0;
|
|
272
289
|
if (decRes instanceof Promise)
|
|
273
290
|
decRes = yield decRes;
|
|
274
291
|
return JSON.parse(decRes != null ? decRes : data);
|
|
275
292
|
});
|
|
276
293
|
}
|
|
277
|
-
|
|
294
|
+
//#region misc
|
|
295
|
+
/** Copies a JSON-compatible object and loses all its internal references in the process */
|
|
278
296
|
deepCopy(obj) {
|
|
279
297
|
return JSON.parse(JSON.stringify(obj));
|
|
280
298
|
}
|
|
299
|
+
//#region storage
|
|
300
|
+
/** Gets a value from persistent storage - can be overwritten in a subclass if you want to use something other than GM storage */
|
|
301
|
+
getValue(name, defaultValue) {
|
|
302
|
+
return __async(this, null, function* () {
|
|
303
|
+
var _a, _b;
|
|
304
|
+
switch (this.storageMethod) {
|
|
305
|
+
case "localStorage":
|
|
306
|
+
return (_a = localStorage.getItem(name)) != null ? _a : defaultValue;
|
|
307
|
+
case "sessionStorage":
|
|
308
|
+
return (_b = sessionStorage.getItem(name)) != null ? _b : defaultValue;
|
|
309
|
+
default:
|
|
310
|
+
return GM.getValue(name, defaultValue);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Sets a value in persistent storage - can be overwritten in a subclass if you want to use something other than GM storage.
|
|
316
|
+
* The default storage engines will stringify all passed values like numbers or booleans, so be aware of that.
|
|
317
|
+
*/
|
|
318
|
+
setValue(name, value) {
|
|
319
|
+
return __async(this, null, function* () {
|
|
320
|
+
switch (this.storageMethod) {
|
|
321
|
+
case "localStorage":
|
|
322
|
+
return localStorage.setItem(name, String(value));
|
|
323
|
+
case "sessionStorage":
|
|
324
|
+
return sessionStorage.setItem(name, String(value));
|
|
325
|
+
default:
|
|
326
|
+
return GM.setValue(name, String(value));
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
/** Deletes a value from persistent storage - can be overwritten in a subclass if you want to use something other than GM storage */
|
|
331
|
+
deleteValue(name) {
|
|
332
|
+
return __async(this, null, function* () {
|
|
333
|
+
switch (this.storageMethod) {
|
|
334
|
+
case "localStorage":
|
|
335
|
+
return localStorage.removeItem(name);
|
|
336
|
+
case "sessionStorage":
|
|
337
|
+
return sessionStorage.removeItem(name);
|
|
338
|
+
default:
|
|
339
|
+
return GM.deleteValue(name);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// lib/DataStoreSerializer.ts
|
|
346
|
+
var DataStoreSerializer = class {
|
|
347
|
+
constructor(stores, options = {}) {
|
|
348
|
+
__publicField(this, "stores");
|
|
349
|
+
__publicField(this, "options");
|
|
350
|
+
if (!getUnsafeWindow().crypto || !getUnsafeWindow().crypto.subtle)
|
|
351
|
+
throw new Error("DataStoreSerializer has to run in a secure context (HTTPS)!");
|
|
352
|
+
this.stores = stores;
|
|
353
|
+
this.options = __spreadValues({
|
|
354
|
+
addChecksum: true,
|
|
355
|
+
ensureIntegrity: true
|
|
356
|
+
}, options);
|
|
357
|
+
}
|
|
358
|
+
/** Calculates the checksum of a string */
|
|
359
|
+
calcChecksum(input) {
|
|
360
|
+
return __async(this, null, function* () {
|
|
361
|
+
return computeHash(input, "SHA-256");
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
/** Serializes a DataStore instance */
|
|
365
|
+
serializeStore(storeInst) {
|
|
366
|
+
return __async(this, null, function* () {
|
|
367
|
+
const data = storeInst.encodingEnabled() ? yield storeInst.encodeData(JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
|
|
368
|
+
const checksum = this.options.addChecksum ? yield this.calcChecksum(data) : void 0;
|
|
369
|
+
return {
|
|
370
|
+
id: storeInst.id,
|
|
371
|
+
data,
|
|
372
|
+
formatVersion: storeInst.formatVersion,
|
|
373
|
+
encoded: storeInst.encodingEnabled(),
|
|
374
|
+
checksum
|
|
375
|
+
};
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
/** Serializes the data stores into a string */
|
|
379
|
+
serialize() {
|
|
380
|
+
return __async(this, null, function* () {
|
|
381
|
+
const serData = [];
|
|
382
|
+
for (const store of this.stores)
|
|
383
|
+
serData.push(yield this.serializeStore(store));
|
|
384
|
+
return JSON.stringify(serData);
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Deserializes the data exported via {@linkcode serialize()} and imports it into the DataStore instances.
|
|
389
|
+
* Also triggers the migration process if the data format has changed.
|
|
390
|
+
*/
|
|
391
|
+
deserialize(serializedData) {
|
|
392
|
+
return __async(this, null, function* () {
|
|
393
|
+
const deserStores = JSON.parse(serializedData);
|
|
394
|
+
for (const storeData of deserStores) {
|
|
395
|
+
const storeInst = this.stores.find((s) => s.id === storeData.id);
|
|
396
|
+
if (!storeInst)
|
|
397
|
+
throw new Error(`DataStore instance with ID "${storeData.id}" not found! Make sure to provide it in the DataStoreSerializer constructor.`);
|
|
398
|
+
if (this.options.ensureIntegrity && typeof storeData.checksum === "string") {
|
|
399
|
+
const checksum = yield this.calcChecksum(storeData.data);
|
|
400
|
+
if (checksum !== storeData.checksum)
|
|
401
|
+
throw new Error(`Checksum mismatch for DataStore with ID "${storeData.id}"!
|
|
402
|
+
Expected: ${storeData.checksum}
|
|
403
|
+
Has: ${checksum}`);
|
|
404
|
+
}
|
|
405
|
+
const decodedData = storeData.encoded && storeInst.encodingEnabled() ? yield storeInst.decodeData(storeData.data) : storeData.data;
|
|
406
|
+
if (storeData.formatVersion && !isNaN(Number(storeData.formatVersion)) && Number(storeData.formatVersion) < storeInst.formatVersion)
|
|
407
|
+
yield storeInst.runMigrations(JSON.parse(decodedData), Number(storeData.formatVersion), false);
|
|
408
|
+
else
|
|
409
|
+
yield storeInst.setData(JSON.parse(decodedData));
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
281
413
|
};
|
|
282
414
|
|
|
283
415
|
// lib/dom.ts
|
|
@@ -288,11 +420,6 @@ function getUnsafeWindow() {
|
|
|
288
420
|
return window;
|
|
289
421
|
}
|
|
290
422
|
}
|
|
291
|
-
function insertAfter(beforeElement, afterElement) {
|
|
292
|
-
var _a;
|
|
293
|
-
(_a = beforeElement.parentNode) == null ? void 0 : _a.insertBefore(afterElement, beforeElement.nextSibling);
|
|
294
|
-
return afterElement;
|
|
295
|
-
}
|
|
296
423
|
function addParent(element, newParent) {
|
|
297
424
|
const oldParent = element.parentNode;
|
|
298
425
|
if (!oldParent)
|
|
@@ -316,18 +443,22 @@ function preloadImages(srcUrls, rejects = false) {
|
|
|
316
443
|
}));
|
|
317
444
|
return Promise.allSettled(promises);
|
|
318
445
|
}
|
|
319
|
-
function openInNewTab(href) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
446
|
+
function openInNewTab(href, background) {
|
|
447
|
+
try {
|
|
448
|
+
GM.openInTab(href, background);
|
|
449
|
+
} catch (e) {
|
|
450
|
+
const openElem = document.createElement("a");
|
|
451
|
+
Object.assign(openElem, {
|
|
452
|
+
className: "userutils-open-in-new-tab",
|
|
453
|
+
target: "_blank",
|
|
454
|
+
rel: "noopener noreferrer",
|
|
455
|
+
href
|
|
456
|
+
});
|
|
457
|
+
openElem.style.display = "none";
|
|
458
|
+
document.body.appendChild(openElem);
|
|
459
|
+
openElem.click();
|
|
460
|
+
setTimeout(openElem.remove, 50);
|
|
461
|
+
}
|
|
331
462
|
}
|
|
332
463
|
function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
333
464
|
Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
|
|
@@ -435,9 +566,14 @@ function fetchAdvanced(_0) {
|
|
|
435
566
|
id = setTimeout(() => controller.abort(), timeout);
|
|
436
567
|
signalOpts = { signal: controller.signal };
|
|
437
568
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
569
|
+
try {
|
|
570
|
+
const res = yield fetch(input, __spreadValues(__spreadValues({}, options), signalOpts));
|
|
571
|
+
id && clearTimeout(id);
|
|
572
|
+
return res;
|
|
573
|
+
} catch (err) {
|
|
574
|
+
id && clearTimeout(id);
|
|
575
|
+
throw err;
|
|
576
|
+
}
|
|
441
577
|
});
|
|
442
578
|
}
|
|
443
579
|
function insertValues(input, ...values) {
|
|
@@ -477,8 +613,38 @@ function ab2str(buf) {
|
|
|
477
613
|
function str2ab(str) {
|
|
478
614
|
return Uint8Array.from(getUnsafeWindow().atob(str), (c) => c.charCodeAt(0));
|
|
479
615
|
}
|
|
616
|
+
function computeHash(input, algorithm = "SHA-256") {
|
|
617
|
+
return __async(this, null, function* () {
|
|
618
|
+
let data;
|
|
619
|
+
if (typeof input === "string") {
|
|
620
|
+
const encoder = new TextEncoder();
|
|
621
|
+
data = encoder.encode(input);
|
|
622
|
+
} else
|
|
623
|
+
data = input;
|
|
624
|
+
const hashBuffer = yield crypto.subtle.digest(algorithm, data);
|
|
625
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
626
|
+
const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
627
|
+
return hashHex;
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
function randomId(length = 16, radix = 16, enhancedEntropy = false) {
|
|
631
|
+
if (enhancedEntropy) {
|
|
632
|
+
const arr = new Uint8Array(length);
|
|
633
|
+
crypto.getRandomValues(arr);
|
|
634
|
+
return Array.from(
|
|
635
|
+
arr,
|
|
636
|
+
(v) => mapRange(v, 0, 255, 0, radix).toString(radix).substring(0, 1)
|
|
637
|
+
).join("");
|
|
638
|
+
}
|
|
639
|
+
return Array.from(
|
|
640
|
+
{ length },
|
|
641
|
+
() => Math.floor(Math.random() * radix).toString(radix)
|
|
642
|
+
).join("");
|
|
643
|
+
}
|
|
480
644
|
|
|
481
645
|
// lib/SelectorObserver.ts
|
|
646
|
+
var domLoaded = false;
|
|
647
|
+
document.addEventListener("DOMContentLoaded", () => domLoaded = true);
|
|
482
648
|
var SelectorObserver = class {
|
|
483
649
|
constructor(baseElement, options = {}) {
|
|
484
650
|
__publicField(this, "enabled", false);
|
|
@@ -489,7 +655,6 @@ var SelectorObserver = class {
|
|
|
489
655
|
__publicField(this, "listenerMap");
|
|
490
656
|
this.baseElement = baseElement;
|
|
491
657
|
this.listenerMap = /* @__PURE__ */ new Map();
|
|
492
|
-
this.observer = new MutationObserver(() => this.checkAllSelectors());
|
|
493
658
|
const _a = options, {
|
|
494
659
|
defaultDebounce,
|
|
495
660
|
defaultDebounceEdge,
|
|
@@ -511,11 +676,21 @@ var SelectorObserver = class {
|
|
|
511
676
|
disableOnNoListeners: disableOnNoListeners != null ? disableOnNoListeners : false,
|
|
512
677
|
enableOnAddListener: enableOnAddListener != null ? enableOnAddListener : true
|
|
513
678
|
};
|
|
679
|
+
if (typeof this.customOptions.checkInterval !== "number") {
|
|
680
|
+
this.observer = new MutationObserver(() => this.checkAllSelectors());
|
|
681
|
+
} else {
|
|
682
|
+
this.checkAllSelectors();
|
|
683
|
+
setInterval(() => this.checkAllSelectors(), this.customOptions.checkInterval);
|
|
684
|
+
}
|
|
514
685
|
}
|
|
686
|
+
/** Call to check all selectors in the {@linkcode listenerMap} using {@linkcode checkSelector()} */
|
|
515
687
|
checkAllSelectors() {
|
|
688
|
+
if (!this.enabled || !domLoaded)
|
|
689
|
+
return;
|
|
516
690
|
for (const [selector, listeners] of this.listenerMap.entries())
|
|
517
691
|
this.checkSelector(selector, listeners);
|
|
518
692
|
}
|
|
693
|
+
/** Checks if the element(s) with the given {@linkcode selector} exist in the DOM and calls the respective {@linkcode listeners} accordingly */
|
|
519
694
|
checkSelector(selector, listeners) {
|
|
520
695
|
var _a;
|
|
521
696
|
if (!this.enabled)
|
|
@@ -547,9 +722,6 @@ var SelectorObserver = class {
|
|
|
547
722
|
this.disable();
|
|
548
723
|
}
|
|
549
724
|
}
|
|
550
|
-
debounce(func, time, edge = "falling") {
|
|
551
|
-
return debounce(func, time, edge);
|
|
552
|
-
}
|
|
553
725
|
/**
|
|
554
726
|
* Starts observing the children of the base element for changes to the given {@linkcode selector} according to the set {@linkcode options}
|
|
555
727
|
* @param selector The selector to observe
|
|
@@ -558,11 +730,16 @@ var SelectorObserver = class {
|
|
|
558
730
|
* @param [options.all] Whether to use `querySelectorAll()` instead - default is false
|
|
559
731
|
* @param [options.continuous] Whether to call the listener continuously instead of just once - default is false
|
|
560
732
|
* @param [options.debounce] Whether to debounce the listener to reduce calls to `querySelector` or `querySelectorAll` - set undefined or <=0 to disable (default)
|
|
733
|
+
* @returns Returns a function that can be called to remove this listener more easily
|
|
561
734
|
*/
|
|
562
735
|
addListener(selector, options) {
|
|
563
|
-
options = __spreadValues({
|
|
736
|
+
options = __spreadValues({
|
|
737
|
+
all: false,
|
|
738
|
+
continuous: false,
|
|
739
|
+
debounce: 0
|
|
740
|
+
}, options);
|
|
564
741
|
if (options.debounce && options.debounce > 0 || this.customOptions.defaultDebounce && this.customOptions.defaultDebounce > 0) {
|
|
565
|
-
options.listener =
|
|
742
|
+
options.listener = debounce(
|
|
566
743
|
options.listener,
|
|
567
744
|
options.debounce || this.customOptions.defaultDebounce,
|
|
568
745
|
options.debounceEdge || this.customOptions.defaultDebounceEdge
|
|
@@ -575,13 +752,15 @@ var SelectorObserver = class {
|
|
|
575
752
|
if (this.enabled === false && this.customOptions.enableOnAddListener)
|
|
576
753
|
this.enable();
|
|
577
754
|
this.checkSelector(selector, [options]);
|
|
755
|
+
return () => this.removeListener(selector, options);
|
|
578
756
|
}
|
|
579
757
|
/** Disables the observation of the child elements */
|
|
580
758
|
disable() {
|
|
759
|
+
var _a;
|
|
581
760
|
if (!this.enabled)
|
|
582
761
|
return;
|
|
583
762
|
this.enabled = false;
|
|
584
|
-
this.observer.disconnect();
|
|
763
|
+
(_a = this.observer) == null ? void 0 : _a.disconnect();
|
|
585
764
|
}
|
|
586
765
|
/**
|
|
587
766
|
* Enables or reenables the observation of the child elements.
|
|
@@ -589,11 +768,12 @@ var SelectorObserver = class {
|
|
|
589
768
|
* @returns Returns true when the observation was enabled, false otherwise (e.g. when the base element wasn't found)
|
|
590
769
|
*/
|
|
591
770
|
enable(immediatelyCheckSelectors = true) {
|
|
771
|
+
var _a;
|
|
592
772
|
const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
|
|
593
773
|
if (this.enabled || !baseElement)
|
|
594
774
|
return false;
|
|
595
775
|
this.enabled = true;
|
|
596
|
-
this.observer.observe(baseElement, this.observerOptions);
|
|
776
|
+
(_a = this.observer) == null ? void 0 : _a.observe(baseElement, this.observerOptions);
|
|
597
777
|
if (immediatelyCheckSelectors)
|
|
598
778
|
this.checkAllSelectors();
|
|
599
779
|
return true;
|
|
@@ -664,18 +844,19 @@ tr.getLanguage = () => {
|
|
|
664
844
|
};
|
|
665
845
|
|
|
666
846
|
exports.DataStore = DataStore;
|
|
847
|
+
exports.DataStoreSerializer = DataStoreSerializer;
|
|
667
848
|
exports.SelectorObserver = SelectorObserver;
|
|
668
849
|
exports.addGlobalStyle = addGlobalStyle;
|
|
669
850
|
exports.addParent = addParent;
|
|
670
851
|
exports.autoPlural = autoPlural;
|
|
671
852
|
exports.clamp = clamp;
|
|
672
853
|
exports.compress = compress;
|
|
854
|
+
exports.computeHash = computeHash;
|
|
673
855
|
exports.debounce = debounce;
|
|
674
856
|
exports.decompress = decompress;
|
|
675
857
|
exports.fetchAdvanced = fetchAdvanced;
|
|
676
858
|
exports.getSiblingsFrame = getSiblingsFrame;
|
|
677
859
|
exports.getUnsafeWindow = getUnsafeWindow;
|
|
678
|
-
exports.insertAfter = insertAfter;
|
|
679
860
|
exports.insertValues = insertValues;
|
|
680
861
|
exports.interceptEvent = interceptEvent;
|
|
681
862
|
exports.interceptWindowEvent = interceptWindowEvent;
|