@sv443-network/userutils 9.1.0 → 9.2.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 +19 -0
- package/README-summary.md +10 -0
- package/README.md +10 -0
- package/dist/index.cjs +119 -41
- package/dist/index.global.js +121 -43
- package/dist/index.js +111 -42
- package/dist/lib/DataStoreSerializer.d.ts +16 -6
- package/dist/lib/dom.d.ts +19 -0
- package/dist/lib/errors.d.ts +21 -0
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/math.d.ts +52 -2
- package/dist/lib/translation.d.ts +23 -16
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @sv443-network/userutils
|
|
2
2
|
|
|
3
|
+
## 9.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 905fea4: Added function `isDomLoaded()` to check if the DOM is queryable, regardless of `@run-at` setting
|
|
8
|
+
- 4264154: Added parameter `withDecimals` to `digitCount()` (true by default)
|
|
9
|
+
- 4264154: Added function `roundFixed()` to round a floating-point number to the given amount of decimals. Can also round to the given power of 10.
|
|
10
|
+
- 7e492cf: Added `probeElementStyle()` to probe the computed style of a temporary element, allowing to resolve CSS variables and default style values, etc.
|
|
11
|
+
- 905fea4: Added function `onDomLoad()` to call a callback and/or resolve a Promise when the DOM is loaded, even retroactively
|
|
12
|
+
- 4264154: Added function `bitSetHas()` to check if a given value is present in a [bitset](https://www.geeksforgeeks.org/cpp-bitset-and-its-application/)
|
|
13
|
+
- bf55335: Replaced a bunch of generic `Error`s with the new custom error class instances
|
|
14
|
+
- bf55335: Added custom error classes `ChecksumMismatchError`, `DataMigrationError` and `PlatformError`, extending from the base class `UUError`
|
|
15
|
+
The base class has the additional property `date` which is the time of the error creation
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 157dacb: Fixed example code in tsdoc comments of translation functions
|
|
20
|
+
- 4264154: Fixed `digitCount()` not counting decimals by default
|
|
21
|
+
|
|
3
22
|
## 9.1.0
|
|
4
23
|
|
|
5
24
|
### Minor Changes
|
package/README-summary.md
CHANGED
|
@@ -36,6 +36,8 @@ View the documentation of previous major releases:
|
|
|
36
36
|
- **DOM:**
|
|
37
37
|
- [`SelectorObserver`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#selectorobserver) - class that manages listeners that are called when selectors are found in the DOM
|
|
38
38
|
- [`getUnsafeWindow()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#getunsafewindow) - get the unsafeWindow object or fall back to the regular window object
|
|
39
|
+
- [`isDomLoaded()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#isdomloaded) - check if the DOM has finished loading and can be queried and modified
|
|
40
|
+
- [`onDomLoad()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#ondomload) - run a function or pause async execution until the DOM has finished loading (or immediately if DOM is already loaded)
|
|
39
41
|
- [`addParent()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#addparent) - add a parent element around another element
|
|
40
42
|
- [`addGlobalStyle()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#addglobalstyle) - add a global style to the page
|
|
41
43
|
- [`preloadImages()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#preloadimages) - preload images into the browser cache for faster loading later on
|
|
@@ -46,11 +48,14 @@ View the documentation of previous major releases:
|
|
|
46
48
|
- [`observeElementProp()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#observeelementprop) - observe changes to an element's property that can't be observed with MutationObserver
|
|
47
49
|
- [`getSiblingsFrame()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#getsiblingsframe) - returns a frame of an element's siblings, with a given alignment and size
|
|
48
50
|
- [`setInnerHtmlUnsafe()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#setinnerhtmlunsafe) - set the innerHTML of an element using a [Trusted Types policy](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) without sanitizing or escaping it
|
|
51
|
+
- [`probeElementStyle()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#probeelementstyle) - probe the computed style of a temporary element (get default font size, resolve CSS variables, etc.)
|
|
49
52
|
- **Math:**
|
|
50
53
|
- [`clamp()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#clamp) - constrain a number between a min and max value
|
|
51
54
|
- [`mapRange()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#maprange) - map a number from one range to the same spot in another range
|
|
52
55
|
- [`randRange()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#randrange) - generate a random number between a min and max boundary
|
|
53
56
|
- [`digitCount()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#digitcount) - calculate the amount of digits in a number
|
|
57
|
+
- [`roundFixed()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#roundfixed) - round a floating-point number at the given amount of decimals, or to the given power of 10
|
|
58
|
+
- [`bitSetHas()`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#bitsethas) - check if a bit is set in a [bitset](https://www.geeksforgeeks.org/cpp-bitset-and-its-application/)
|
|
54
59
|
- **Misc:**
|
|
55
60
|
- [`DataStore`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#datastore) - class that manages a hybrid sync & async persistent JSON database, including data migration
|
|
56
61
|
- [`DataStoreSerializer`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#datastoreserializer) - class for importing & exporting data of multiple DataStore instances, including compression, checksumming and running migrations
|
|
@@ -101,6 +106,11 @@ View the documentation of previous major releases:
|
|
|
101
106
|
- [`ValueGen`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#valuegen) - a "generator" value that allows for super flexible value typing and declaration
|
|
102
107
|
- [`StringGen`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#stringgen) - a "generator" string that allows for super flexible string typing and declaration, including enhanced support for unions
|
|
103
108
|
- [`ListWithLength`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#listwithlength) - represents an array or object with a numeric `length`, `count` or `size` property
|
|
109
|
+
- **Custom Error classes:**
|
|
110
|
+
- [`UUError`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#uuerror) - base class for all custom UserUtils errors - has a custom `date` prop set to the time of creation
|
|
111
|
+
- [`ChecksumMismatchError`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#checksummismatcherror) - thrown when a string of data doesn't match its checksum
|
|
112
|
+
- [`DataMigrationError`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#datamigrationerror) - thrown when a data migration fails
|
|
113
|
+
- [`PlatformError`](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#platformerror) - thrown when a function is called in an unsupported environment
|
|
104
114
|
|
|
105
115
|
<br><br>
|
|
106
116
|
|
package/README.md
CHANGED
|
@@ -39,6 +39,8 @@ View the documentation of previous major releases:
|
|
|
39
39
|
- [**DOM:**](./docs.md#dom)
|
|
40
40
|
- [`SelectorObserver`](./docs.md#selectorobserver) - class that manages listeners that are called when selectors are found in the DOM
|
|
41
41
|
- [`getUnsafeWindow()`](./docs.md#getunsafewindow) - get the unsafeWindow object or fall back to the regular window object
|
|
42
|
+
- [`isDomLoaded()`](./docs.md#isdomloaded) - check if the DOM has finished loading and can be queried and modified
|
|
43
|
+
- [`onDomLoad()`](./docs.md#ondomload) - run a function or pause async execution until the DOM has finished loading (or immediately if DOM is already loaded)
|
|
42
44
|
- [`addParent()`](./docs.md#addparent) - add a parent element around another element
|
|
43
45
|
- [`addGlobalStyle()`](./docs.md#addglobalstyle) - add a global style to the page
|
|
44
46
|
- [`preloadImages()`](./docs.md#preloadimages) - preload images into the browser cache for faster loading later on
|
|
@@ -49,11 +51,14 @@ View the documentation of previous major releases:
|
|
|
49
51
|
- [`observeElementProp()`](./docs.md#observeelementprop) - observe changes to an element's property that can't be observed with MutationObserver
|
|
50
52
|
- [`getSiblingsFrame()`](./docs.md#getsiblingsframe) - returns a frame of an element's siblings, with a given alignment and size
|
|
51
53
|
- [`setInnerHtmlUnsafe()`](./docs.md#setinnerhtmlunsafe) - set the innerHTML of an element using a [Trusted Types policy](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API) without sanitizing or escaping it
|
|
54
|
+
- [`probeElementStyle()`](./docs.md#probeelementstyle) - probe the computed style of a temporary element (get default font size, resolve CSS variables, etc.)
|
|
52
55
|
- [**Math:**](./docs.md#math)
|
|
53
56
|
- [`clamp()`](./docs.md#clamp) - constrain a number between a min and max value
|
|
54
57
|
- [`mapRange()`](./docs.md#maprange) - map a number from one range to the same spot in another range
|
|
55
58
|
- [`randRange()`](./docs.md#randrange) - generate a random number between a min and max boundary
|
|
56
59
|
- [`digitCount()`](./docs.md#digitcount) - calculate the amount of digits in a number
|
|
60
|
+
- [`roundFixed()`](./docs.md#roundfixed) - round a floating-point number at the given amount of decimals, or to the given power of 10
|
|
61
|
+
- [`bitSetHas()`](./docs.md#bitsethas) - check if a bit is set in a [bitset](https://www.geeksforgeeks.org/cpp-bitset-and-its-application/)
|
|
57
62
|
- [**Misc:**](./docs.md#misc)
|
|
58
63
|
- [`DataStore`](./docs.md#datastore) - class that manages a hybrid sync & async persistent JSON database, including data migration
|
|
59
64
|
- [`DataStoreSerializer`](./docs.md#datastoreserializer) - class for importing & exporting data of multiple DataStore instances, including compression, checksumming and running migrations
|
|
@@ -104,6 +109,11 @@ View the documentation of previous major releases:
|
|
|
104
109
|
- [`ValueGen`](./docs.md#valuegen) - a "generator" value that allows for super flexible value typing and declaration
|
|
105
110
|
- [`StringGen`](./docs.md#stringgen) - a "generator" string that allows for super flexible string typing and declaration, including enhanced support for unions
|
|
106
111
|
- [`ListWithLength`](./docs.md#listwithlength) - represents an array or object with a numeric `length`, `count` or `size` property
|
|
112
|
+
- [**Custom Error classes**](./docs.md#error-classes)
|
|
113
|
+
- [`UUError`](./docs.md#uuerror) - base class for all custom UserUtils errors - has a custom `date` prop set to the time of creation
|
|
114
|
+
- [`ChecksumMismatchError`](./docs.md#checksummismatcherror) - thrown when a string of data doesn't match its checksum
|
|
115
|
+
- [`DataMigrationError`](./docs.md#datamigrationerror) - thrown when a data migration fails
|
|
116
|
+
- [`PlatformError`](./docs.md#platformerror) - thrown when a function is called in an unsupported environment
|
|
107
117
|
|
|
108
118
|
<br><br>
|
|
109
119
|
|
package/dist/index.cjs
CHANGED
|
@@ -99,13 +99,21 @@ function randRange(...args) {
|
|
|
99
99
|
} else
|
|
100
100
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
101
101
|
}
|
|
102
|
-
function digitCount(num) {
|
|
102
|
+
function digitCount(num, withDecimals = true) {
|
|
103
103
|
num = Number(!["string", "number"].includes(typeof num) ? String(num) : num);
|
|
104
104
|
if (typeof num === "number" && isNaN(num))
|
|
105
105
|
return NaN;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
const [intPart, decPart] = num.toString().split(".");
|
|
107
|
+
const intDigits = intPart === "0" ? 1 : Math.floor(Math.log10(Math.abs(Number(intPart))) + 1);
|
|
108
|
+
const decDigits = withDecimals && decPart ? decPart.length : 0;
|
|
109
|
+
return intDigits + decDigits;
|
|
110
|
+
}
|
|
111
|
+
function roundFixed(num, fractionDigits) {
|
|
112
|
+
const scale = 10 ** fractionDigits;
|
|
113
|
+
return Math.round(num * scale) / scale;
|
|
114
|
+
}
|
|
115
|
+
function bitSetHas(bitSet, checkVal) {
|
|
116
|
+
return (bitSet & checkVal) === checkVal;
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
// lib/array.ts
|
|
@@ -130,7 +138,7 @@ function randomizeArray(array) {
|
|
|
130
138
|
if (array.length === 0)
|
|
131
139
|
return retArray;
|
|
132
140
|
for (let i = retArray.length - 1; i > 0; i--) {
|
|
133
|
-
const j = Math.floor(
|
|
141
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
134
142
|
[retArray[i], retArray[j]] = [retArray[j], retArray[i]];
|
|
135
143
|
}
|
|
136
144
|
return retArray;
|
|
@@ -173,10 +181,10 @@ function darkenColor(color, percent, upperCase = false) {
|
|
|
173
181
|
else if (color.startsWith("rgb")) {
|
|
174
182
|
const rgbValues = (_a = color.match(/\d+(\.\d+)?/g)) == null ? undefined : _a.map(Number);
|
|
175
183
|
if (!rgbValues)
|
|
176
|
-
throw new
|
|
184
|
+
throw new TypeError("Invalid RGB/RGBA color format");
|
|
177
185
|
[r, g, b, a] = rgbValues;
|
|
178
186
|
} else
|
|
179
|
-
throw new
|
|
187
|
+
throw new TypeError("Unsupported color format");
|
|
180
188
|
[r, g, b] = darkenRgb(r, g, b, percent);
|
|
181
189
|
if (isHexCol)
|
|
182
190
|
return rgbToHex(r, g, b, a, color.startsWith("#"), upperCase);
|
|
@@ -185,10 +193,39 @@ function darkenColor(color, percent, upperCase = false) {
|
|
|
185
193
|
else if (color.startsWith("rgb"))
|
|
186
194
|
return `rgb(${r}, ${g}, ${b})`;
|
|
187
195
|
else
|
|
188
|
-
throw new
|
|
196
|
+
throw new TypeError("Unsupported color format");
|
|
189
197
|
}
|
|
190
198
|
|
|
199
|
+
// lib/errors.ts
|
|
200
|
+
var UUError = class extends Error {
|
|
201
|
+
constructor(message, options) {
|
|
202
|
+
super(message, options);
|
|
203
|
+
__publicField(this, "date");
|
|
204
|
+
this.date = /* @__PURE__ */ new Date();
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
var ChecksumMismatchError = class extends UUError {
|
|
208
|
+
constructor(message, options) {
|
|
209
|
+
super(message, options);
|
|
210
|
+
this.name = "ChecksumMismatchError";
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
var MigrationError = class extends UUError {
|
|
214
|
+
constructor(message, options) {
|
|
215
|
+
super(message, options);
|
|
216
|
+
this.name = "MigrationError";
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
var PlatformError = class extends UUError {
|
|
220
|
+
constructor(message, options) {
|
|
221
|
+
super(message, options);
|
|
222
|
+
this.name = "PlatformError";
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
191
226
|
// lib/dom.ts
|
|
227
|
+
var domReady = false;
|
|
228
|
+
document.addEventListener("DOMContentLoaded", () => domReady = true);
|
|
192
229
|
function getUnsafeWindow() {
|
|
193
230
|
try {
|
|
194
231
|
return unsafeWindow;
|
|
@@ -244,8 +281,8 @@ function openInNewTab(href, background, additionalProps) {
|
|
|
244
281
|
}
|
|
245
282
|
function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
246
283
|
var _a;
|
|
247
|
-
if ((
|
|
248
|
-
throw new
|
|
284
|
+
if (((_a = GM == null ? undefined : GM.info) == null ? undefined : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey" && (eventObject === window || eventObject === getUnsafeWindow()))
|
|
285
|
+
throw new PlatformError("Intercepting window events is not supported on FireMonkey due to the isolated context the userscript runs in.");
|
|
249
286
|
Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
|
|
250
287
|
if (isNaN(Error.stackTraceLimit))
|
|
251
288
|
Error.stackTraceLimit = 100;
|
|
@@ -327,6 +364,36 @@ function setInnerHtmlUnsafe(element, html) {
|
|
|
327
364
|
element.innerHTML = (_c = (_b = ttPolicy == null ? undefined : ttPolicy.createHTML) == null ? undefined : _b.call(ttPolicy, html)) != null ? _c : html;
|
|
328
365
|
return element;
|
|
329
366
|
}
|
|
367
|
+
function probeElementStyle(probeStyle, element, hideOffscreen = true, parentElement = document.body) {
|
|
368
|
+
const el = element ? typeof element === "function" ? element() : element : document.createElement("span");
|
|
369
|
+
if (hideOffscreen) {
|
|
370
|
+
el.style.position = "absolute";
|
|
371
|
+
el.style.left = "-9999px";
|
|
372
|
+
el.style.top = "-9999px";
|
|
373
|
+
el.style.zIndex = "-9999";
|
|
374
|
+
}
|
|
375
|
+
el.classList.add("_uu_probe_element");
|
|
376
|
+
parentElement.appendChild(el);
|
|
377
|
+
const style = window.getComputedStyle(el);
|
|
378
|
+
const result = probeStyle(style, el);
|
|
379
|
+
setTimeout(() => el.remove(), 1);
|
|
380
|
+
return result;
|
|
381
|
+
}
|
|
382
|
+
function isDomLoaded() {
|
|
383
|
+
return domReady;
|
|
384
|
+
}
|
|
385
|
+
function onDomLoad(cb) {
|
|
386
|
+
return new Promise((res) => {
|
|
387
|
+
if (domReady) {
|
|
388
|
+
cb == null ? undefined : cb();
|
|
389
|
+
res();
|
|
390
|
+
} else
|
|
391
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
392
|
+
cb == null ? undefined : cb();
|
|
393
|
+
res();
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
}
|
|
330
397
|
|
|
331
398
|
// lib/crypto.ts
|
|
332
399
|
function compress(input, compressionFormat, outputType = "string") {
|
|
@@ -374,6 +441,8 @@ function computeHash(input, algorithm = "SHA-256") {
|
|
|
374
441
|
});
|
|
375
442
|
}
|
|
376
443
|
function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase = true) {
|
|
444
|
+
if (radix < 2 || radix > 36)
|
|
445
|
+
throw new RangeError("The radix argument must be between 2 and 36");
|
|
377
446
|
let arr = [];
|
|
378
447
|
const caseArr = randomCase ? [0, 1] : [0];
|
|
379
448
|
if (enhancedEntropy) {
|
|
@@ -547,8 +616,7 @@ var DataStore = class {
|
|
|
547
616
|
lastFmtVer = oldFmtVer = ver;
|
|
548
617
|
} catch (err) {
|
|
549
618
|
if (!resetOnError)
|
|
550
|
-
throw new
|
|
551
|
-
console.error(`Error while running migration function for format version '${fmtVer}' - resetting to the default value.`, err);
|
|
619
|
+
throw new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
|
|
552
620
|
yield this.saveDefaultData();
|
|
553
621
|
return this.getData();
|
|
554
622
|
}
|
|
@@ -609,7 +677,6 @@ var DataStore = class {
|
|
|
609
677
|
return JSON.parse(decRes != null ? decRes : data);
|
|
610
678
|
});
|
|
611
679
|
}
|
|
612
|
-
//#region misc
|
|
613
680
|
/** Copies a JSON-compatible object and loses all its internal references in the process */
|
|
614
681
|
deepCopy(obj) {
|
|
615
682
|
return JSON.parse(JSON.stringify(obj));
|
|
@@ -661,7 +728,7 @@ var DataStore = class {
|
|
|
661
728
|
};
|
|
662
729
|
|
|
663
730
|
// lib/DataStoreSerializer.ts
|
|
664
|
-
var DataStoreSerializer = class {
|
|
731
|
+
var DataStoreSerializer = class _DataStoreSerializer {
|
|
665
732
|
constructor(stores, options = {}) {
|
|
666
733
|
__publicField(this, "stores");
|
|
667
734
|
__publicField(this, "options");
|
|
@@ -679,27 +746,25 @@ var DataStoreSerializer = class {
|
|
|
679
746
|
return computeHash(input, "SHA-256");
|
|
680
747
|
});
|
|
681
748
|
}
|
|
682
|
-
/**
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
id: storeInst.id,
|
|
689
|
-
data,
|
|
690
|
-
formatVersion: storeInst.formatVersion,
|
|
691
|
-
encoded: storeInst.encodingEnabled(),
|
|
692
|
-
checksum
|
|
693
|
-
};
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
/** Serializes the data stores into a string */
|
|
697
|
-
serialize() {
|
|
749
|
+
/**
|
|
750
|
+
* Serializes the data stores into a string.
|
|
751
|
+
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
752
|
+
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
753
|
+
*/
|
|
754
|
+
serialize(useEncoding = true, stringified = true) {
|
|
698
755
|
return __async(this, null, function* () {
|
|
699
756
|
const serData = [];
|
|
700
|
-
for (const
|
|
701
|
-
|
|
702
|
-
|
|
757
|
+
for (const storeInst of this.stores) {
|
|
758
|
+
const data = useEncoding && storeInst.encodingEnabled() ? yield storeInst.encodeData(JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
|
|
759
|
+
serData.push({
|
|
760
|
+
id: storeInst.id,
|
|
761
|
+
data,
|
|
762
|
+
formatVersion: storeInst.formatVersion,
|
|
763
|
+
encoded: useEncoding && storeInst.encodingEnabled(),
|
|
764
|
+
checksum: this.options.addChecksum ? yield this.calcChecksum(data) : undefined
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
return stringified ? JSON.stringify(serData) : serData;
|
|
703
768
|
});
|
|
704
769
|
}
|
|
705
770
|
/**
|
|
@@ -708,7 +773,9 @@ var DataStoreSerializer = class {
|
|
|
708
773
|
*/
|
|
709
774
|
deserialize(serializedData) {
|
|
710
775
|
return __async(this, null, function* () {
|
|
711
|
-
const deserStores = JSON.parse(serializedData);
|
|
776
|
+
const deserStores = typeof serializedData === "string" ? JSON.parse(serializedData) : serializedData;
|
|
777
|
+
if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStore))
|
|
778
|
+
throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
|
|
712
779
|
for (const storeData of deserStores) {
|
|
713
780
|
const storeInst = this.stores.find((s) => s.id === storeData.id);
|
|
714
781
|
if (!storeInst)
|
|
@@ -716,7 +783,7 @@ var DataStoreSerializer = class {
|
|
|
716
783
|
if (this.options.ensureIntegrity && typeof storeData.checksum === "string") {
|
|
717
784
|
const checksum = yield this.calcChecksum(storeData.data);
|
|
718
785
|
if (checksum !== storeData.checksum)
|
|
719
|
-
throw new
|
|
786
|
+
throw new ChecksumMismatchError(`Checksum mismatch for DataStore with ID "${storeData.id}"!
|
|
720
787
|
Expected: ${storeData.checksum}
|
|
721
788
|
Has: ${checksum}`);
|
|
722
789
|
}
|
|
@@ -760,6 +827,10 @@ Has: ${checksum}`);
|
|
|
760
827
|
return Promise.allSettled(this.stores.map((store) => store.deleteData()));
|
|
761
828
|
});
|
|
762
829
|
}
|
|
830
|
+
/** Checks if a given value is a SerializedDataStore object */
|
|
831
|
+
static isSerializedDataStore(obj) {
|
|
832
|
+
return typeof obj === "object" && obj !== null && "id" in obj && "data" in obj && "formatVersion" in obj && "encoded" in obj;
|
|
833
|
+
}
|
|
763
834
|
};
|
|
764
835
|
var NanoEmitter = class {
|
|
765
836
|
constructor(options = {}) {
|
|
@@ -899,7 +970,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
899
970
|
}, this.timeout);
|
|
900
971
|
break;
|
|
901
972
|
default:
|
|
902
|
-
throw new
|
|
973
|
+
throw new TypeError(`Invalid debouncer type: ${this.type}`);
|
|
903
974
|
}
|
|
904
975
|
}
|
|
905
976
|
};
|
|
@@ -1403,8 +1474,6 @@ function getListLength(obj, zeroOnInvalid = true) {
|
|
|
1403
1474
|
}
|
|
1404
1475
|
|
|
1405
1476
|
// lib/SelectorObserver.ts
|
|
1406
|
-
var domLoaded = false;
|
|
1407
|
-
document.addEventListener("DOMContentLoaded", () => domLoaded = true);
|
|
1408
1477
|
var SelectorObserver = class {
|
|
1409
1478
|
constructor(baseElement, options = {}) {
|
|
1410
1479
|
__publicField(this, "enabled", false);
|
|
@@ -1445,7 +1514,7 @@ var SelectorObserver = class {
|
|
|
1445
1514
|
}
|
|
1446
1515
|
/** Call to check all selectors in the {@linkcode listenerMap} using {@linkcode checkSelector()} */
|
|
1447
1516
|
checkAllSelectors() {
|
|
1448
|
-
if (!this.enabled || !
|
|
1517
|
+
if (!this.enabled || !isDomLoaded())
|
|
1449
1518
|
return;
|
|
1450
1519
|
for (const [selector, listeners] of this.listenerMap.entries())
|
|
1451
1520
|
this.checkSelector(selector, listeners);
|
|
@@ -1589,9 +1658,9 @@ function translate(language, key, ...trArgs) {
|
|
|
1589
1658
|
if (typeof language !== "string" || language.length === 0 || typeof trObj !== "object" || trObj === null)
|
|
1590
1659
|
return fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
|
|
1591
1660
|
const transformTrVal = (trKey, trValue) => {
|
|
1592
|
-
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(trValue));
|
|
1661
|
+
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(String(trValue)));
|
|
1593
1662
|
if (tfs.length === 0)
|
|
1594
|
-
return trValue;
|
|
1663
|
+
return String(trValue);
|
|
1595
1664
|
let retStr = String(trValue);
|
|
1596
1665
|
for (const tf of tfs) {
|
|
1597
1666
|
const re = new RegExp(tf.regex);
|
|
@@ -1740,15 +1809,20 @@ var tr = {
|
|
|
1740
1809
|
}
|
|
1741
1810
|
};
|
|
1742
1811
|
|
|
1812
|
+
exports.ChecksumMismatchError = ChecksumMismatchError;
|
|
1743
1813
|
exports.DataStore = DataStore;
|
|
1744
1814
|
exports.DataStoreSerializer = DataStoreSerializer;
|
|
1745
1815
|
exports.Debouncer = Debouncer;
|
|
1746
1816
|
exports.Dialog = Dialog;
|
|
1817
|
+
exports.MigrationError = MigrationError;
|
|
1747
1818
|
exports.NanoEmitter = NanoEmitter;
|
|
1819
|
+
exports.PlatformError = PlatformError;
|
|
1748
1820
|
exports.SelectorObserver = SelectorObserver;
|
|
1821
|
+
exports.UUError = UUError;
|
|
1749
1822
|
exports.addGlobalStyle = addGlobalStyle;
|
|
1750
1823
|
exports.addParent = addParent;
|
|
1751
1824
|
exports.autoPlural = autoPlural;
|
|
1825
|
+
exports.bitSetHas = bitSetHas;
|
|
1752
1826
|
exports.clamp = clamp;
|
|
1753
1827
|
exports.compress = compress;
|
|
1754
1828
|
exports.computeHash = computeHash;
|
|
@@ -1768,20 +1842,24 @@ exports.hexToRgb = hexToRgb;
|
|
|
1768
1842
|
exports.insertValues = insertValues;
|
|
1769
1843
|
exports.interceptEvent = interceptEvent;
|
|
1770
1844
|
exports.interceptWindowEvent = interceptWindowEvent;
|
|
1845
|
+
exports.isDomLoaded = isDomLoaded;
|
|
1771
1846
|
exports.isScrollable = isScrollable;
|
|
1772
1847
|
exports.lightenColor = lightenColor;
|
|
1773
1848
|
exports.mapRange = mapRange;
|
|
1774
1849
|
exports.observeElementProp = observeElementProp;
|
|
1850
|
+
exports.onDomLoad = onDomLoad;
|
|
1775
1851
|
exports.openDialogs = openDialogs;
|
|
1776
1852
|
exports.openInNewTab = openInNewTab;
|
|
1777
1853
|
exports.pauseFor = pauseFor;
|
|
1778
1854
|
exports.preloadImages = preloadImages;
|
|
1855
|
+
exports.probeElementStyle = probeElementStyle;
|
|
1779
1856
|
exports.randRange = randRange;
|
|
1780
1857
|
exports.randomId = randomId;
|
|
1781
1858
|
exports.randomItem = randomItem;
|
|
1782
1859
|
exports.randomItemIndex = randomItemIndex;
|
|
1783
1860
|
exports.randomizeArray = randomizeArray;
|
|
1784
1861
|
exports.rgbToHex = rgbToHex;
|
|
1862
|
+
exports.roundFixed = roundFixed;
|
|
1785
1863
|
exports.setInnerHtmlUnsafe = setInnerHtmlUnsafe;
|
|
1786
1864
|
exports.takeRandomItem = takeRandomItem;
|
|
1787
1865
|
exports.tr = tr;
|
package/dist/index.global.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
// ==UserLibrary==
|
|
9
9
|
// @name UserUtils
|
|
10
|
-
// @description
|
|
11
|
-
// @version 9.
|
|
10
|
+
// @description General purpose DOM/GreaseMonkey library that allows you to register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and much more
|
|
11
|
+
// @version 9.2.0
|
|
12
12
|
// @license MIT
|
|
13
13
|
// @copyright Sv443 (https://github.com/Sv443)
|
|
14
14
|
|
|
@@ -117,13 +117,21 @@ var UserUtils = (function (exports) {
|
|
|
117
117
|
} else
|
|
118
118
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
119
119
|
}
|
|
120
|
-
function digitCount(num) {
|
|
120
|
+
function digitCount(num, withDecimals = true) {
|
|
121
121
|
num = Number(!["string", "number"].includes(typeof num) ? String(num) : num);
|
|
122
122
|
if (typeof num === "number" && isNaN(num))
|
|
123
123
|
return NaN;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
const [intPart, decPart] = num.toString().split(".");
|
|
125
|
+
const intDigits = intPart === "0" ? 1 : Math.floor(Math.log10(Math.abs(Number(intPart))) + 1);
|
|
126
|
+
const decDigits = withDecimals && decPart ? decPart.length : 0;
|
|
127
|
+
return intDigits + decDigits;
|
|
128
|
+
}
|
|
129
|
+
function roundFixed(num, fractionDigits) {
|
|
130
|
+
const scale = 10 ** fractionDigits;
|
|
131
|
+
return Math.round(num * scale) / scale;
|
|
132
|
+
}
|
|
133
|
+
function bitSetHas(bitSet, checkVal) {
|
|
134
|
+
return (bitSet & checkVal) === checkVal;
|
|
127
135
|
}
|
|
128
136
|
|
|
129
137
|
// lib/array.ts
|
|
@@ -148,7 +156,7 @@ var UserUtils = (function (exports) {
|
|
|
148
156
|
if (array.length === 0)
|
|
149
157
|
return retArray;
|
|
150
158
|
for (let i = retArray.length - 1; i > 0; i--) {
|
|
151
|
-
const j = Math.floor(
|
|
159
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
152
160
|
[retArray[i], retArray[j]] = [retArray[j], retArray[i]];
|
|
153
161
|
}
|
|
154
162
|
return retArray;
|
|
@@ -191,10 +199,10 @@ var UserUtils = (function (exports) {
|
|
|
191
199
|
else if (color.startsWith("rgb")) {
|
|
192
200
|
const rgbValues = (_a = color.match(/\d+(\.\d+)?/g)) == null ? undefined : _a.map(Number);
|
|
193
201
|
if (!rgbValues)
|
|
194
|
-
throw new
|
|
202
|
+
throw new TypeError("Invalid RGB/RGBA color format");
|
|
195
203
|
[r, g, b, a] = rgbValues;
|
|
196
204
|
} else
|
|
197
|
-
throw new
|
|
205
|
+
throw new TypeError("Unsupported color format");
|
|
198
206
|
[r, g, b] = darkenRgb(r, g, b, percent);
|
|
199
207
|
if (isHexCol)
|
|
200
208
|
return rgbToHex(r, g, b, a, color.startsWith("#"), upperCase);
|
|
@@ -203,10 +211,39 @@ var UserUtils = (function (exports) {
|
|
|
203
211
|
else if (color.startsWith("rgb"))
|
|
204
212
|
return `rgb(${r}, ${g}, ${b})`;
|
|
205
213
|
else
|
|
206
|
-
throw new
|
|
214
|
+
throw new TypeError("Unsupported color format");
|
|
207
215
|
}
|
|
208
216
|
|
|
217
|
+
// lib/errors.ts
|
|
218
|
+
var UUError = class extends Error {
|
|
219
|
+
constructor(message, options) {
|
|
220
|
+
super(message, options);
|
|
221
|
+
__publicField(this, "date");
|
|
222
|
+
this.date = /* @__PURE__ */ new Date();
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
var ChecksumMismatchError = class extends UUError {
|
|
226
|
+
constructor(message, options) {
|
|
227
|
+
super(message, options);
|
|
228
|
+
this.name = "ChecksumMismatchError";
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var MigrationError = class extends UUError {
|
|
232
|
+
constructor(message, options) {
|
|
233
|
+
super(message, options);
|
|
234
|
+
this.name = "MigrationError";
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
var PlatformError = class extends UUError {
|
|
238
|
+
constructor(message, options) {
|
|
239
|
+
super(message, options);
|
|
240
|
+
this.name = "PlatformError";
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
209
244
|
// lib/dom.ts
|
|
245
|
+
var domReady = false;
|
|
246
|
+
document.addEventListener("DOMContentLoaded", () => domReady = true);
|
|
210
247
|
function getUnsafeWindow() {
|
|
211
248
|
try {
|
|
212
249
|
return unsafeWindow;
|
|
@@ -262,8 +299,8 @@ var UserUtils = (function (exports) {
|
|
|
262
299
|
}
|
|
263
300
|
function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
264
301
|
var _a;
|
|
265
|
-
if ((
|
|
266
|
-
throw new
|
|
302
|
+
if (((_a = GM == null ? undefined : GM.info) == null ? undefined : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey" && (eventObject === window || eventObject === getUnsafeWindow()))
|
|
303
|
+
throw new PlatformError("Intercepting window events is not supported on FireMonkey due to the isolated context the userscript runs in.");
|
|
267
304
|
Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
|
|
268
305
|
if (isNaN(Error.stackTraceLimit))
|
|
269
306
|
Error.stackTraceLimit = 100;
|
|
@@ -345,6 +382,36 @@ var UserUtils = (function (exports) {
|
|
|
345
382
|
element.innerHTML = (_c = (_b = ttPolicy == null ? undefined : ttPolicy.createHTML) == null ? undefined : _b.call(ttPolicy, html)) != null ? _c : html;
|
|
346
383
|
return element;
|
|
347
384
|
}
|
|
385
|
+
function probeElementStyle(probeStyle, element, hideOffscreen = true, parentElement = document.body) {
|
|
386
|
+
const el = element ? typeof element === "function" ? element() : element : document.createElement("span");
|
|
387
|
+
if (hideOffscreen) {
|
|
388
|
+
el.style.position = "absolute";
|
|
389
|
+
el.style.left = "-9999px";
|
|
390
|
+
el.style.top = "-9999px";
|
|
391
|
+
el.style.zIndex = "-9999";
|
|
392
|
+
}
|
|
393
|
+
el.classList.add("_uu_probe_element");
|
|
394
|
+
parentElement.appendChild(el);
|
|
395
|
+
const style = window.getComputedStyle(el);
|
|
396
|
+
const result = probeStyle(style, el);
|
|
397
|
+
setTimeout(() => el.remove(), 1);
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
function isDomLoaded() {
|
|
401
|
+
return domReady;
|
|
402
|
+
}
|
|
403
|
+
function onDomLoad(cb) {
|
|
404
|
+
return new Promise((res) => {
|
|
405
|
+
if (domReady) {
|
|
406
|
+
cb == null ? undefined : cb();
|
|
407
|
+
res();
|
|
408
|
+
} else
|
|
409
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
410
|
+
cb == null ? undefined : cb();
|
|
411
|
+
res();
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
}
|
|
348
415
|
|
|
349
416
|
// lib/crypto.ts
|
|
350
417
|
function compress(input, compressionFormat, outputType = "string") {
|
|
@@ -392,6 +459,8 @@ var UserUtils = (function (exports) {
|
|
|
392
459
|
});
|
|
393
460
|
}
|
|
394
461
|
function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase = true) {
|
|
462
|
+
if (radix < 2 || radix > 36)
|
|
463
|
+
throw new RangeError("The radix argument must be between 2 and 36");
|
|
395
464
|
let arr = [];
|
|
396
465
|
const caseArr = randomCase ? [0, 1] : [0];
|
|
397
466
|
if (enhancedEntropy) {
|
|
@@ -565,8 +634,7 @@ var UserUtils = (function (exports) {
|
|
|
565
634
|
lastFmtVer = oldFmtVer = ver;
|
|
566
635
|
} catch (err) {
|
|
567
636
|
if (!resetOnError)
|
|
568
|
-
throw new
|
|
569
|
-
console.error(`Error while running migration function for format version '${fmtVer}' - resetting to the default value.`, err);
|
|
637
|
+
throw new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
|
|
570
638
|
yield this.saveDefaultData();
|
|
571
639
|
return this.getData();
|
|
572
640
|
}
|
|
@@ -627,7 +695,6 @@ var UserUtils = (function (exports) {
|
|
|
627
695
|
return JSON.parse(decRes != null ? decRes : data);
|
|
628
696
|
});
|
|
629
697
|
}
|
|
630
|
-
//#region misc
|
|
631
698
|
/** Copies a JSON-compatible object and loses all its internal references in the process */
|
|
632
699
|
deepCopy(obj) {
|
|
633
700
|
return JSON.parse(JSON.stringify(obj));
|
|
@@ -679,7 +746,7 @@ var UserUtils = (function (exports) {
|
|
|
679
746
|
};
|
|
680
747
|
|
|
681
748
|
// lib/DataStoreSerializer.ts
|
|
682
|
-
var DataStoreSerializer = class {
|
|
749
|
+
var DataStoreSerializer = class _DataStoreSerializer {
|
|
683
750
|
constructor(stores, options = {}) {
|
|
684
751
|
__publicField(this, "stores");
|
|
685
752
|
__publicField(this, "options");
|
|
@@ -697,27 +764,25 @@ var UserUtils = (function (exports) {
|
|
|
697
764
|
return computeHash(input, "SHA-256");
|
|
698
765
|
});
|
|
699
766
|
}
|
|
700
|
-
/**
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
id: storeInst.id,
|
|
707
|
-
data,
|
|
708
|
-
formatVersion: storeInst.formatVersion,
|
|
709
|
-
encoded: storeInst.encodingEnabled(),
|
|
710
|
-
checksum
|
|
711
|
-
};
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
/** Serializes the data stores into a string */
|
|
715
|
-
serialize() {
|
|
767
|
+
/**
|
|
768
|
+
* Serializes the data stores into a string.
|
|
769
|
+
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
770
|
+
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
771
|
+
*/
|
|
772
|
+
serialize(useEncoding = true, stringified = true) {
|
|
716
773
|
return __async(this, null, function* () {
|
|
717
774
|
const serData = [];
|
|
718
|
-
for (const
|
|
719
|
-
|
|
720
|
-
|
|
775
|
+
for (const storeInst of this.stores) {
|
|
776
|
+
const data = useEncoding && storeInst.encodingEnabled() ? yield storeInst.encodeData(JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
|
|
777
|
+
serData.push({
|
|
778
|
+
id: storeInst.id,
|
|
779
|
+
data,
|
|
780
|
+
formatVersion: storeInst.formatVersion,
|
|
781
|
+
encoded: useEncoding && storeInst.encodingEnabled(),
|
|
782
|
+
checksum: this.options.addChecksum ? yield this.calcChecksum(data) : undefined
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
return stringified ? JSON.stringify(serData) : serData;
|
|
721
786
|
});
|
|
722
787
|
}
|
|
723
788
|
/**
|
|
@@ -726,7 +791,9 @@ var UserUtils = (function (exports) {
|
|
|
726
791
|
*/
|
|
727
792
|
deserialize(serializedData) {
|
|
728
793
|
return __async(this, null, function* () {
|
|
729
|
-
const deserStores = JSON.parse(serializedData);
|
|
794
|
+
const deserStores = typeof serializedData === "string" ? JSON.parse(serializedData) : serializedData;
|
|
795
|
+
if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStore))
|
|
796
|
+
throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
|
|
730
797
|
for (const storeData of deserStores) {
|
|
731
798
|
const storeInst = this.stores.find((s) => s.id === storeData.id);
|
|
732
799
|
if (!storeInst)
|
|
@@ -734,7 +801,7 @@ var UserUtils = (function (exports) {
|
|
|
734
801
|
if (this.options.ensureIntegrity && typeof storeData.checksum === "string") {
|
|
735
802
|
const checksum = yield this.calcChecksum(storeData.data);
|
|
736
803
|
if (checksum !== storeData.checksum)
|
|
737
|
-
throw new
|
|
804
|
+
throw new ChecksumMismatchError(`Checksum mismatch for DataStore with ID "${storeData.id}"!
|
|
738
805
|
Expected: ${storeData.checksum}
|
|
739
806
|
Has: ${checksum}`);
|
|
740
807
|
}
|
|
@@ -778,6 +845,10 @@ Has: ${checksum}`);
|
|
|
778
845
|
return Promise.allSettled(this.stores.map((store) => store.deleteData()));
|
|
779
846
|
});
|
|
780
847
|
}
|
|
848
|
+
/** Checks if a given value is a SerializedDataStore object */
|
|
849
|
+
static isSerializedDataStore(obj) {
|
|
850
|
+
return typeof obj === "object" && obj !== null && "id" in obj && "data" in obj && "formatVersion" in obj && "encoded" in obj;
|
|
851
|
+
}
|
|
781
852
|
};
|
|
782
853
|
|
|
783
854
|
// node_modules/.pnpm/nanoevents@9.1.0/node_modules/nanoevents/index.js
|
|
@@ -937,7 +1008,7 @@ Has: ${checksum}`);
|
|
|
937
1008
|
}, this.timeout);
|
|
938
1009
|
break;
|
|
939
1010
|
default:
|
|
940
|
-
throw new
|
|
1011
|
+
throw new TypeError(`Invalid debouncer type: ${this.type}`);
|
|
941
1012
|
}
|
|
942
1013
|
}
|
|
943
1014
|
};
|
|
@@ -1441,8 +1512,6 @@ Has: ${checksum}`);
|
|
|
1441
1512
|
}
|
|
1442
1513
|
|
|
1443
1514
|
// lib/SelectorObserver.ts
|
|
1444
|
-
var domLoaded = false;
|
|
1445
|
-
document.addEventListener("DOMContentLoaded", () => domLoaded = true);
|
|
1446
1515
|
var SelectorObserver = class {
|
|
1447
1516
|
constructor(baseElement, options = {}) {
|
|
1448
1517
|
__publicField(this, "enabled", false);
|
|
@@ -1483,7 +1552,7 @@ Has: ${checksum}`);
|
|
|
1483
1552
|
}
|
|
1484
1553
|
/** Call to check all selectors in the {@linkcode listenerMap} using {@linkcode checkSelector()} */
|
|
1485
1554
|
checkAllSelectors() {
|
|
1486
|
-
if (!this.enabled || !
|
|
1555
|
+
if (!this.enabled || !isDomLoaded())
|
|
1487
1556
|
return;
|
|
1488
1557
|
for (const [selector, listeners] of this.listenerMap.entries())
|
|
1489
1558
|
this.checkSelector(selector, listeners);
|
|
@@ -1627,9 +1696,9 @@ Has: ${checksum}`);
|
|
|
1627
1696
|
if (typeof language !== "string" || language.length === 0 || typeof trObj !== "object" || trObj === null)
|
|
1628
1697
|
return fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
|
|
1629
1698
|
const transformTrVal = (trKey, trValue) => {
|
|
1630
|
-
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(trValue));
|
|
1699
|
+
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(String(trValue)));
|
|
1631
1700
|
if (tfs.length === 0)
|
|
1632
|
-
return trValue;
|
|
1701
|
+
return String(trValue);
|
|
1633
1702
|
let retStr = String(trValue);
|
|
1634
1703
|
for (const tf of tfs) {
|
|
1635
1704
|
const re = new RegExp(tf.regex);
|
|
@@ -1778,15 +1847,20 @@ Has: ${checksum}`);
|
|
|
1778
1847
|
}
|
|
1779
1848
|
};
|
|
1780
1849
|
|
|
1850
|
+
exports.ChecksumMismatchError = ChecksumMismatchError;
|
|
1781
1851
|
exports.DataStore = DataStore;
|
|
1782
1852
|
exports.DataStoreSerializer = DataStoreSerializer;
|
|
1783
1853
|
exports.Debouncer = Debouncer;
|
|
1784
1854
|
exports.Dialog = Dialog;
|
|
1855
|
+
exports.MigrationError = MigrationError;
|
|
1785
1856
|
exports.NanoEmitter = NanoEmitter;
|
|
1857
|
+
exports.PlatformError = PlatformError;
|
|
1786
1858
|
exports.SelectorObserver = SelectorObserver;
|
|
1859
|
+
exports.UUError = UUError;
|
|
1787
1860
|
exports.addGlobalStyle = addGlobalStyle;
|
|
1788
1861
|
exports.addParent = addParent;
|
|
1789
1862
|
exports.autoPlural = autoPlural;
|
|
1863
|
+
exports.bitSetHas = bitSetHas;
|
|
1790
1864
|
exports.clamp = clamp;
|
|
1791
1865
|
exports.compress = compress;
|
|
1792
1866
|
exports.computeHash = computeHash;
|
|
@@ -1806,20 +1880,24 @@ Has: ${checksum}`);
|
|
|
1806
1880
|
exports.insertValues = insertValues;
|
|
1807
1881
|
exports.interceptEvent = interceptEvent;
|
|
1808
1882
|
exports.interceptWindowEvent = interceptWindowEvent;
|
|
1883
|
+
exports.isDomLoaded = isDomLoaded;
|
|
1809
1884
|
exports.isScrollable = isScrollable;
|
|
1810
1885
|
exports.lightenColor = lightenColor;
|
|
1811
1886
|
exports.mapRange = mapRange;
|
|
1812
1887
|
exports.observeElementProp = observeElementProp;
|
|
1888
|
+
exports.onDomLoad = onDomLoad;
|
|
1813
1889
|
exports.openDialogs = openDialogs;
|
|
1814
1890
|
exports.openInNewTab = openInNewTab;
|
|
1815
1891
|
exports.pauseFor = pauseFor;
|
|
1816
1892
|
exports.preloadImages = preloadImages;
|
|
1893
|
+
exports.probeElementStyle = probeElementStyle;
|
|
1817
1894
|
exports.randRange = randRange;
|
|
1818
1895
|
exports.randomId = randomId;
|
|
1819
1896
|
exports.randomItem = randomItem;
|
|
1820
1897
|
exports.randomItemIndex = randomItemIndex;
|
|
1821
1898
|
exports.randomizeArray = randomizeArray;
|
|
1822
1899
|
exports.rgbToHex = rgbToHex;
|
|
1900
|
+
exports.roundFixed = roundFixed;
|
|
1823
1901
|
exports.setInnerHtmlUnsafe = setInnerHtmlUnsafe;
|
|
1824
1902
|
exports.takeRandomItem = takeRandomItem;
|
|
1825
1903
|
exports.tr = tr;
|
package/dist/index.js
CHANGED
|
@@ -97,13 +97,21 @@ function randRange(...args) {
|
|
|
97
97
|
} else
|
|
98
98
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
99
99
|
}
|
|
100
|
-
function digitCount(num) {
|
|
100
|
+
function digitCount(num, withDecimals = true) {
|
|
101
101
|
num = Number(!["string", "number"].includes(typeof num) ? String(num) : num);
|
|
102
102
|
if (typeof num === "number" && isNaN(num))
|
|
103
103
|
return NaN;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
const [intPart, decPart] = num.toString().split(".");
|
|
105
|
+
const intDigits = intPart === "0" ? 1 : Math.floor(Math.log10(Math.abs(Number(intPart))) + 1);
|
|
106
|
+
const decDigits = withDecimals && decPart ? decPart.length : 0;
|
|
107
|
+
return intDigits + decDigits;
|
|
108
|
+
}
|
|
109
|
+
function roundFixed(num, fractionDigits) {
|
|
110
|
+
const scale = 10 ** fractionDigits;
|
|
111
|
+
return Math.round(num * scale) / scale;
|
|
112
|
+
}
|
|
113
|
+
function bitSetHas(bitSet, checkVal) {
|
|
114
|
+
return (bitSet & checkVal) === checkVal;
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
// lib/array.ts
|
|
@@ -128,7 +136,7 @@ function randomizeArray(array) {
|
|
|
128
136
|
if (array.length === 0)
|
|
129
137
|
return retArray;
|
|
130
138
|
for (let i = retArray.length - 1; i > 0; i--) {
|
|
131
|
-
const j = Math.floor(
|
|
139
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
132
140
|
[retArray[i], retArray[j]] = [retArray[j], retArray[i]];
|
|
133
141
|
}
|
|
134
142
|
return retArray;
|
|
@@ -171,10 +179,10 @@ function darkenColor(color, percent, upperCase = false) {
|
|
|
171
179
|
else if (color.startsWith("rgb")) {
|
|
172
180
|
const rgbValues = (_a = color.match(/\d+(\.\d+)?/g)) == null ? undefined : _a.map(Number);
|
|
173
181
|
if (!rgbValues)
|
|
174
|
-
throw new
|
|
182
|
+
throw new TypeError("Invalid RGB/RGBA color format");
|
|
175
183
|
[r, g, b, a] = rgbValues;
|
|
176
184
|
} else
|
|
177
|
-
throw new
|
|
185
|
+
throw new TypeError("Unsupported color format");
|
|
178
186
|
[r, g, b] = darkenRgb(r, g, b, percent);
|
|
179
187
|
if (isHexCol)
|
|
180
188
|
return rgbToHex(r, g, b, a, color.startsWith("#"), upperCase);
|
|
@@ -183,10 +191,39 @@ function darkenColor(color, percent, upperCase = false) {
|
|
|
183
191
|
else if (color.startsWith("rgb"))
|
|
184
192
|
return `rgb(${r}, ${g}, ${b})`;
|
|
185
193
|
else
|
|
186
|
-
throw new
|
|
194
|
+
throw new TypeError("Unsupported color format");
|
|
187
195
|
}
|
|
188
196
|
|
|
197
|
+
// lib/errors.ts
|
|
198
|
+
var UUError = class extends Error {
|
|
199
|
+
constructor(message, options) {
|
|
200
|
+
super(message, options);
|
|
201
|
+
__publicField(this, "date");
|
|
202
|
+
this.date = /* @__PURE__ */ new Date();
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var ChecksumMismatchError = class extends UUError {
|
|
206
|
+
constructor(message, options) {
|
|
207
|
+
super(message, options);
|
|
208
|
+
this.name = "ChecksumMismatchError";
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
var MigrationError = class extends UUError {
|
|
212
|
+
constructor(message, options) {
|
|
213
|
+
super(message, options);
|
|
214
|
+
this.name = "MigrationError";
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
var PlatformError = class extends UUError {
|
|
218
|
+
constructor(message, options) {
|
|
219
|
+
super(message, options);
|
|
220
|
+
this.name = "PlatformError";
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
189
224
|
// lib/dom.ts
|
|
225
|
+
var domReady = false;
|
|
226
|
+
document.addEventListener("DOMContentLoaded", () => domReady = true);
|
|
190
227
|
function getUnsafeWindow() {
|
|
191
228
|
try {
|
|
192
229
|
return unsafeWindow;
|
|
@@ -242,8 +279,8 @@ function openInNewTab(href, background, additionalProps) {
|
|
|
242
279
|
}
|
|
243
280
|
function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
244
281
|
var _a;
|
|
245
|
-
if ((
|
|
246
|
-
throw new
|
|
282
|
+
if (((_a = GM == null ? undefined : GM.info) == null ? undefined : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey" && (eventObject === window || eventObject === getUnsafeWindow()))
|
|
283
|
+
throw new PlatformError("Intercepting window events is not supported on FireMonkey due to the isolated context the userscript runs in.");
|
|
247
284
|
Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
|
|
248
285
|
if (isNaN(Error.stackTraceLimit))
|
|
249
286
|
Error.stackTraceLimit = 100;
|
|
@@ -325,6 +362,36 @@ function setInnerHtmlUnsafe(element, html) {
|
|
|
325
362
|
element.innerHTML = (_c = (_b = ttPolicy == null ? undefined : ttPolicy.createHTML) == null ? undefined : _b.call(ttPolicy, html)) != null ? _c : html;
|
|
326
363
|
return element;
|
|
327
364
|
}
|
|
365
|
+
function probeElementStyle(probeStyle, element, hideOffscreen = true, parentElement = document.body) {
|
|
366
|
+
const el = element ? typeof element === "function" ? element() : element : document.createElement("span");
|
|
367
|
+
if (hideOffscreen) {
|
|
368
|
+
el.style.position = "absolute";
|
|
369
|
+
el.style.left = "-9999px";
|
|
370
|
+
el.style.top = "-9999px";
|
|
371
|
+
el.style.zIndex = "-9999";
|
|
372
|
+
}
|
|
373
|
+
el.classList.add("_uu_probe_element");
|
|
374
|
+
parentElement.appendChild(el);
|
|
375
|
+
const style = window.getComputedStyle(el);
|
|
376
|
+
const result = probeStyle(style, el);
|
|
377
|
+
setTimeout(() => el.remove(), 1);
|
|
378
|
+
return result;
|
|
379
|
+
}
|
|
380
|
+
function isDomLoaded() {
|
|
381
|
+
return domReady;
|
|
382
|
+
}
|
|
383
|
+
function onDomLoad(cb) {
|
|
384
|
+
return new Promise((res) => {
|
|
385
|
+
if (domReady) {
|
|
386
|
+
cb == null ? undefined : cb();
|
|
387
|
+
res();
|
|
388
|
+
} else
|
|
389
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
390
|
+
cb == null ? undefined : cb();
|
|
391
|
+
res();
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
}
|
|
328
395
|
|
|
329
396
|
// lib/crypto.ts
|
|
330
397
|
function compress(input, compressionFormat, outputType = "string") {
|
|
@@ -372,6 +439,8 @@ function computeHash(input, algorithm = "SHA-256") {
|
|
|
372
439
|
});
|
|
373
440
|
}
|
|
374
441
|
function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase = true) {
|
|
442
|
+
if (radix < 2 || radix > 36)
|
|
443
|
+
throw new RangeError("The radix argument must be between 2 and 36");
|
|
375
444
|
let arr = [];
|
|
376
445
|
const caseArr = randomCase ? [0, 1] : [0];
|
|
377
446
|
if (enhancedEntropy) {
|
|
@@ -545,8 +614,7 @@ var DataStore = class {
|
|
|
545
614
|
lastFmtVer = oldFmtVer = ver;
|
|
546
615
|
} catch (err) {
|
|
547
616
|
if (!resetOnError)
|
|
548
|
-
throw new
|
|
549
|
-
console.error(`Error while running migration function for format version '${fmtVer}' - resetting to the default value.`, err);
|
|
617
|
+
throw new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
|
|
550
618
|
yield this.saveDefaultData();
|
|
551
619
|
return this.getData();
|
|
552
620
|
}
|
|
@@ -607,7 +675,6 @@ var DataStore = class {
|
|
|
607
675
|
return JSON.parse(decRes != null ? decRes : data);
|
|
608
676
|
});
|
|
609
677
|
}
|
|
610
|
-
//#region misc
|
|
611
678
|
/** Copies a JSON-compatible object and loses all its internal references in the process */
|
|
612
679
|
deepCopy(obj) {
|
|
613
680
|
return JSON.parse(JSON.stringify(obj));
|
|
@@ -659,7 +726,7 @@ var DataStore = class {
|
|
|
659
726
|
};
|
|
660
727
|
|
|
661
728
|
// lib/DataStoreSerializer.ts
|
|
662
|
-
var DataStoreSerializer = class {
|
|
729
|
+
var DataStoreSerializer = class _DataStoreSerializer {
|
|
663
730
|
constructor(stores, options = {}) {
|
|
664
731
|
__publicField(this, "stores");
|
|
665
732
|
__publicField(this, "options");
|
|
@@ -677,27 +744,25 @@ var DataStoreSerializer = class {
|
|
|
677
744
|
return computeHash(input, "SHA-256");
|
|
678
745
|
});
|
|
679
746
|
}
|
|
680
|
-
/**
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
id: storeInst.id,
|
|
687
|
-
data,
|
|
688
|
-
formatVersion: storeInst.formatVersion,
|
|
689
|
-
encoded: storeInst.encodingEnabled(),
|
|
690
|
-
checksum
|
|
691
|
-
};
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
/** Serializes the data stores into a string */
|
|
695
|
-
serialize() {
|
|
747
|
+
/**
|
|
748
|
+
* Serializes the data stores into a string.
|
|
749
|
+
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
750
|
+
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
751
|
+
*/
|
|
752
|
+
serialize(useEncoding = true, stringified = true) {
|
|
696
753
|
return __async(this, null, function* () {
|
|
697
754
|
const serData = [];
|
|
698
|
-
for (const
|
|
699
|
-
|
|
700
|
-
|
|
755
|
+
for (const storeInst of this.stores) {
|
|
756
|
+
const data = useEncoding && storeInst.encodingEnabled() ? yield storeInst.encodeData(JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
|
|
757
|
+
serData.push({
|
|
758
|
+
id: storeInst.id,
|
|
759
|
+
data,
|
|
760
|
+
formatVersion: storeInst.formatVersion,
|
|
761
|
+
encoded: useEncoding && storeInst.encodingEnabled(),
|
|
762
|
+
checksum: this.options.addChecksum ? yield this.calcChecksum(data) : undefined
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
return stringified ? JSON.stringify(serData) : serData;
|
|
701
766
|
});
|
|
702
767
|
}
|
|
703
768
|
/**
|
|
@@ -706,7 +771,9 @@ var DataStoreSerializer = class {
|
|
|
706
771
|
*/
|
|
707
772
|
deserialize(serializedData) {
|
|
708
773
|
return __async(this, null, function* () {
|
|
709
|
-
const deserStores = JSON.parse(serializedData);
|
|
774
|
+
const deserStores = typeof serializedData === "string" ? JSON.parse(serializedData) : serializedData;
|
|
775
|
+
if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStore))
|
|
776
|
+
throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
|
|
710
777
|
for (const storeData of deserStores) {
|
|
711
778
|
const storeInst = this.stores.find((s) => s.id === storeData.id);
|
|
712
779
|
if (!storeInst)
|
|
@@ -714,7 +781,7 @@ var DataStoreSerializer = class {
|
|
|
714
781
|
if (this.options.ensureIntegrity && typeof storeData.checksum === "string") {
|
|
715
782
|
const checksum = yield this.calcChecksum(storeData.data);
|
|
716
783
|
if (checksum !== storeData.checksum)
|
|
717
|
-
throw new
|
|
784
|
+
throw new ChecksumMismatchError(`Checksum mismatch for DataStore with ID "${storeData.id}"!
|
|
718
785
|
Expected: ${storeData.checksum}
|
|
719
786
|
Has: ${checksum}`);
|
|
720
787
|
}
|
|
@@ -758,6 +825,10 @@ Has: ${checksum}`);
|
|
|
758
825
|
return Promise.allSettled(this.stores.map((store) => store.deleteData()));
|
|
759
826
|
});
|
|
760
827
|
}
|
|
828
|
+
/** Checks if a given value is a SerializedDataStore object */
|
|
829
|
+
static isSerializedDataStore(obj) {
|
|
830
|
+
return typeof obj === "object" && obj !== null && "id" in obj && "data" in obj && "formatVersion" in obj && "encoded" in obj;
|
|
831
|
+
}
|
|
761
832
|
};
|
|
762
833
|
var NanoEmitter = class {
|
|
763
834
|
constructor(options = {}) {
|
|
@@ -897,7 +968,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
897
968
|
}, this.timeout);
|
|
898
969
|
break;
|
|
899
970
|
default:
|
|
900
|
-
throw new
|
|
971
|
+
throw new TypeError(`Invalid debouncer type: ${this.type}`);
|
|
901
972
|
}
|
|
902
973
|
}
|
|
903
974
|
};
|
|
@@ -1401,8 +1472,6 @@ function getListLength(obj, zeroOnInvalid = true) {
|
|
|
1401
1472
|
}
|
|
1402
1473
|
|
|
1403
1474
|
// lib/SelectorObserver.ts
|
|
1404
|
-
var domLoaded = false;
|
|
1405
|
-
document.addEventListener("DOMContentLoaded", () => domLoaded = true);
|
|
1406
1475
|
var SelectorObserver = class {
|
|
1407
1476
|
constructor(baseElement, options = {}) {
|
|
1408
1477
|
__publicField(this, "enabled", false);
|
|
@@ -1443,7 +1512,7 @@ var SelectorObserver = class {
|
|
|
1443
1512
|
}
|
|
1444
1513
|
/** Call to check all selectors in the {@linkcode listenerMap} using {@linkcode checkSelector()} */
|
|
1445
1514
|
checkAllSelectors() {
|
|
1446
|
-
if (!this.enabled || !
|
|
1515
|
+
if (!this.enabled || !isDomLoaded())
|
|
1447
1516
|
return;
|
|
1448
1517
|
for (const [selector, listeners] of this.listenerMap.entries())
|
|
1449
1518
|
this.checkSelector(selector, listeners);
|
|
@@ -1587,9 +1656,9 @@ function translate(language, key, ...trArgs) {
|
|
|
1587
1656
|
if (typeof language !== "string" || language.length === 0 || typeof trObj !== "object" || trObj === null)
|
|
1588
1657
|
return fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
|
|
1589
1658
|
const transformTrVal = (trKey, trValue) => {
|
|
1590
|
-
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(trValue));
|
|
1659
|
+
const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(String(trValue)));
|
|
1591
1660
|
if (tfs.length === 0)
|
|
1592
|
-
return trValue;
|
|
1661
|
+
return String(trValue);
|
|
1593
1662
|
let retStr = String(trValue);
|
|
1594
1663
|
for (const tf of tfs) {
|
|
1595
1664
|
const re = new RegExp(tf.regex);
|
|
@@ -1738,4 +1807,4 @@ var tr = {
|
|
|
1738
1807
|
}
|
|
1739
1808
|
};
|
|
1740
1809
|
|
|
1741
|
-
export { DataStore, DataStoreSerializer, Debouncer, Dialog, NanoEmitter, SelectorObserver, addGlobalStyle, addParent, autoPlural, clamp, compress, computeHash, consumeGen, consumeStringGen, currentDialogId, darkenColor, debounce, decompress, defaultDialogCss, defaultStrings, digitCount, fetchAdvanced, getListLength, getSiblingsFrame, getUnsafeWindow, hexToRgb, insertValues, interceptEvent, interceptWindowEvent, isScrollable, lightenColor, mapRange, observeElementProp, openDialogs, openInNewTab, pauseFor, preloadImages, randRange, randomId, randomItem, randomItemIndex, randomizeArray, rgbToHex, setInnerHtmlUnsafe, takeRandomItem, tr };
|
|
1810
|
+
export { ChecksumMismatchError, DataStore, DataStoreSerializer, Debouncer, Dialog, MigrationError, NanoEmitter, PlatformError, SelectorObserver, UUError, addGlobalStyle, addParent, autoPlural, bitSetHas, clamp, compress, computeHash, consumeGen, consumeStringGen, currentDialogId, darkenColor, debounce, decompress, defaultDialogCss, defaultStrings, digitCount, fetchAdvanced, getListLength, getSiblingsFrame, getUnsafeWindow, hexToRgb, insertValues, interceptEvent, interceptWindowEvent, isDomLoaded, isScrollable, lightenColor, mapRange, observeElementProp, onDomLoad, openDialogs, openInNewTab, pauseFor, preloadImages, probeElementStyle, randRange, randomId, randomItem, randomItemIndex, randomizeArray, rgbToHex, roundFixed, setInnerHtmlUnsafe, takeRandomItem, tr };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module lib/DataStoreSerializer
|
|
3
3
|
* This module contains the DataStoreSerializer class, which allows you to import and export serialized DataStore data - [see the documentation for more info](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#datastoreserializer)
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import type { DataStore } from "./DataStore.js";
|
|
6
6
|
export type DataStoreSerializerOptions = {
|
|
7
7
|
/** Whether to add a checksum to the exported data */
|
|
8
8
|
addChecksum?: boolean;
|
|
@@ -43,15 +43,23 @@ export declare class DataStoreSerializer {
|
|
|
43
43
|
constructor(stores: DataStore[], options?: DataStoreSerializerOptions);
|
|
44
44
|
/** Calculates the checksum of a string */
|
|
45
45
|
protected calcChecksum(input: string): Promise<string>;
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Serializes the data stores into a string.
|
|
48
|
+
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
49
|
+
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
50
|
+
*/
|
|
51
|
+
serialize(useEncoding: boolean, stringified: true): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Serializes the data stores into a string.
|
|
54
|
+
* @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
|
|
55
|
+
* @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
|
|
56
|
+
*/
|
|
57
|
+
serialize(useEncoding: boolean, stringified: false): Promise<SerializedDataStore[]>;
|
|
50
58
|
/**
|
|
51
59
|
* Deserializes the data exported via {@linkcode serialize()} and imports it into the DataStore instances.
|
|
52
60
|
* Also triggers the migration process if the data format has changed.
|
|
53
61
|
*/
|
|
54
|
-
deserialize(serializedData: string): Promise<void>;
|
|
62
|
+
deserialize(serializedData: string | SerializedDataStore[]): Promise<void>;
|
|
55
63
|
/**
|
|
56
64
|
* Loads the persistent data of the DataStore instances into the in-memory cache.
|
|
57
65
|
* Also triggers the migration process if the data format has changed.
|
|
@@ -65,4 +73,6 @@ export declare class DataStoreSerializer {
|
|
|
65
73
|
* Leaves the in-memory data untouched.
|
|
66
74
|
*/
|
|
67
75
|
deleteStoresData(): Promise<PromiseSettledResult<void>[]>;
|
|
76
|
+
/** Checks if a given value is a SerializedDataStore object */
|
|
77
|
+
static isSerializedDataStore(obj: unknown): obj is SerializedDataStore;
|
|
68
78
|
}
|
package/dist/lib/dom.d.ts
CHANGED
|
@@ -76,3 +76,22 @@ export declare function getSiblingsFrame<TSibling extends Element = HTMLElement>
|
|
|
76
76
|
* - ⚠️ This function does not perform any sanitization and should thus be used with utmost caution, as it can easily lead to XSS vulnerabilities!
|
|
77
77
|
*/
|
|
78
78
|
export declare function setInnerHtmlUnsafe<TElement extends Element = HTMLElement>(element: TElement, html: string): TElement;
|
|
79
|
+
/**
|
|
80
|
+
* Creates an invisible temporary element to probe its rendered style.
|
|
81
|
+
* Has to be run after the `DOMContentLoaded` event has fired on the document object.
|
|
82
|
+
* @param probeStyle Function to probe the element's style. First argument is the element's style object from [`window.getComputedStyle()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle), second argument is the element itself
|
|
83
|
+
* @param element The element to probe, or a function that creates and returns the element - should not be added to the DOM prior to calling this function! - all probe elements will have the class `_uu_probe_element` added to them
|
|
84
|
+
* @param hideOffscreen Whether to hide the element offscreen, enabled by default - disable if you want to probe the position style properties of the element
|
|
85
|
+
* @param parentElement The parent element to append the probe element to, defaults to `document.body`
|
|
86
|
+
* @returns The value returned by the `probeElement` function
|
|
87
|
+
*/
|
|
88
|
+
export declare function probeElementStyle<TValue, TElem extends HTMLElement = HTMLSpanElement>(probeStyle: (style: CSSStyleDeclaration, element: TElem) => TValue, element?: TElem | (() => TElem), hideOffscreen?: boolean, parentElement?: HTMLElement): TValue;
|
|
89
|
+
/** Returns whether or not the DOM has finished loading */
|
|
90
|
+
export declare function isDomLoaded(): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Executes a callback and/or resolves the returned Promise when the DOM has finished loading.
|
|
93
|
+
* Immediately executes/resolves if the DOM is already loaded.
|
|
94
|
+
* @param cb Callback to execute when the DOM has finished loading
|
|
95
|
+
* @returns Returns a Promise that resolves when the DOM has finished loading
|
|
96
|
+
*/
|
|
97
|
+
export declare function onDomLoad(cb?: () => void): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module lib/errors
|
|
3
|
+
* Contains custom error classes
|
|
4
|
+
*/
|
|
5
|
+
/** Base class for all UserUtils errors - adds a `date` prop set to the error throw time */
|
|
6
|
+
export declare class UUError extends Error {
|
|
7
|
+
readonly date: Date;
|
|
8
|
+
constructor(message: string, options?: ErrorOptions);
|
|
9
|
+
}
|
|
10
|
+
/** Error while validating checksum */
|
|
11
|
+
export declare class ChecksumMismatchError extends UUError {
|
|
12
|
+
constructor(message: string, options?: ErrorOptions);
|
|
13
|
+
}
|
|
14
|
+
/** Error while migrating data */
|
|
15
|
+
export declare class MigrationError extends UUError {
|
|
16
|
+
constructor(message: string, options?: ErrorOptions);
|
|
17
|
+
}
|
|
18
|
+
/** Error due to the platform, like using a feature that's not supported by the browser */
|
|
19
|
+
export declare class PlatformError extends UUError {
|
|
20
|
+
constructor(message: string, options?: ErrorOptions);
|
|
21
|
+
}
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./DataStoreSerializer.js";
|
|
|
10
10
|
export * from "./Debouncer.js";
|
|
11
11
|
export * from "./Dialog.js";
|
|
12
12
|
export * from "./dom.js";
|
|
13
|
+
export * from "./errors.js";
|
|
13
14
|
export * from "./math.js";
|
|
14
15
|
export * from "./misc.js";
|
|
15
16
|
export * from "./NanoEmitter.js";
|
package/dist/lib/math.d.ts
CHANGED
|
@@ -27,5 +27,55 @@ export declare function randRange(min: number, max: number, enhancedEntropy?: bo
|
|
|
27
27
|
* Set {@linkcode enhancedEntropy} to true to use `crypto.getRandomValues()` for better cryptographic randomness (this also makes it take longer to generate)
|
|
28
28
|
*/
|
|
29
29
|
export declare function randRange(max: number, enhancedEntropy?: boolean): number;
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Calculates the amount of digits in the given number - the given number or string will be passed to the `Number()` constructor.
|
|
32
|
+
* Returns NaN if the number is invalid.
|
|
33
|
+
* @param num The number to count the digits of
|
|
34
|
+
* @param withDecimals Whether to count the decimal places as well (defaults to true)
|
|
35
|
+
* @example ```ts
|
|
36
|
+
* digitCount(); // NaN
|
|
37
|
+
* digitCount(0); // 1
|
|
38
|
+
* digitCount(123); // 3
|
|
39
|
+
* digitCount(123.456); // 6
|
|
40
|
+
* digitCount(Infinity); // Infinity
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function digitCount(num: number | Stringifiable, withDecimals?: boolean): number;
|
|
44
|
+
/**
|
|
45
|
+
* Rounds {@linkcode num} to a fixed amount of decimal places, specified by {@linkcode fractionDigits} (supports negative values to round to the nearest power of 10).
|
|
46
|
+
* @example ```ts
|
|
47
|
+
* roundFixed(234.567, -2); // 200
|
|
48
|
+
* roundFixed(234.567, -1); // 230
|
|
49
|
+
* roundFixed(234.567, 0); // 235
|
|
50
|
+
* roundFixed(234.567, 1); // 234.6
|
|
51
|
+
* roundFixed(234.567, 2); // 234.57
|
|
52
|
+
* roundFixed(234.567, 3); // 234.567
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function roundFixed(num: number, fractionDigits: number): number;
|
|
56
|
+
/**
|
|
57
|
+
* Checks if the {@linkcode bitSet} has the {@linkcode checkVal} set
|
|
58
|
+
* @example ```ts
|
|
59
|
+
* // the two vertically adjacent bits are tested for:
|
|
60
|
+
* bitSetHas(
|
|
61
|
+
* 0b1110,
|
|
62
|
+
* 0b0010,
|
|
63
|
+
* ); // true
|
|
64
|
+
*
|
|
65
|
+
* bitSetHas(
|
|
66
|
+
* 0b1110,
|
|
67
|
+
* 0b0001,
|
|
68
|
+
* ); // false
|
|
69
|
+
*
|
|
70
|
+
* // with TS enums (or JS maps):
|
|
71
|
+
* enum MyEnum {
|
|
72
|
+
* A = 1, B = 2, C = 4,
|
|
73
|
+
* D = 8, E = 16, F = 32,
|
|
74
|
+
* }
|
|
75
|
+
*
|
|
76
|
+
* const myBitSet = MyEnum.A | MyEnum.B;
|
|
77
|
+
* bitSetHas(myBitSet, MyEnum.B); // true
|
|
78
|
+
* bitSetHas(myBitSet, MyEnum.F); // false
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function bitSetHas<TType extends number | bigint>(bitSet: TType, checkVal: TType): boolean;
|
|
@@ -120,35 +120,42 @@ declare function getFallbackLanguage(): string | undefined;
|
|
|
120
120
|
* After all %n-formatted values have been injected, the transform functions will be called sequentially in the order they were added.
|
|
121
121
|
* @example
|
|
122
122
|
* ```ts
|
|
123
|
-
* tr
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
123
|
+
* import { tr, type TrKeys } from "@sv443-network/userutils";
|
|
124
|
+
*
|
|
125
|
+
* const transEn = {
|
|
126
|
+
* "headline": {
|
|
127
|
+
* "basic": "Hello, ${USERNAME}",
|
|
128
|
+
* "html": "Hello, ${USERNAME}<br><c=red>You have ${UNREAD_NOTIFS} unread notifications.</c>"
|
|
127
129
|
* }
|
|
128
|
-
* }
|
|
130
|
+
* } as const;
|
|
131
|
+
*
|
|
132
|
+
* tr.addTranslations("en", transEn);
|
|
129
133
|
*
|
|
130
|
-
* // replace ${PATTERN}
|
|
131
|
-
* tr.addTransform(
|
|
134
|
+
* // replace ${PATTERN} with predefined values
|
|
135
|
+
* tr.addTransform(/\$\{([A-Z_]+)\}/g, ({ matches }) => {
|
|
132
136
|
* switch(matches?.[1]) {
|
|
133
|
-
* default:
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* case "
|
|
137
|
+
* default:
|
|
138
|
+
* return `[UNKNOWN: ${matches?.[1]}]`;
|
|
139
|
+
* // these would be grabbed from elsewhere in the application, like a DataStore, global state or variable:
|
|
140
|
+
* case "USERNAME":
|
|
141
|
+
* return "JohnDoe45";
|
|
142
|
+
* case "UNREAD_NOTIFS":
|
|
143
|
+
* return 5;
|
|
137
144
|
* }
|
|
138
145
|
* });
|
|
139
146
|
*
|
|
140
|
-
* // replace <c=red>...</c> with <span
|
|
147
|
+
* // replace <c=red>...</c> with <span style="color: red;">...</span>
|
|
141
148
|
* tr.addTransform(/<c=([a-z]+)>(.*?)<\/c>/g, ({ matches }) => {
|
|
142
149
|
* const color = matches?.[1];
|
|
143
150
|
* const content = matches?.[2];
|
|
144
151
|
*
|
|
145
|
-
* return
|
|
152
|
+
* return `<span style="color: ${color};">${content}</span>`;
|
|
146
153
|
* });
|
|
147
154
|
*
|
|
148
|
-
* tr.
|
|
155
|
+
* const t = tr.use<TrKeys<typeof transEn>>("en");
|
|
149
156
|
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
157
|
+
* t("headline.basic"); // "Hello, JohnDoe45"
|
|
158
|
+
* t("headline.html"); // "Hello, JohnDoe45<br><span style="color: red;">You have 5 unread notifications.</span>"
|
|
152
159
|
* ```
|
|
153
160
|
* @param args A tuple containing the regular expression to match and the transform function to call if the pattern is found in a translation string
|
|
154
161
|
*/
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sv443-network/userutils",
|
|
3
3
|
"libName": "UserUtils",
|
|
4
|
-
"version": "9.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "9.2.0",
|
|
5
|
+
"description": "General purpose DOM/GreaseMonkey library that allows you to register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and much more",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
8
8
|
"types": "dist/lib/index.d.ts",
|