@sv443-network/userutils 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/README.md +381 -156
- package/dist/index.global.js +186 -96
- package/dist/index.js +185 -95
- package/dist/index.mjs +184 -92
- package/dist/lib/{config.d.ts → ConfigManager.d.ts} +8 -5
- package/dist/lib/SelectorObserver.d.ts +84 -0
- package/dist/lib/array.d.ts +2 -0
- package/dist/lib/dom.d.ts +34 -32
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/math.d.ts +12 -5
- package/dist/lib/misc.d.ts +3 -3
- package/dist/lib/translation.d.ts +2 -2
- package/package.json +10 -4
- package/dist/lib/onSelector.d.ts +0 -40
package/dist/index.mjs
CHANGED
|
@@ -46,28 +46,36 @@ var __async = (__this, __arguments, generator) => {
|
|
|
46
46
|
function clamp(value, min, max) {
|
|
47
47
|
return Math.max(Math.min(value, max), min);
|
|
48
48
|
}
|
|
49
|
-
function mapRange(value,
|
|
50
|
-
if (Number(
|
|
51
|
-
return value * (
|
|
52
|
-
return (value -
|
|
49
|
+
function mapRange(value, range1min, range1max, range2min, range2max) {
|
|
50
|
+
if (Number(range1min) === 0 && Number(range2min) === 0)
|
|
51
|
+
return value * (range2max / range1max);
|
|
52
|
+
return (value - range1min) * ((range2max - range2min) / (range1max - range1min)) + range2min;
|
|
53
53
|
}
|
|
54
54
|
function randRange(...args) {
|
|
55
55
|
let min, max;
|
|
56
|
-
if (typeof args[0] === "number" && typeof args[1] === "number")
|
|
56
|
+
if (typeof args[0] === "number" && typeof args[1] === "number")
|
|
57
57
|
[min, max] = args;
|
|
58
|
-
|
|
58
|
+
else if (typeof args[0] === "number" && typeof args[1] !== "number") {
|
|
59
59
|
min = 0;
|
|
60
|
-
max = args
|
|
60
|
+
[max] = args;
|
|
61
61
|
} else
|
|
62
62
|
throw new TypeError(`Wrong parameter(s) provided - expected: "number" and "number|undefined", got: "${typeof args[0]}" and "${typeof args[1]}"`);
|
|
63
63
|
min = Number(min);
|
|
64
64
|
max = Number(max);
|
|
65
65
|
if (isNaN(min) || isNaN(max))
|
|
66
|
-
|
|
66
|
+
return NaN;
|
|
67
67
|
if (min > max)
|
|
68
68
|
throw new TypeError(`Parameter "min" can't be bigger than "max"`);
|
|
69
69
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
70
70
|
}
|
|
71
|
+
function randomId(length = 16, radix = 16) {
|
|
72
|
+
const arr = new Uint8Array(length);
|
|
73
|
+
crypto.getRandomValues(arr);
|
|
74
|
+
return Array.from(
|
|
75
|
+
arr,
|
|
76
|
+
(v) => mapRange(v, 0, 255, 0, radix).toString(radix).substring(0, 1)
|
|
77
|
+
).join("");
|
|
78
|
+
}
|
|
71
79
|
|
|
72
80
|
// lib/array.ts
|
|
73
81
|
function randomItem(array) {
|
|
@@ -97,14 +105,14 @@ function randomizeArray(array) {
|
|
|
97
105
|
return retArray;
|
|
98
106
|
}
|
|
99
107
|
|
|
100
|
-
// lib/
|
|
108
|
+
// lib/ConfigManager.ts
|
|
101
109
|
var ConfigManager = class {
|
|
102
110
|
/**
|
|
103
111
|
* Creates an instance of ConfigManager to manage a user configuration that is cached in memory and persistently saved across sessions.
|
|
104
112
|
* Supports migrating data from older versions of the configuration to newer ones and populating the cache with default data if no persistent data is found.
|
|
105
113
|
*
|
|
106
114
|
* ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue`
|
|
107
|
-
* ⚠️ Make sure to call
|
|
115
|
+
* ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultConfig`
|
|
108
116
|
*
|
|
109
117
|
* @template TData The type of the data that is saved in persistent storage (will be automatically inferred from `config.defaultConfig`) - this should also be the type of the data format associated with the current `options.formatVersion`
|
|
110
118
|
* @param options The options for this ConfigManager instance
|
|
@@ -147,7 +155,10 @@ var ConfigManager = class {
|
|
|
147
155
|
}
|
|
148
156
|
});
|
|
149
157
|
}
|
|
150
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* Returns a copy of the data from the in-memory cache.
|
|
160
|
+
* Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).
|
|
161
|
+
*/
|
|
151
162
|
getData() {
|
|
152
163
|
return this.deepCopy(this.cachedConfig);
|
|
153
164
|
}
|
|
@@ -177,8 +188,8 @@ var ConfigManager = class {
|
|
|
177
188
|
}
|
|
178
189
|
/**
|
|
179
190
|
* Call this method to clear all persistently stored data associated with this ConfigManager instance.
|
|
180
|
-
* The in-memory cache will be left untouched, so you may still access the data with
|
|
181
|
-
* Calling
|
|
191
|
+
* The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
|
|
192
|
+
* Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
|
|
182
193
|
*
|
|
183
194
|
* ⚠️ This requires the additional directive `@grant GM.deleteValue`
|
|
184
195
|
*/
|
|
@@ -271,7 +282,7 @@ function openInNewTab(href) {
|
|
|
271
282
|
openElem.click();
|
|
272
283
|
setTimeout(openElem.remove, 50);
|
|
273
284
|
}
|
|
274
|
-
function interceptEvent(eventObject, eventName, predicate) {
|
|
285
|
+
function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
275
286
|
if (typeof Error.stackTraceLimit === "number" && Error.stackTraceLimit < 1e3) {
|
|
276
287
|
Error.stackTraceLimit = 1e3;
|
|
277
288
|
}
|
|
@@ -289,54 +300,44 @@ function interceptEvent(eventObject, eventName, predicate) {
|
|
|
289
300
|
};
|
|
290
301
|
})(eventObject.__proto__.addEventListener);
|
|
291
302
|
}
|
|
292
|
-
function interceptWindowEvent(eventName, predicate) {
|
|
303
|
+
function interceptWindowEvent(eventName, predicate = () => true) {
|
|
293
304
|
return interceptEvent(getUnsafeWindow(), eventName, predicate);
|
|
294
305
|
}
|
|
295
|
-
function amplifyMedia(mediaElement,
|
|
306
|
+
function amplifyMedia(mediaElement, initialGain = 1) {
|
|
296
307
|
const context = new (window.AudioContext || window.webkitAudioContext)();
|
|
297
308
|
const props = {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
309
|
+
context,
|
|
310
|
+
sourceNode: context.createMediaElementSource(mediaElement),
|
|
311
|
+
gainNode: context.createGain(),
|
|
312
|
+
/** Sets the gain of the amplifying GainNode */
|
|
313
|
+
setGain(gain) {
|
|
314
|
+
props.gainNode.gain.value = gain;
|
|
301
315
|
},
|
|
302
|
-
/** Returns the current gain
|
|
316
|
+
/** Returns the current gain of the amplifying GainNode */
|
|
303
317
|
getGain() {
|
|
304
318
|
return props.gainNode.gain.value;
|
|
305
319
|
},
|
|
320
|
+
/** Whether the amplification is currently enabled */
|
|
321
|
+
enabled: false,
|
|
306
322
|
/** Enable the amplification for the first time or if it was disabled before */
|
|
307
323
|
enable() {
|
|
308
|
-
|
|
309
|
-
|
|
324
|
+
if (props.enabled)
|
|
325
|
+
return;
|
|
326
|
+
props.enabled = true;
|
|
327
|
+
props.sourceNode.connect(props.gainNode);
|
|
310
328
|
props.gainNode.connect(props.context.destination);
|
|
311
329
|
},
|
|
312
330
|
/** Disable the amplification */
|
|
313
331
|
disable() {
|
|
314
|
-
|
|
315
|
-
|
|
332
|
+
if (!props.enabled)
|
|
333
|
+
return;
|
|
334
|
+
props.enabled = false;
|
|
335
|
+
props.sourceNode.disconnect(props.gainNode);
|
|
316
336
|
props.gainNode.disconnect(props.context.destination);
|
|
317
|
-
props.
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Set the options of the [limiter / DynamicsCompressorNode](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode/DynamicsCompressorNode#options)
|
|
321
|
-
* The default is `{ threshold: -2, knee: 40, ratio: 12, attack: 0.003, release: 0.25 }`
|
|
322
|
-
*/
|
|
323
|
-
setLimiterOptions(options) {
|
|
324
|
-
for (const [key, val] of Object.entries(options))
|
|
325
|
-
props.limiterNode[key].setValueAtTime(val, props.context.currentTime);
|
|
326
|
-
},
|
|
327
|
-
context,
|
|
328
|
-
source: context.createMediaElementSource(mediaElement),
|
|
329
|
-
gainNode: context.createGain(),
|
|
330
|
-
limiterNode: context.createDynamicsCompressor()
|
|
337
|
+
props.sourceNode.connect(props.context.destination);
|
|
338
|
+
}
|
|
331
339
|
};
|
|
332
|
-
props.
|
|
333
|
-
threshold: -2,
|
|
334
|
-
knee: 40,
|
|
335
|
-
ratio: 12,
|
|
336
|
-
attack: 3e-3,
|
|
337
|
-
release: 0.25
|
|
338
|
-
});
|
|
339
|
-
props.setGain(initialMultiplier);
|
|
340
|
+
props.setGain(initialGain);
|
|
340
341
|
return props;
|
|
341
342
|
}
|
|
342
343
|
function isScrollable(element) {
|
|
@@ -385,55 +386,146 @@ function insertValues(str, ...values) {
|
|
|
385
386
|
});
|
|
386
387
|
}
|
|
387
388
|
|
|
388
|
-
// lib/
|
|
389
|
-
var
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
389
|
+
// lib/SelectorObserver.ts
|
|
390
|
+
var SelectorObserver = class {
|
|
391
|
+
constructor(baseElement, options = {}) {
|
|
392
|
+
__publicField(this, "enabled", false);
|
|
393
|
+
__publicField(this, "baseElement");
|
|
394
|
+
__publicField(this, "observer");
|
|
395
|
+
__publicField(this, "observerOptions");
|
|
396
|
+
__publicField(this, "listenerMap");
|
|
397
|
+
this.baseElement = baseElement;
|
|
398
|
+
this.listenerMap = /* @__PURE__ */ new Map();
|
|
399
|
+
this.observer = new MutationObserver(() => this.checkAllSelectors());
|
|
400
|
+
this.observerOptions = __spreadValues({
|
|
401
|
+
childList: true,
|
|
402
|
+
subtree: true
|
|
403
|
+
}, options);
|
|
404
|
+
}
|
|
405
|
+
checkAllSelectors() {
|
|
406
|
+
for (const [selector, listeners] of this.listenerMap.entries())
|
|
407
|
+
this.checkSelector(selector, listeners);
|
|
408
|
+
}
|
|
409
|
+
checkSelector(selector, listeners) {
|
|
410
|
+
var _a;
|
|
411
|
+
if (!this.enabled)
|
|
412
|
+
return;
|
|
413
|
+
const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
|
|
414
|
+
if (!baseElement)
|
|
415
|
+
return;
|
|
416
|
+
const all = listeners.some((listener) => listener.all);
|
|
417
|
+
const one = listeners.some((listener) => !listener.all);
|
|
418
|
+
const allElements = all ? baseElement.querySelectorAll(selector) : null;
|
|
419
|
+
const oneElement = one ? baseElement.querySelector(selector) : null;
|
|
420
|
+
for (const options of listeners) {
|
|
421
|
+
if (options.all) {
|
|
422
|
+
if (allElements && allElements.length > 0) {
|
|
423
|
+
options.listener(allElements);
|
|
424
|
+
if (!options.continuous)
|
|
425
|
+
this.removeListener(selector, options);
|
|
426
|
+
}
|
|
427
|
+
} else {
|
|
428
|
+
if (oneElement) {
|
|
429
|
+
options.listener(oneElement);
|
|
430
|
+
if (!options.continuous)
|
|
431
|
+
this.removeListener(selector, options);
|
|
432
|
+
}
|
|
410
433
|
}
|
|
411
|
-
|
|
412
|
-
|
|
434
|
+
if (((_a = this.listenerMap.get(selector)) == null ? void 0 : _a.length) === 0)
|
|
435
|
+
this.listenerMap.delete(selector);
|
|
413
436
|
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
437
|
+
}
|
|
438
|
+
debounce(func, time) {
|
|
439
|
+
let timeout;
|
|
440
|
+
return function(...args) {
|
|
441
|
+
clearTimeout(timeout);
|
|
442
|
+
timeout = setTimeout(() => func.apply(this, args), time);
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Starts observing the children of the base element for changes to the given {@linkcode selector} according to the set {@linkcode options}
|
|
447
|
+
* @param selector The selector to observe
|
|
448
|
+
* @param options Options for the selector observation
|
|
449
|
+
* @param options.listener Gets called whenever the selector was found in the DOM
|
|
450
|
+
* @param [options.all] Whether to use `querySelectorAll()` instead - default is false
|
|
451
|
+
* @param [options.continuous] Whether to call the listener continuously instead of just once - default is false
|
|
452
|
+
* @param [options.debounce] Whether to debounce the listener to reduce calls to `querySelector` or `querySelectorAll` - set undefined or <=0 to disable (default)
|
|
453
|
+
*/
|
|
454
|
+
addListener(selector, options) {
|
|
455
|
+
options = __spreadValues({ all: false, continuous: false, debounce: 0 }, options);
|
|
456
|
+
if (options.debounce && options.debounce > 0 || this.observerOptions.defaultDebounce && this.observerOptions.defaultDebounce > 0) {
|
|
457
|
+
options.listener = this.debounce(
|
|
458
|
+
options.listener,
|
|
459
|
+
options.debounce || this.observerOptions.defaultDebounce
|
|
460
|
+
);
|
|
421
461
|
}
|
|
462
|
+
if (this.listenerMap.has(selector))
|
|
463
|
+
this.listenerMap.get(selector).push(options);
|
|
464
|
+
else
|
|
465
|
+
this.listenerMap.set(selector, [options]);
|
|
466
|
+
this.checkSelector(selector, [options]);
|
|
422
467
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
468
|
+
/** Disables the observation of the child elements */
|
|
469
|
+
disable() {
|
|
470
|
+
if (!this.enabled)
|
|
471
|
+
return;
|
|
472
|
+
this.enabled = false;
|
|
473
|
+
this.observer.disconnect();
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Enables or reenables the observation of the child elements.
|
|
477
|
+
* @param immediatelyCheckSelectors Whether to immediately check if all previously registered selectors exist (default is true)
|
|
478
|
+
* @returns Returns true when the observation was enabled, false otherwise (e.g. when the base element wasn't found)
|
|
479
|
+
*/
|
|
480
|
+
enable(immediatelyCheckSelectors = true) {
|
|
481
|
+
const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
|
|
482
|
+
if (this.enabled || !baseElement)
|
|
483
|
+
return false;
|
|
484
|
+
this.enabled = true;
|
|
485
|
+
this.observer.observe(baseElement, this.observerOptions);
|
|
486
|
+
if (immediatelyCheckSelectors)
|
|
487
|
+
this.checkAllSelectors();
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
/** Returns whether the observation of the child elements is currently enabled */
|
|
491
|
+
isEnabled() {
|
|
492
|
+
return this.enabled;
|
|
493
|
+
}
|
|
494
|
+
/** Removes all listeners that have been registered with {@linkcode addListener()} */
|
|
495
|
+
clearListeners() {
|
|
496
|
+
this.listenerMap.clear();
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Removes all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()}
|
|
500
|
+
* @returns Returns true when all listeners for the associated selector were found and removed, false otherwise
|
|
501
|
+
*/
|
|
502
|
+
removeAllListeners(selector) {
|
|
503
|
+
return this.listenerMap.delete(selector);
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Removes a single listener for the given {@linkcode selector} and {@linkcode options} that has been registered with {@linkcode addListener()}
|
|
507
|
+
* @returns Returns true when the listener was found and removed, false otherwise
|
|
508
|
+
*/
|
|
509
|
+
removeListener(selector, options) {
|
|
510
|
+
const listeners = this.listenerMap.get(selector);
|
|
511
|
+
if (!listeners)
|
|
512
|
+
return false;
|
|
513
|
+
const index = listeners.indexOf(options);
|
|
514
|
+
if (index > -1) {
|
|
515
|
+
listeners.splice(index, 1);
|
|
516
|
+
return true;
|
|
517
|
+
}
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
/** Returns all listeners that have been registered with {@linkcode addListener()} */
|
|
521
|
+
getAllListeners() {
|
|
522
|
+
return this.listenerMap;
|
|
523
|
+
}
|
|
524
|
+
/** Returns all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()} */
|
|
525
|
+
getListeners(selector) {
|
|
526
|
+
return this.listenerMap.get(selector);
|
|
527
|
+
}
|
|
528
|
+
};
|
|
437
529
|
|
|
438
530
|
// lib/translation.ts
|
|
439
531
|
var trans = {};
|
|
@@ -460,4 +552,4 @@ tr.getLanguage = () => {
|
|
|
460
552
|
return curLang;
|
|
461
553
|
};
|
|
462
554
|
|
|
463
|
-
export { ConfigManager, addGlobalStyle, addParent, amplifyMedia, autoPlural, clamp, debounce, fetchAdvanced,
|
|
555
|
+
export { ConfigManager, SelectorObserver, addGlobalStyle, addParent, amplifyMedia, autoPlural, clamp, debounce, fetchAdvanced, getUnsafeWindow, insertAfter, insertValues, interceptEvent, interceptWindowEvent, isScrollable, mapRange, openInNewTab, pauseFor, preloadImages, randRange, randomId, randomItem, randomItemIndex, randomizeArray, takeRandomItem, tr };
|
|
@@ -34,7 +34,7 @@ export interface ConfigManagerOptions<TData> {
|
|
|
34
34
|
* Supports migrating data from older versions of the configuration to newer ones and populating the cache with default data if no persistent data is found.
|
|
35
35
|
*
|
|
36
36
|
* ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue`
|
|
37
|
-
* ⚠️ Make sure to call
|
|
37
|
+
* ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultConfig`
|
|
38
38
|
*
|
|
39
39
|
* @template TData The type of the data that is saved in persistent storage (will be automatically inferred from `config.defaultConfig`) - this should also be the type of the data format associated with the current `options.formatVersion`
|
|
40
40
|
*/
|
|
@@ -49,7 +49,7 @@ export declare class ConfigManager<TData = any> {
|
|
|
49
49
|
* Supports migrating data from older versions of the configuration to newer ones and populating the cache with default data if no persistent data is found.
|
|
50
50
|
*
|
|
51
51
|
* ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue`
|
|
52
|
-
* ⚠️ Make sure to call
|
|
52
|
+
* ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultConfig`
|
|
53
53
|
*
|
|
54
54
|
* @template TData The type of the data that is saved in persistent storage (will be automatically inferred from `config.defaultConfig`) - this should also be the type of the data format associated with the current `options.formatVersion`
|
|
55
55
|
* @param options The options for this ConfigManager instance
|
|
@@ -61,7 +61,10 @@ export declare class ConfigManager<TData = any> {
|
|
|
61
61
|
* Also runs all necessary migration functions if the data format has changed since the last time the data was saved.
|
|
62
62
|
*/
|
|
63
63
|
loadData(): Promise<TData>;
|
|
64
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* Returns a copy of the data from the in-memory cache.
|
|
66
|
+
* Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).
|
|
67
|
+
*/
|
|
65
68
|
getData(): TData;
|
|
66
69
|
/** Saves the data synchronously to the in-memory cache and asynchronously to the persistent storage */
|
|
67
70
|
setData(data: TData): Promise<void>;
|
|
@@ -69,8 +72,8 @@ export declare class ConfigManager<TData = any> {
|
|
|
69
72
|
saveDefaultData(): Promise<void>;
|
|
70
73
|
/**
|
|
71
74
|
* Call this method to clear all persistently stored data associated with this ConfigManager instance.
|
|
72
|
-
* The in-memory cache will be left untouched, so you may still access the data with
|
|
73
|
-
* Calling
|
|
75
|
+
* The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
|
|
76
|
+
* Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
|
|
74
77
|
*
|
|
75
78
|
* ⚠️ This requires the additional directive `@grant GM.deleteValue`
|
|
76
79
|
*/
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** Options for the `onSelector()` method of {@linkcode SelectorObserver} */
|
|
2
|
+
export type SelectorListenerOptions<TElem extends Element = HTMLElement> = SelectorOptionsOne<TElem> | SelectorOptionsAll<TElem>;
|
|
3
|
+
type SelectorOptionsOne<TElem extends Element> = SelectorOptionsCommon & {
|
|
4
|
+
/** Whether to use `querySelectorAll()` instead - default is false */
|
|
5
|
+
all?: false;
|
|
6
|
+
/** Gets called whenever the selector was found in the DOM */
|
|
7
|
+
listener: (element: TElem) => void;
|
|
8
|
+
};
|
|
9
|
+
type SelectorOptionsAll<TElem extends Element> = SelectorOptionsCommon & {
|
|
10
|
+
/** Whether to use `querySelectorAll()` instead - default is false */
|
|
11
|
+
all: true;
|
|
12
|
+
/** Gets called whenever the selector was found in the DOM */
|
|
13
|
+
listener: (elements: NodeListOf<TElem>) => void;
|
|
14
|
+
};
|
|
15
|
+
type SelectorOptionsCommon = {
|
|
16
|
+
/** Whether to call the listener continuously instead of once - default is false */
|
|
17
|
+
continuous?: boolean;
|
|
18
|
+
/** Whether to debounce the listener to reduce calls to `querySelector` or `querySelectorAll` - set undefined or <=0 to disable (default) */
|
|
19
|
+
debounce?: number;
|
|
20
|
+
};
|
|
21
|
+
export type SelectorObserverOptions = MutationObserverInit & {
|
|
22
|
+
/** If set, applies this debounce in milliseconds to all listeners that don't have their own debounce set */
|
|
23
|
+
defaultDebounce?: number;
|
|
24
|
+
};
|
|
25
|
+
/** Observes the children of the given element for changes */
|
|
26
|
+
export declare class SelectorObserver {
|
|
27
|
+
private enabled;
|
|
28
|
+
private baseElement;
|
|
29
|
+
private observer;
|
|
30
|
+
private observerOptions;
|
|
31
|
+
private listenerMap;
|
|
32
|
+
/**
|
|
33
|
+
* Creates a new SelectorObserver that will observe the children of the given base element selector for changes (only creation and deletion of elements by default)
|
|
34
|
+
* @param baseElementSelector The selector of the element to observe
|
|
35
|
+
* @param options Fine-tune what triggers the MutationObserver's checking function - `subtree` and `childList` are set to true by default
|
|
36
|
+
*/
|
|
37
|
+
constructor(baseElementSelector: string, options: SelectorObserverOptions);
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new SelectorObserver that will observe the children of the given base element for changes (only creation and deletion of elements by default)
|
|
40
|
+
* @param baseElement The element to observe
|
|
41
|
+
* @param options Fine-tune what triggers the MutationObserver's checking function - `subtree` and `childList` are set to true by default
|
|
42
|
+
*/
|
|
43
|
+
constructor(baseElement: Element, options: SelectorObserverOptions);
|
|
44
|
+
private checkAllSelectors;
|
|
45
|
+
private checkSelector;
|
|
46
|
+
private debounce;
|
|
47
|
+
/**
|
|
48
|
+
* Starts observing the children of the base element for changes to the given {@linkcode selector} according to the set {@linkcode options}
|
|
49
|
+
* @param selector The selector to observe
|
|
50
|
+
* @param options Options for the selector observation
|
|
51
|
+
* @param options.listener Gets called whenever the selector was found in the DOM
|
|
52
|
+
* @param [options.all] Whether to use `querySelectorAll()` instead - default is false
|
|
53
|
+
* @param [options.continuous] Whether to call the listener continuously instead of just once - default is false
|
|
54
|
+
* @param [options.debounce] Whether to debounce the listener to reduce calls to `querySelector` or `querySelectorAll` - set undefined or <=0 to disable (default)
|
|
55
|
+
*/
|
|
56
|
+
addListener<TElem extends Element = HTMLElement>(selector: string, options: SelectorListenerOptions<TElem>): void;
|
|
57
|
+
/** Disables the observation of the child elements */
|
|
58
|
+
disable(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Enables or reenables the observation of the child elements.
|
|
61
|
+
* @param immediatelyCheckSelectors Whether to immediately check if all previously registered selectors exist (default is true)
|
|
62
|
+
* @returns Returns true when the observation was enabled, false otherwise (e.g. when the base element wasn't found)
|
|
63
|
+
*/
|
|
64
|
+
enable(immediatelyCheckSelectors?: boolean): boolean;
|
|
65
|
+
/** Returns whether the observation of the child elements is currently enabled */
|
|
66
|
+
isEnabled(): boolean;
|
|
67
|
+
/** Removes all listeners that have been registered with {@linkcode addListener()} */
|
|
68
|
+
clearListeners(): void;
|
|
69
|
+
/**
|
|
70
|
+
* Removes all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()}
|
|
71
|
+
* @returns Returns true when all listeners for the associated selector were found and removed, false otherwise
|
|
72
|
+
*/
|
|
73
|
+
removeAllListeners(selector: string): boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Removes a single listener for the given {@linkcode selector} and {@linkcode options} that has been registered with {@linkcode addListener()}
|
|
76
|
+
* @returns Returns true when the listener was found and removed, false otherwise
|
|
77
|
+
*/
|
|
78
|
+
removeListener(selector: string, options: SelectorListenerOptions): boolean;
|
|
79
|
+
/** Returns all listeners that have been registered with {@linkcode addListener()} */
|
|
80
|
+
getAllListeners(): Map<string, SelectorListenerOptions<HTMLElement>[]>;
|
|
81
|
+
/** Returns all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()} */
|
|
82
|
+
getListeners(selector: string): SelectorListenerOptions<HTMLElement>[] | undefined;
|
|
83
|
+
}
|
|
84
|
+
export {};
|
package/dist/lib/array.d.ts
CHANGED
package/dist/lib/dom.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export declare function getUnsafeWindow(): Window;
|
|
5
5
|
/**
|
|
6
|
-
* Inserts
|
|
7
|
-
* @returns Returns the
|
|
6
|
+
* Inserts {@linkcode afterElement} as a sibling just after the provided {@linkcode beforeElement}
|
|
7
|
+
* @returns Returns the {@linkcode afterElement}
|
|
8
8
|
*/
|
|
9
9
|
export declare function insertAfter(beforeElement: Element, afterElement: Element): Element;
|
|
10
10
|
/**
|
|
@@ -32,64 +32,66 @@ export declare function preloadImages(srcUrls: string[], rejects?: boolean): Pro
|
|
|
32
32
|
*/
|
|
33
33
|
export declare function openInNewTab(href: string): void;
|
|
34
34
|
/**
|
|
35
|
-
* Intercepts the specified event on the passed object and prevents it from being called if the called
|
|
35
|
+
* Intercepts the specified event on the passed object and prevents it from being called if the called {@linkcode predicate} function returns a truthy value.
|
|
36
|
+
* If no predicate is specified, all events will be discarded.
|
|
36
37
|
* This function should be called as soon as possible (I recommend using `@run-at document-start`), as it will only intercept events that are added after this function is called.
|
|
37
38
|
* Calling this function will set the `Error.stackTraceLimit` to 1000 to ensure the stack trace is preserved.
|
|
38
39
|
*/
|
|
39
|
-
export declare function interceptEvent<TEvtObj extends EventTarget, TPredicateEvt extends Event>(eventObject: TEvtObj, eventName: Parameters<TEvtObj["addEventListener"]>[0], predicate
|
|
40
|
+
export declare function interceptEvent<TEvtObj extends EventTarget, TPredicateEvt extends Event>(eventObject: TEvtObj, eventName: Parameters<TEvtObj["addEventListener"]>[0], predicate?: (event: TPredicateEvt) => boolean): void;
|
|
40
41
|
/**
|
|
41
|
-
* Intercepts the specified event on the window object and prevents it from being called if the called
|
|
42
|
+
* Intercepts the specified event on the window object and prevents it from being called if the called {@linkcode predicate} function returns a truthy value.
|
|
43
|
+
* If no predicate is specified, all events will be discarded.
|
|
42
44
|
* This function should be called as soon as possible (I recommend using `@run-at document-start`), as it will only intercept events that are added after this function is called.
|
|
43
45
|
* Calling this function will set the `Error.stackTraceLimit` to 1000 to ensure the stack trace is preserved.
|
|
44
46
|
*/
|
|
45
|
-
export declare function interceptWindowEvent<TEvtKey extends keyof WindowEventMap>(eventName: TEvtKey, predicate
|
|
47
|
+
export declare function interceptWindowEvent<TEvtKey extends keyof WindowEventMap>(eventName: TEvtKey, predicate?: (event: WindowEventMap[TEvtKey]) => boolean): void;
|
|
48
|
+
/** An object which contains the results of {@linkcode amplifyMedia()} */
|
|
49
|
+
export type AmplifyMediaResult = ReturnType<typeof amplifyMedia>;
|
|
46
50
|
/**
|
|
47
|
-
* Amplifies the gain of the passed media element's audio by the specified
|
|
48
|
-
* Also applies a limiter to prevent clipping and distortion.
|
|
51
|
+
* Amplifies the gain of the passed media element's audio by the specified value.
|
|
49
52
|
* This function supports any MediaElement instance like `<audio>` or `<video>`
|
|
50
53
|
*
|
|
51
54
|
* This is the audio processing workflow:
|
|
52
|
-
* `MediaElement (source)` => `
|
|
55
|
+
* `MediaElement (source)` => `GainNode (amplification)` => `destination`
|
|
53
56
|
*
|
|
54
57
|
* ⚠️ This function has to be run in response to a user interaction event, else the browser will reject it because of the strict autoplay policy.
|
|
55
58
|
* ⚠️ Make sure to call the returned function `enable()` after calling this function to actually enable the amplification.
|
|
59
|
+
* ⚠️ You should implement a safety limit by using the [`clamp()`](https://github.com/Sv443-Network/UserUtils#clamp) function to prevent any accidental bleeding eardrums.
|
|
56
60
|
*
|
|
57
61
|
* @param mediaElement The media element to amplify (e.g. `<audio>` or `<video>`)
|
|
58
|
-
* @param
|
|
62
|
+
* @param initialGain The initial gain to apply to the GainNode responsible for volume amplification (floating point number, default is `1.0`)
|
|
59
63
|
* @returns Returns an object with the following properties:
|
|
64
|
+
* **Important properties:**
|
|
60
65
|
* | Property | Description |
|
|
61
66
|
* | :-- | :-- |
|
|
62
|
-
* | `
|
|
63
|
-
* | `getGain()` | Returns the current gain multiplier |
|
|
64
|
-
* | `enable()` | Call to enable the amplification for the first time or if it was disabled before |
|
|
67
|
+
* | `enable()` | Call to enable the amplification for the first time or re-enable it if it was disabled before |
|
|
65
68
|
* | `disable()` | Call to disable amplification |
|
|
66
|
-
* | `
|
|
69
|
+
* | `enabled` | Whether the amplification is currently enabled |
|
|
70
|
+
* | `setGain()` | Used to change the gain value from the default given by the parameter {@linkcode initialGain} |
|
|
71
|
+
* | `getGain()` | Returns the current gain value |
|
|
72
|
+
*
|
|
73
|
+
* **Other properties:**
|
|
74
|
+
* | Property | Description |
|
|
75
|
+
* | :-- | :-- |
|
|
67
76
|
* | `context` | The AudioContext instance |
|
|
68
|
-
* | `
|
|
69
|
-
* | `gainNode` | The GainNode instance |
|
|
70
|
-
* | `limiterNode` | The DynamicsCompressorNode instance used for limiting clipping and distortion |
|
|
77
|
+
* | `sourceNode` | A MediaElementSourceNode instance created from the passed {@linkcode mediaElement} |
|
|
78
|
+
* | `gainNode` | The GainNode instance used for volume amplification |
|
|
71
79
|
*/
|
|
72
|
-
export declare function amplifyMedia<TElem extends HTMLMediaElement>(mediaElement: TElem,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
export declare function amplifyMedia<TElem extends HTMLMediaElement>(mediaElement: TElem, initialGain?: number): {
|
|
81
|
+
context: AudioContext;
|
|
82
|
+
sourceNode: MediaElementAudioSourceNode;
|
|
83
|
+
gainNode: GainNode;
|
|
84
|
+
/** Sets the gain of the amplifying GainNode */
|
|
85
|
+
setGain(gain: number): void;
|
|
86
|
+
/** Returns the current gain of the amplifying GainNode */
|
|
76
87
|
getGain(): number;
|
|
88
|
+
/** Whether the amplification is currently enabled */
|
|
89
|
+
enabled: boolean;
|
|
77
90
|
/** Enable the amplification for the first time or if it was disabled before */
|
|
78
91
|
enable(): void;
|
|
79
92
|
/** Disable the amplification */
|
|
80
93
|
disable(): void;
|
|
81
|
-
/**
|
|
82
|
-
* Set the options of the [limiter / DynamicsCompressorNode](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode/DynamicsCompressorNode#options)
|
|
83
|
-
* The default is `{ threshold: -2, knee: 40, ratio: 12, attack: 0.003, release: 0.25 }`
|
|
84
|
-
*/
|
|
85
|
-
setLimiterOptions(options: Partial<Record<"threshold" | "knee" | "ratio" | "attack" | "release", number>>): void;
|
|
86
|
-
context: AudioContext;
|
|
87
|
-
source: MediaElementAudioSourceNode;
|
|
88
|
-
gainNode: GainNode;
|
|
89
|
-
limiterNode: DynamicsCompressorNode;
|
|
90
94
|
};
|
|
91
|
-
/** An object which contains the results of `amplifyMedia()` */
|
|
92
|
-
export type AmplifyMediaResult = ReturnType<typeof amplifyMedia>;
|
|
93
95
|
/** Checks if an element is scrollable in the horizontal and vertical directions */
|
|
94
96
|
export declare function isScrollable(element: Element): {
|
|
95
97
|
vertical: boolean;
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from "./array";
|
|
2
|
-
export * from "./
|
|
2
|
+
export * from "./ConfigManager";
|
|
3
3
|
export * from "./dom";
|
|
4
4
|
export * from "./math";
|
|
5
5
|
export * from "./misc";
|
|
6
|
-
export * from "./
|
|
6
|
+
export * from "./SelectorObserver";
|
|
7
7
|
export * from "./translation";
|
package/dist/lib/math.d.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
/** Ensures the passed
|
|
1
|
+
/** Ensures the passed {@linkcode value} always stays between {@linkcode min} and {@linkcode max} */
|
|
2
2
|
export declare function clamp(value: number, min: number, max: number): number;
|
|
3
3
|
/**
|
|
4
|
-
* Transforms the value parameter from the numerical range `
|
|
4
|
+
* Transforms the value parameter from the numerical range `range1min─range1max` to the numerical range `range2min─range2max`
|
|
5
5
|
* For example, you can map the value 2 in the range of 0-5 to the range of 0-10 and you'd get a 4 as a result.
|
|
6
6
|
*/
|
|
7
|
-
export declare function mapRange(value: number,
|
|
8
|
-
/** Returns a random number between
|
|
7
|
+
export declare function mapRange(value: number, range1min: number, range1max: number, range2min: number, range2max: number): number;
|
|
8
|
+
/** Returns a random number between {@linkcode min} and {@linkcode max} (inclusive) */
|
|
9
9
|
export declare function randRange(min: number, max: number): number;
|
|
10
|
-
/** Returns a random number between 0 and
|
|
10
|
+
/** Returns a random number between 0 and {@linkcode max} (inclusive) */
|
|
11
11
|
export declare function randRange(max: number): number;
|
|
12
|
+
/**
|
|
13
|
+
* Generates a random ID with the specified length and radix (16 characters and hexadecimal by default)
|
|
14
|
+
* Uses [`crypto.getRandomValues()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues) for better cryptographic randomness
|
|
15
|
+
* @param length The length of the ID to generate (defaults to 16)
|
|
16
|
+
* @param radix The [radix](https://en.wikipedia.org/wiki/Radix) of each digit (defaults to 16 which is hexadecimal. Use 2 for binary, 10 for decimal, 36 for alphanumeric, etc.)
|
|
17
|
+
*/
|
|
18
|
+
export declare function randomId(length?: number, radix?: number): string;
|