@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/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, range_1_min, range_1_max, range_2_min, range_2_max) {
50
- if (Number(range_1_min) === 0 && Number(range_2_min) === 0)
51
- return value * (range_2_max / range_1_max);
52
- return (value - range_1_min) * ((range_2_max - range_2_min) / (range_1_max - range_1_min)) + range_2_min;
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
- } else if (typeof args[0] === "number" && typeof args[1] !== "number") {
58
+ else if (typeof args[0] === "number" && typeof args[1] !== "number") {
59
59
  min = 0;
60
- max = args[0];
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
- throw new TypeError(`Parameters "min" and "max" can't be NaN`);
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/config.ts
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 `loadData()` at least once after creating an instance, or the returned data will be the same as `options.defaultConfig`
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
- /** Returns a copy of the data from the in-memory cache. Use `loadData()` to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage). */
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 `getData()`.
181
- * Calling `loadData()` or `setData()` after this method was called will recreate persistent storage with the cached or default data.
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, initialMultiplier = 1) {
306
+ function amplifyMedia(mediaElement, initialGain = 1) {
296
307
  const context = new (window.AudioContext || window.webkitAudioContext)();
297
308
  const props = {
298
- /** Sets the gain multiplier */
299
- setGain(multiplier) {
300
- props.gainNode.gain.setValueAtTime(multiplier, props.context.currentTime);
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 multiplier */
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
- props.source.connect(props.limiterNode);
309
- props.limiterNode.connect(props.gainNode);
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
- props.source.disconnect(props.limiterNode);
315
- props.limiterNode.disconnect(props.gainNode);
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.source.connect(props.context.destination);
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.setLimiterOptions({
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/onSelector.ts
389
- var selectorMap = /* @__PURE__ */ new Map();
390
- function onSelector(selector, options) {
391
- let selectorMapItems = [];
392
- if (selectorMap.has(selector))
393
- selectorMapItems = selectorMap.get(selector);
394
- selectorMapItems.push(options);
395
- selectorMap.set(selector, selectorMapItems);
396
- checkSelectorExists(selector, selectorMapItems);
397
- }
398
- function removeOnSelector(selector) {
399
- return selectorMap.delete(selector);
400
- }
401
- function checkSelectorExists(selector, options) {
402
- const deleteIndices = [];
403
- options.forEach((option, i) => {
404
- try {
405
- const elements = option.all ? document.querySelectorAll(selector) : document.querySelector(selector);
406
- if (elements !== null && elements instanceof NodeList && elements.length > 0 || elements !== null) {
407
- option.listener(elements);
408
- if (!option.continuous)
409
- deleteIndices.push(i);
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
- } catch (err) {
412
- console.error(`Couldn't call listener for selector '${selector}'`, err);
434
+ if (((_a = this.listenerMap.get(selector)) == null ? void 0 : _a.length) === 0)
435
+ this.listenerMap.delete(selector);
413
436
  }
414
- });
415
- if (deleteIndices.length > 0) {
416
- const newOptsArray = options.filter((_, i) => !deleteIndices.includes(i));
417
- if (newOptsArray.length === 0)
418
- selectorMap.delete(selector);
419
- else {
420
- selectorMap.set(selector, newOptsArray);
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
- function initOnSelector(options = {}) {
425
- const observer = new MutationObserver(() => {
426
- for (const [selector, options2] of selectorMap.entries())
427
- checkSelectorExists(selector, options2);
428
- });
429
- observer.observe(document.body, __spreadValues({
430
- subtree: true,
431
- childList: true
432
- }, options));
433
- }
434
- function getSelectorMap() {
435
- return selectorMap;
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, getSelectorMap, getUnsafeWindow, initOnSelector, insertAfter, insertValues, interceptEvent, interceptWindowEvent, isScrollable, mapRange, onSelector, openInNewTab, pauseFor, preloadImages, randRange, randomItem, randomItemIndex, randomizeArray, removeOnSelector, takeRandomItem, tr };
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 `loadData()` at least once after creating an instance, or the returned data will be the same as `options.defaultConfig`
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 `loadData()` at least once after creating an instance, or the returned data will be the same as `options.defaultConfig`
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
- /** Returns a copy of the data from the in-memory cache. Use `loadData()` to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage). */
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 `getData()`.
73
- * Calling `loadData()` or `setData()` after this method was called will recreate persistent storage with the cached or default data.
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 {};
@@ -1,3 +1,5 @@
1
+ /** Describes an array with at least one item */
2
+ export type NonEmptyArray<T = unknown> = [T, ...T[]];
1
3
  /** Returns a random item from the passed array */
2
4
  export declare function randomItem<T = unknown>(array: T[]): T | undefined;
3
5
  /**
package/dist/lib/dom.d.ts CHANGED
@@ -3,8 +3,8 @@
3
3
  */
4
4
  export declare function getUnsafeWindow(): Window;
5
5
  /**
6
- * Inserts `afterElement` as a sibling just after the provided `beforeElement`
7
- * @returns Returns the `afterElement`
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 `predicate` function returns a truthy value.
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: (event: TPredicateEvt) => boolean): void;
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 `predicate` function returns a truthy value.
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: (event: WindowEventMap[TEvtKey]) => boolean): void;
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 multiplier.
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)` => `DynamicsCompressorNode (limiter)` => `GainNode` => `AudioDestinationNode (output)`
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 initialMultiplier The initial gain multiplier to apply (floating point number, default is `1.0`)
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
- * | `setGain()` | Used to change the gain multiplier |
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
- * | `setLimiterOptions()` | Used for changing the [options of the DynamicsCompressorNode](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode/DynamicsCompressorNode#options) - the default is `{ threshold: -2, knee: 40, ratio: 12, attack: 0.003, release: 0.25 }` |
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
- * | `source` | The MediaElementSourceNode instance |
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, initialMultiplier?: number): {
73
- /** Sets the gain multiplier */
74
- setGain(multiplier: number): void;
75
- /** Returns the current gain multiplier */
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;
@@ -1,7 +1,7 @@
1
1
  export * from "./array";
2
- export * from "./config";
2
+ export * from "./ConfigManager";
3
3
  export * from "./dom";
4
4
  export * from "./math";
5
5
  export * from "./misc";
6
- export * from "./onSelector";
6
+ export * from "./SelectorObserver";
7
7
  export * from "./translation";
@@ -1,11 +1,18 @@
1
- /** Ensures the passed `value` always stays between `min` and `max` */
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 `range_1_min-range_1_max` to the numerical range `range_2_min-range_2_max`
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, range_1_min: number, range_1_max: number, range_2_min: number, range_2_max: number): number;
8
- /** Returns a random number between `min` and `max` (inclusive) */
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 `max` (inclusive) */
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;