@sv443-network/userutils 9.3.0 → 9.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +22 -11
- package/dist/index.cjs +211 -30
- package/dist/index.global.js +212 -31
- package/dist/index.js +211 -31
- package/dist/lib/DataStoreSerializer.d.ts +22 -11
- package/dist/lib/Debouncer.d.ts +2 -0
- package/dist/lib/Mixins.d.ts +127 -0
- package/dist/lib/NanoEmitter.d.ts +52 -5
- package/dist/lib/dom.d.ts +1 -1
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/translation.d.ts +1 -1
- package/package.json +18 -6
- package/README-summary.md +0 -214
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @sv443-network/userutils
|
|
2
2
|
|
|
3
|
+
## 9.4.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- bb40243: Fixed includes for JSR
|
|
8
|
+
|
|
9
|
+
## 9.4.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 5075831: Added `Mixins` class for allowing multiple sources to modify values in a controlled way
|
|
14
|
+
- 99dedfd: Added unit tests
|
|
15
|
+
- 7530fd0: Added `Debouncer.getListeners()` method to get an array of all listener functions
|
|
16
|
+
- 48306da: Added `stores` filter parameter to the `DataStoreSerializer` methods `loadStoresData()`, `resetStoresData()` and `deleteStoresData()`
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- f6a68c7: Fixed error when calling `interceptEvent()` in a non-GM environment
|
|
21
|
+
|
|
3
22
|
## 9.3.0
|
|
4
23
|
|
|
5
24
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<!-- #region Description -->
|
|
4
4
|
## UserUtils
|
|
5
5
|
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
|
-
Contains builtin TypeScript declarations. Supports ESM and CJS imports via a bundler and global declaration via `@require`
|
|
6
|
+
Contains builtin TypeScript declarations. Supports ESM and CJS imports via a bundler and global declaration via `@require` or `<script>`
|
|
7
7
|
The library works in any DOM environment with or without the [GreaseMonkey API](https://wiki.greasespot.net/Greasemonkey_Manual:API), but some features will be unavailable or limited.
|
|
8
8
|
|
|
9
9
|
You may want to check out my [template for userscripts in TypeScript](https://github.com/Sv443/Userscript.ts) that you can use to get started quickly. It also includes this library by default.
|
|
@@ -11,22 +11,32 @@ If you like using this library, please consider [supporting the development ❤
|
|
|
11
11
|
|
|
12
12
|
<br>
|
|
13
13
|
|
|
14
|
-
[](https://bundlephobia.com/package/@sv443-network/userutils)
|
|
15
|
+
[](https://coveralls.io/github/Sv443-Network/UserUtils)
|
|
16
|
+
[](https://bundlephobia.com/package/@sv443-network/userutils)
|
|
17
|
+
[](https://bundlephobia.com/package/@sv443-network/userutils)
|
|
17
18
|
|
|
18
|
-
[](https://dc.sv443.net/)
|
|
20
|
+
[](https://github.com/Sv443-Network/UserUtils/stargazers)
|
|
21
|
+
[](https://github.com/sponsors/Sv443)
|
|
20
22
|
|
|
21
|
-
<
|
|
22
|
-
View the documentation of previous major releases:
|
|
23
|
-
</sup>
|
|
23
|
+
<br>
|
|
24
24
|
<sub>
|
|
25
25
|
|
|
26
|
+
View the documentation of previous releases:
|
|
27
|
+
|
|
28
|
+
<a href="https://github.com/Sv443-Network/UserUtils/blob/v8.0.0/README.md" title="View docs for major version 8.0.0" rel="noopener noreferrer">**v8** </a><sup>(<a title="View docs for minor version 8.4.0" href="https://github.com/Sv443-Network/UserUtils/blob/v8.4.0/README.md" rel="noopener noreferrer">8.4.0</a>)</sup>
|
|
29
|
+
• <a href="https://github.com/Sv443-Network/UserUtils/blob/v7.0.0/README.md" title="View docs for major version 7.0.0" rel="noopener noreferrer">**v7** </a><sup>(<a href="https://github.com/Sv443-Network/UserUtils/blob/v7.3.0/README.md" title="View docs for minor version 7.3.0" rel="noopener noreferrer">7.3.0</a>)</sup>
|
|
30
|
+
• <a href="https://github.com/Sv443-Network/UserUtils/blob/v6.0.0/README.md" title="View docs for major version 6.0.0" rel="noopener noreferrer">**v6** </a><sup>(<a href="https://github.com/Sv443-Network/UserUtils/blob/v6.3.0/README.md" title="View docs for minor version 6.3.0" rel="noopener noreferrer">6.3.0</a>)</sup>
|
|
31
|
+
• <a href="https://github.com/Sv443-Network/UserUtils/blob/v5.0.0/README.md" title="View docs for major version 5.0.0" rel="noopener noreferrer">**v5** </a><sup>(<a href="https://github.com/Sv443-Network/UserUtils/blob/v5.0.1/README.md" title="View docs for patch version 5.0.1" rel="noopener noreferrer">5.0.1</a>)</sup>
|
|
32
|
+
• <a href="https://github.com/Sv443-Network/UserUtils/blob/v4.0.0/README.md" title="View docs for major version 4.0.0" rel="noopener noreferrer">**v4** </a><sup>(<a href="https://github.com/Sv443-Network/UserUtils/blob/v4.2.1/README.md" title="View docs for patch version 4.2.1" rel="noopener noreferrer">4.2.1</a>)</sup>
|
|
33
|
+
• <a href="https://github.com/Sv443-Network/UserUtils/blob/v3.0.0/README.md" title="View docs for major version 3.0.0" rel="noopener noreferrer">**v3**</a>
|
|
34
|
+
• <a href="https://github.com/Sv443-Network/UserUtils/blob/v2.0.0/README.md" title="View docs for major version 2.0.0" rel="noopener noreferrer">**v2** </a><sup>(<a href="https://github.com/Sv443-Network/UserUtils/blob/v2.0.1/README.md" title="View docs for patch version 2.0.1" rel="noopener noreferrer">2.0.1</a>)</sup>
|
|
35
|
+
• <a href="https://github.com/Sv443-Network/UserUtils/blob/v1.0.0/README.md" title="View docs for major version 1.0.0" rel="noopener noreferrer">**v1** </a><sup>(<a href="https://github.com/Sv443-Network/UserUtils/blob/v1.2.0/README.md" title="View docs for minor version 1.2.0" rel="noopener noreferrer">1.2.0</a>)</sup>
|
|
36
|
+
• <a href="https://github.com/Sv443-Network/UserUtils/blob/v0.5.3/README.md" title="View docs for patch version 0.5.3" rel="noopener noreferrer">**v0.5.3**</a>
|
|
26
37
|
<!-- <a href="https://github.com/Sv443-Network/UserUtils/blob/vX.0.0/docs.md" rel="noopener noreferrer">X.0.0</a>, -->
|
|
27
|
-
<a href="https://github.com/Sv443-Network/UserUtils/blob/v8.0.0/README.md" rel="noopener noreferrer">8.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v7.0.0/README.md" rel="noopener noreferrer">7.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v6.0.0/README.md" rel="noopener noreferrer">6.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v5.0.0/README.md" rel="noopener noreferrer">5.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v4.0.0/README.md" rel="noopener noreferrer">4.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v3.0.0/README.md" rel="noopener noreferrer">3.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v2.0.0/README.md" rel="noopener noreferrer">2.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v1.0.0/README.md" rel="noopener noreferrer">1.0.0</a>, <a href="https://github.com/Sv443-Network/UserUtils/blob/v0.5.3/README.md" rel="noopener noreferrer">0.5.3</a>
|
|
28
|
-
|
|
29
38
|
</sub>
|
|
39
|
+
|
|
30
40
|
</div>
|
|
31
41
|
<br>
|
|
32
42
|
|
|
@@ -63,6 +73,7 @@ View the documentation of previous major releases:
|
|
|
63
73
|
- [`DataStore`](./docs.md#datastore) - class that manages a hybrid sync & async persistent JSON database, including data migration
|
|
64
74
|
- [`DataStoreSerializer`](./docs.md#datastoreserializer) - class for importing & exporting data of multiple DataStore instances, including compression, checksumming and running migrations
|
|
65
75
|
- [`Dialog`](./docs.md#dialog) - class for creating custom modal dialogs with a promise-based API and a generic, default style
|
|
76
|
+
- [`Mixins`](./docs.md#mixins) - class for creating mixin functions that allow multiple sources to modify a target value in a highly flexible way
|
|
66
77
|
- [`NanoEmitter`](./docs.md#nanoemitter) - tiny event emitter class with a focus on performance and simplicity (based on [nanoevents](https://npmjs.com/package/nanoevents))
|
|
67
78
|
- [`Debouncer`](./docs.md#debouncer) - class for debouncing function calls with a given timeout
|
|
68
79
|
- [`debounce()`](./docs.md#debounce) - function wrapper for the Debouncer class for easier usage
|
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
var nanoevents = require('nanoevents');
|
|
4
4
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
|
+
var __defProps = Object.defineProperties;
|
|
7
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
8
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
7
9
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
10
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
@@ -18,6 +20,7 @@ var __spreadValues = (a, b) => {
|
|
|
18
20
|
}
|
|
19
21
|
return a;
|
|
20
22
|
};
|
|
23
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
21
24
|
var __objRest = (source, exclude) => {
|
|
22
25
|
var target = {};
|
|
23
26
|
for (var prop in source)
|
|
@@ -250,16 +253,16 @@ function addGlobalStyle(style) {
|
|
|
250
253
|
function preloadImages(srcUrls, rejects = false) {
|
|
251
254
|
const promises = srcUrls.map((src) => new Promise((res, rej) => {
|
|
252
255
|
const image = new Image();
|
|
253
|
-
image.src = src;
|
|
254
256
|
image.addEventListener("load", () => res(image));
|
|
255
257
|
image.addEventListener("error", (evt) => rejects && rej(evt));
|
|
258
|
+
image.src = src;
|
|
256
259
|
}));
|
|
257
260
|
return Promise.allSettled(promises);
|
|
258
261
|
}
|
|
259
262
|
function openInNewTab(href, background, additionalProps) {
|
|
260
|
-
var _a;
|
|
261
263
|
try {
|
|
262
|
-
(
|
|
264
|
+
if (typeof window.GM === "object")
|
|
265
|
+
GM.openInTab(href, background);
|
|
263
266
|
} catch (e) {
|
|
264
267
|
const openElem = document.createElement("a");
|
|
265
268
|
Object.assign(openElem, __spreadValues({
|
|
@@ -276,12 +279,17 @@ function openInNewTab(href, background, additionalProps) {
|
|
|
276
279
|
});
|
|
277
280
|
document.body.appendChild(openElem);
|
|
278
281
|
openElem.click();
|
|
279
|
-
setTimeout(
|
|
282
|
+
setTimeout(() => {
|
|
283
|
+
try {
|
|
284
|
+
openElem.remove();
|
|
285
|
+
} catch (e2) {
|
|
286
|
+
}
|
|
287
|
+
}, 0);
|
|
280
288
|
}
|
|
281
289
|
}
|
|
282
290
|
function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
283
291
|
var _a;
|
|
284
|
-
if (((_a = GM == null ? undefined : GM.info) == null ? undefined : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey" && (eventObject === window || eventObject === getUnsafeWindow()))
|
|
292
|
+
if (typeof window.GM === "object" && ((_a = GM == null ? undefined : GM.info) == null ? undefined : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey" && (eventObject === window || eventObject === getUnsafeWindow()))
|
|
285
293
|
throw new PlatformError("Intercepting window events is not supported on FireMonkey due to the isolated context the userscript runs in.");
|
|
286
294
|
Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
|
|
287
295
|
if (isNaN(Error.stackTraceLimit))
|
|
@@ -441,6 +449,8 @@ function computeHash(input, algorithm = "SHA-256") {
|
|
|
441
449
|
});
|
|
442
450
|
}
|
|
443
451
|
function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase = true) {
|
|
452
|
+
if (length < 1)
|
|
453
|
+
throw new RangeError("The length argument must be at least 1");
|
|
444
454
|
if (radix < 2 || radix > 36)
|
|
445
455
|
throw new RangeError("The radix argument must be between 2 and 36");
|
|
446
456
|
let arr = [];
|
|
@@ -785,7 +795,7 @@ var DataStoreSerializer = class _DataStoreSerializer {
|
|
|
785
795
|
deserializePartial(stores, data) {
|
|
786
796
|
return __async(this, null, function* () {
|
|
787
797
|
const deserStores = typeof data === "string" ? JSON.parse(data) : data;
|
|
788
|
-
if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.
|
|
798
|
+
if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStoreObj))
|
|
789
799
|
throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
|
|
790
800
|
for (const storeData of deserStores.filter((s) => typeof stores === "function" ? stores(s.id) : stores.includes(s.id))) {
|
|
791
801
|
const storeInst = this.stores.find((s) => s.id === storeData.id);
|
|
@@ -818,41 +828,59 @@ Has: ${checksum}`);
|
|
|
818
828
|
/**
|
|
819
829
|
* Loads the persistent data of the DataStore instances into the in-memory cache.
|
|
820
830
|
* Also triggers the migration process if the data format has changed.
|
|
831
|
+
* @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be loaded
|
|
821
832
|
* @returns Returns a PromiseSettledResult array with the results of each DataStore instance in the format `{ id: string, data: object }`
|
|
822
833
|
*/
|
|
823
|
-
loadStoresData() {
|
|
834
|
+
loadStoresData(stores) {
|
|
824
835
|
return __async(this, null, function* () {
|
|
825
|
-
return Promise.allSettled(
|
|
826
|
-
(store) => __async(this, null, function* () {
|
|
836
|
+
return Promise.allSettled(
|
|
837
|
+
this.getStoresFiltered(stores).map((store) => __async(this, null, function* () {
|
|
827
838
|
return {
|
|
828
839
|
id: store.id,
|
|
829
840
|
data: yield store.loadData()
|
|
830
841
|
};
|
|
831
|
-
})
|
|
832
|
-
)
|
|
842
|
+
}))
|
|
843
|
+
);
|
|
833
844
|
});
|
|
834
845
|
}
|
|
835
|
-
/**
|
|
836
|
-
|
|
846
|
+
/**
|
|
847
|
+
* Resets the persistent and in-memory data of the DataStore instances to their default values.
|
|
848
|
+
* @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be affected
|
|
849
|
+
*/
|
|
850
|
+
resetStoresData(stores) {
|
|
837
851
|
return __async(this, null, function* () {
|
|
838
|
-
return Promise.allSettled(
|
|
852
|
+
return Promise.allSettled(
|
|
853
|
+
this.getStoresFiltered(stores).map((store) => store.saveDefaultData())
|
|
854
|
+
);
|
|
839
855
|
});
|
|
840
856
|
}
|
|
841
857
|
/**
|
|
842
858
|
* Deletes the persistent data of the DataStore instances.
|
|
843
|
-
* Leaves the in-memory data untouched.
|
|
859
|
+
* Leaves the in-memory data untouched.
|
|
860
|
+
* @param stores An array of store IDs or a function that takes the store IDs and returns a boolean - if omitted, all stores will be affected
|
|
844
861
|
*/
|
|
845
|
-
deleteStoresData() {
|
|
862
|
+
deleteStoresData(stores) {
|
|
846
863
|
return __async(this, null, function* () {
|
|
847
|
-
return Promise.allSettled(
|
|
864
|
+
return Promise.allSettled(
|
|
865
|
+
this.getStoresFiltered(stores).map((store) => store.deleteData())
|
|
866
|
+
);
|
|
848
867
|
});
|
|
849
868
|
}
|
|
869
|
+
/** Checks if a given value is an array of SerializedDataStore objects */
|
|
870
|
+
static isSerializedDataStoreObjArray(obj) {
|
|
871
|
+
return Array.isArray(obj) && obj.every((o) => typeof o === "object" && o !== null && "id" in o && "data" in o && "formatVersion" in o && "encoded" in o);
|
|
872
|
+
}
|
|
850
873
|
/** Checks if a given value is a SerializedDataStore object */
|
|
851
|
-
static
|
|
874
|
+
static isSerializedDataStoreObj(obj) {
|
|
852
875
|
return typeof obj === "object" && obj !== null && "id" in obj && "data" in obj && "formatVersion" in obj && "encoded" in obj;
|
|
853
876
|
}
|
|
877
|
+
/** Returns the DataStore instances whose IDs match the provided array or function */
|
|
878
|
+
getStoresFiltered(stores) {
|
|
879
|
+
return this.stores.filter((s) => typeof stores === "undefined" ? true : Array.isArray(stores) ? stores.includes(s.id) : stores(s.id));
|
|
880
|
+
}
|
|
854
881
|
};
|
|
855
882
|
var NanoEmitter = class {
|
|
883
|
+
/** Creates a new instance of NanoEmitter - a lightweight event emitter with helper methods and a strongly typed event map */
|
|
856
884
|
constructor(options = {}) {
|
|
857
885
|
__publicField(this, "events", nanoevents.createNanoEvents());
|
|
858
886
|
__publicField(this, "eventUnsubscribes", []);
|
|
@@ -861,7 +889,27 @@ var NanoEmitter = class {
|
|
|
861
889
|
publicEmit: false
|
|
862
890
|
}, options);
|
|
863
891
|
}
|
|
864
|
-
/**
|
|
892
|
+
/**
|
|
893
|
+
* Subscribes to an event and calls the callback when it's emitted.
|
|
894
|
+
* @param event The event to subscribe to. Use `as "_"` in case your event names aren't thoroughly typed (like when using a template literal, e.g. \`event-${val}\` as "_")
|
|
895
|
+
* @returns Returns a function that can be called to unsubscribe the event listener
|
|
896
|
+
* @example ```ts
|
|
897
|
+
* const emitter = new NanoEmitter<{
|
|
898
|
+
* foo: (bar: string) => void;
|
|
899
|
+
* }>({
|
|
900
|
+
* publicEmit: true,
|
|
901
|
+
* });
|
|
902
|
+
*
|
|
903
|
+
* let i = 0;
|
|
904
|
+
* const unsub = emitter.on("foo", (bar) => {
|
|
905
|
+
* // unsubscribe after 10 events:
|
|
906
|
+
* if(++i === 10) unsub();
|
|
907
|
+
* console.log(bar);
|
|
908
|
+
* });
|
|
909
|
+
*
|
|
910
|
+
* emitter.emit("foo", "bar");
|
|
911
|
+
* ```
|
|
912
|
+
*/
|
|
865
913
|
on(event, cb) {
|
|
866
914
|
let unsub;
|
|
867
915
|
const unsubProxy = () => {
|
|
@@ -874,19 +922,43 @@ var NanoEmitter = class {
|
|
|
874
922
|
this.eventUnsubscribes.push(unsub);
|
|
875
923
|
return unsubProxy;
|
|
876
924
|
}
|
|
877
|
-
/**
|
|
925
|
+
/**
|
|
926
|
+
* Subscribes to an event and calls the callback or resolves the Promise only once when it's emitted.
|
|
927
|
+
* @param event The event to subscribe to. Use `as "_"` in case your event names aren't thoroughly typed (like when using a template literal, e.g. \`event-${val}\` as "_")
|
|
928
|
+
* @param cb The callback to call when the event is emitted - if provided or not, the returned Promise will resolve with the event arguments
|
|
929
|
+
* @returns Returns a Promise that resolves with the event arguments when the event is emitted
|
|
930
|
+
* @example ```ts
|
|
931
|
+
* const emitter = new NanoEmitter<{
|
|
932
|
+
* foo: (bar: string) => void;
|
|
933
|
+
* }>();
|
|
934
|
+
*
|
|
935
|
+
* // Promise syntax:
|
|
936
|
+
* const [bar] = await emitter.once("foo");
|
|
937
|
+
* console.log(bar);
|
|
938
|
+
*
|
|
939
|
+
* // Callback syntax:
|
|
940
|
+
* emitter.once("foo", (bar) => console.log(bar));
|
|
941
|
+
* ```
|
|
942
|
+
*/
|
|
878
943
|
once(event, cb) {
|
|
879
944
|
return new Promise((resolve) => {
|
|
880
945
|
let unsub;
|
|
881
946
|
const onceProxy = (...args) => {
|
|
882
|
-
unsub();
|
|
883
947
|
cb == null ? undefined : cb(...args);
|
|
948
|
+
unsub == null ? undefined : unsub();
|
|
884
949
|
resolve(args);
|
|
885
950
|
};
|
|
886
|
-
unsub = this.on(event, onceProxy);
|
|
951
|
+
unsub = this.events.on(event, onceProxy);
|
|
952
|
+
this.eventUnsubscribes.push(unsub);
|
|
887
953
|
});
|
|
888
954
|
}
|
|
889
|
-
/**
|
|
955
|
+
/**
|
|
956
|
+
* Emits an event on this instance.
|
|
957
|
+
* ⚠️ Needs `publicEmit` to be set to true in the NanoEmitter constructor or super() call!
|
|
958
|
+
* @param event The event to emit
|
|
959
|
+
* @param args The arguments to pass to the event listeners
|
|
960
|
+
* @returns Returns true if `publicEmit` is true and the event was emitted successfully
|
|
961
|
+
*/
|
|
890
962
|
emit(event, ...args) {
|
|
891
963
|
if (this.emitterOptions.publicEmit) {
|
|
892
964
|
this.events.emit(event, ...args);
|
|
@@ -894,7 +966,7 @@ var NanoEmitter = class {
|
|
|
894
966
|
}
|
|
895
967
|
return false;
|
|
896
968
|
}
|
|
897
|
-
/** Unsubscribes all event listeners */
|
|
969
|
+
/** Unsubscribes all event listeners from this instance */
|
|
898
970
|
unsubscribeAll() {
|
|
899
971
|
for (const unsub of this.eventUnsubscribes)
|
|
900
972
|
unsub();
|
|
@@ -934,6 +1006,10 @@ var Debouncer = class extends NanoEmitter {
|
|
|
934
1006
|
removeAllListeners() {
|
|
935
1007
|
this.listeners = [];
|
|
936
1008
|
}
|
|
1009
|
+
/** Returns all registered listeners */
|
|
1010
|
+
getListeners() {
|
|
1011
|
+
return this.listeners;
|
|
1012
|
+
}
|
|
937
1013
|
//#region timeout
|
|
938
1014
|
/** Sets the timeout for the debouncer */
|
|
939
1015
|
setTimeout(timeout) {
|
|
@@ -962,7 +1038,7 @@ var Debouncer = class extends NanoEmitter {
|
|
|
962
1038
|
const cl = (...a) => {
|
|
963
1039
|
this.queuedCall = undefined;
|
|
964
1040
|
this.emit("call", ...a);
|
|
965
|
-
this.listeners.forEach((l) => l.
|
|
1041
|
+
this.listeners.forEach((l) => l.call(this, ...a));
|
|
966
1042
|
};
|
|
967
1043
|
const setRepeatTimeout = () => {
|
|
968
1044
|
this.activeTimeout = setTimeout(() => {
|
|
@@ -1436,8 +1512,6 @@ function autoPlural(term, num, pluralType = "auto") {
|
|
|
1436
1512
|
return `${term}${n === 1 ? "" : "s"}`;
|
|
1437
1513
|
case "-ies":
|
|
1438
1514
|
return `${String(term).slice(0, -1)}${n === 1 ? "y" : "ies"}`;
|
|
1439
|
-
default:
|
|
1440
|
-
return String(term);
|
|
1441
1515
|
}
|
|
1442
1516
|
}
|
|
1443
1517
|
function insertValues(input, ...values) {
|
|
@@ -1496,6 +1570,112 @@ function purifyObj(obj) {
|
|
|
1496
1570
|
return Object.assign(/* @__PURE__ */ Object.create(null), obj);
|
|
1497
1571
|
}
|
|
1498
1572
|
|
|
1573
|
+
// lib/Mixins.ts
|
|
1574
|
+
var Mixins = class {
|
|
1575
|
+
/**
|
|
1576
|
+
* Creates a new Mixins instance.
|
|
1577
|
+
* @param config Configuration object to customize the behavior.
|
|
1578
|
+
*/
|
|
1579
|
+
constructor(config = {}) {
|
|
1580
|
+
/** List of all registered mixins */
|
|
1581
|
+
__publicField(this, "mixins", []);
|
|
1582
|
+
/** Default configuration object for mixins */
|
|
1583
|
+
__publicField(this, "defaultMixinCfg");
|
|
1584
|
+
/** Whether the priorities should auto-increment if not specified */
|
|
1585
|
+
__publicField(this, "autoIncPrioEnabled");
|
|
1586
|
+
/** The current auto-increment priority counter */
|
|
1587
|
+
__publicField(this, "autoIncPrioCounter", /* @__PURE__ */ new Map());
|
|
1588
|
+
var _a, _b, _c;
|
|
1589
|
+
this.defaultMixinCfg = purifyObj({
|
|
1590
|
+
priority: (_a = config.defaultPriority) != null ? _a : 0,
|
|
1591
|
+
stopPropagation: (_b = config.defaultStopPropagation) != null ? _b : false,
|
|
1592
|
+
signal: config.defaultSignal
|
|
1593
|
+
});
|
|
1594
|
+
this.autoIncPrioEnabled = (_c = config.autoIncrementPriority) != null ? _c : false;
|
|
1595
|
+
}
|
|
1596
|
+
//#region public
|
|
1597
|
+
/**
|
|
1598
|
+
* Adds a mixin function to the given {@linkcode mixinKey}.
|
|
1599
|
+
* If no priority is specified, it will be calculated via the protected method {@linkcode calcPriority()} based on the constructor configuration, or fall back to the default priority.
|
|
1600
|
+
* @param mixinKey The key to identify the mixin function.
|
|
1601
|
+
* @param mixinFn The function to be called to apply the mixin. The first argument is the input value, the second argument is the context object (if any).
|
|
1602
|
+
* @param config Configuration object to customize the mixin behavior, or just the priority if a number is passed.
|
|
1603
|
+
* @returns Returns a cleanup function, to be called when this mixin is no longer needed.
|
|
1604
|
+
*/
|
|
1605
|
+
add(mixinKey, mixinFn, config = purifyObj({})) {
|
|
1606
|
+
const calcPrio = typeof config === "number" ? config : this.calcPriority(mixinKey, config);
|
|
1607
|
+
const mixin = purifyObj(__spreadValues(__spreadValues(__spreadProps(__spreadValues({}, this.defaultMixinCfg), {
|
|
1608
|
+
key: mixinKey,
|
|
1609
|
+
fn: mixinFn
|
|
1610
|
+
}), typeof config === "object" ? config : {}), typeof calcPrio === "number" && !isNaN(calcPrio) ? { priority: calcPrio } : {}));
|
|
1611
|
+
this.mixins.push(mixin);
|
|
1612
|
+
const rem = () => {
|
|
1613
|
+
this.mixins = this.mixins.filter((m) => m !== mixin);
|
|
1614
|
+
};
|
|
1615
|
+
if (mixin.signal)
|
|
1616
|
+
mixin.signal.addEventListener("abort", rem, { once: true });
|
|
1617
|
+
return rem;
|
|
1618
|
+
}
|
|
1619
|
+
/** Returns a list of all added mixins with their keys and configuration objects, but not their functions */
|
|
1620
|
+
list() {
|
|
1621
|
+
return this.mixins.map((_a) => {
|
|
1622
|
+
var _b = _a, rest = __objRest(_b, ["fn"]);
|
|
1623
|
+
return rest;
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
/**
|
|
1627
|
+
* Applies all mixins with the given key to the input value, respecting the priority and stopPropagation settings.
|
|
1628
|
+
* If additional context is set in the MixinMap, it will need to be passed as the third argument.
|
|
1629
|
+
* @returns The modified value after all mixins have been applied.
|
|
1630
|
+
*/
|
|
1631
|
+
resolve(mixinKey, inputValue, ...inputCtx) {
|
|
1632
|
+
const mixins = this.mixins.filter((m) => m.key === mixinKey);
|
|
1633
|
+
const sortedMixins = [...mixins].sort((a, b) => b.priority - a.priority);
|
|
1634
|
+
let result = inputValue;
|
|
1635
|
+
for (let i = 0; i < sortedMixins.length; i++) {
|
|
1636
|
+
const mixin = sortedMixins[i];
|
|
1637
|
+
result = mixin.fn(result, ...inputCtx);
|
|
1638
|
+
if (result instanceof Promise) {
|
|
1639
|
+
return (() => __async(this, null, function* () {
|
|
1640
|
+
result = yield result;
|
|
1641
|
+
if (mixin.stopPropagation)
|
|
1642
|
+
return result;
|
|
1643
|
+
for (let j = i + 1; j < sortedMixins.length; j++) {
|
|
1644
|
+
const mixin2 = sortedMixins[j];
|
|
1645
|
+
result = yield mixin2.fn(result, ...inputCtx);
|
|
1646
|
+
if (mixin2.stopPropagation)
|
|
1647
|
+
break;
|
|
1648
|
+
}
|
|
1649
|
+
return result;
|
|
1650
|
+
}))();
|
|
1651
|
+
} else if (mixin.stopPropagation)
|
|
1652
|
+
break;
|
|
1653
|
+
}
|
|
1654
|
+
return result;
|
|
1655
|
+
}
|
|
1656
|
+
//#region protected
|
|
1657
|
+
/** Calculates the priority for a mixin based on the given configuration and the current auto-increment state of the instance */
|
|
1658
|
+
calcPriority(mixinKey, config) {
|
|
1659
|
+
var _a;
|
|
1660
|
+
if (config.priority !== undefined)
|
|
1661
|
+
return undefined;
|
|
1662
|
+
if (!this.autoIncPrioEnabled)
|
|
1663
|
+
return (_a = config.priority) != null ? _a : this.defaultMixinCfg.priority;
|
|
1664
|
+
if (!this.autoIncPrioCounter.has(mixinKey))
|
|
1665
|
+
this.autoIncPrioCounter.set(mixinKey, this.defaultMixinCfg.priority);
|
|
1666
|
+
let prio = this.autoIncPrioCounter.get(mixinKey);
|
|
1667
|
+
while (this.mixins.some((m) => m.key === mixinKey && m.priority === prio))
|
|
1668
|
+
prio++;
|
|
1669
|
+
this.autoIncPrioCounter.set(mixinKey, prio + 1);
|
|
1670
|
+
return prio;
|
|
1671
|
+
}
|
|
1672
|
+
/** Removes all mixins with the given key */
|
|
1673
|
+
removeAll(mixinKey) {
|
|
1674
|
+
this.mixins.filter((m) => m.key === mixinKey);
|
|
1675
|
+
this.mixins = this.mixins.filter((m) => m.key !== mixinKey);
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
|
|
1499
1679
|
// lib/SelectorObserver.ts
|
|
1500
1680
|
var SelectorObserver = class {
|
|
1501
1681
|
constructor(baseElement, options = {}) {
|
|
@@ -1753,15 +1933,15 @@ function getFallbackLanguage() {
|
|
|
1753
1933
|
return fallbackLang;
|
|
1754
1934
|
}
|
|
1755
1935
|
function addTransform(transform) {
|
|
1756
|
-
const [
|
|
1936
|
+
const [regex, fn] = transform;
|
|
1757
1937
|
valTransforms.push({
|
|
1758
1938
|
fn,
|
|
1759
|
-
regex
|
|
1939
|
+
regex
|
|
1760
1940
|
});
|
|
1761
1941
|
}
|
|
1762
1942
|
function deleteTransform(patternOrFn) {
|
|
1763
1943
|
const idx = valTransforms.findIndex(
|
|
1764
|
-
(t) => typeof patternOrFn === "function" ? t.fn === patternOrFn :
|
|
1944
|
+
(t) => typeof patternOrFn === "function" ? t.fn === patternOrFn : t.regex === patternOrFn
|
|
1765
1945
|
);
|
|
1766
1946
|
if (idx !== -1) {
|
|
1767
1947
|
valTransforms.splice(idx, 1);
|
|
@@ -1838,6 +2018,7 @@ exports.DataStoreSerializer = DataStoreSerializer;
|
|
|
1838
2018
|
exports.Debouncer = Debouncer;
|
|
1839
2019
|
exports.Dialog = Dialog;
|
|
1840
2020
|
exports.MigrationError = MigrationError;
|
|
2021
|
+
exports.Mixins = Mixins;
|
|
1841
2022
|
exports.NanoEmitter = NanoEmitter;
|
|
1842
2023
|
exports.PlatformError = PlatformError;
|
|
1843
2024
|
exports.SelectorObserver = SelectorObserver;
|