@sv443-network/userutils 2.0.1 → 4.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,56 +300,9 @@ 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) {
296
- const context = new (window.AudioContext || window.webkitAudioContext)();
297
- const props = {
298
- /** Sets the gain multiplier */
299
- setGain(multiplier) {
300
- props.gainNode.gain.setValueAtTime(multiplier, props.context.currentTime);
301
- },
302
- /** Returns the current gain multiplier */
303
- getGain() {
304
- return props.gainNode.gain.value;
305
- },
306
- /** Enable the amplification for the first time or if it was disabled before */
307
- enable() {
308
- props.source.connect(props.limiterNode);
309
- props.limiterNode.connect(props.gainNode);
310
- props.gainNode.connect(props.context.destination);
311
- },
312
- /** Disable the amplification */
313
- disable() {
314
- props.source.disconnect(props.limiterNode);
315
- props.limiterNode.disconnect(props.gainNode);
316
- 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: -12, knee: 30, 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()
331
- };
332
- props.setLimiterOptions({
333
- threshold: -12,
334
- knee: 30,
335
- ratio: 12,
336
- attack: 3e-3,
337
- release: 0.25
338
- });
339
- props.setGain(initialMultiplier);
340
- return props;
341
- }
342
306
  function isScrollable(element) {
343
307
  const { overflowX, overflowY } = getComputedStyle(element);
344
308
  return {
@@ -377,63 +341,184 @@ function fetchAdvanced(_0) {
377
341
  return res;
378
342
  });
379
343
  }
380
- function insertValues(str, ...values) {
381
- return str.replace(/%\d/gm, (match) => {
344
+ function insertValues(input, ...values) {
345
+ return input.replace(/%\d/gm, (match) => {
382
346
  var _a, _b;
383
347
  const argIndex = Number(match.substring(1)) - 1;
384
348
  return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? void 0 : _b.toString();
385
349
  });
386
350
  }
387
-
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);
351
+ function compress(input, compressionFormat, outputType = "base64") {
352
+ return __async(this, null, function* () {
353
+ const byteArray = typeof input === "string" ? new TextEncoder().encode(input) : input;
354
+ const comp = new CompressionStream(compressionFormat);
355
+ const writer = comp.writable.getWriter();
356
+ writer.write(byteArray);
357
+ writer.close();
358
+ const buf = yield new Response(comp.readable).arrayBuffer();
359
+ return outputType === "arrayBuffer" ? buf : ab2str(buf);
360
+ });
361
+ }
362
+ function decompress(input, compressionFormat, outputType = "string") {
363
+ return __async(this, null, function* () {
364
+ const byteArray = typeof input === "string" ? str2ab(input) : input;
365
+ const decomp = new DecompressionStream(compressionFormat);
366
+ const writer = decomp.writable.getWriter();
367
+ writer.write(byteArray);
368
+ writer.close();
369
+ const buf = yield new Response(decomp.readable).arrayBuffer();
370
+ return outputType === "arrayBuffer" ? buf : new TextDecoder().decode(buf);
371
+ });
372
+ }
373
+ function ab2str(buf) {
374
+ return getUnsafeWindow().btoa(
375
+ new Uint8Array(buf).reduce((data, byte) => data + String.fromCharCode(byte), "")
376
+ );
397
377
  }
398
- function removeOnSelector(selector) {
399
- return selectorMap.delete(selector);
378
+ function str2ab(str) {
379
+ return Uint8Array.from(getUnsafeWindow().atob(str), (c) => c.charCodeAt(0));
400
380
  }
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);
381
+
382
+ // lib/SelectorObserver.ts
383
+ var SelectorObserver = class {
384
+ constructor(baseElement, options = {}) {
385
+ __publicField(this, "enabled", false);
386
+ __publicField(this, "baseElement");
387
+ __publicField(this, "observer");
388
+ __publicField(this, "observerOptions");
389
+ __publicField(this, "listenerMap");
390
+ this.baseElement = baseElement;
391
+ this.listenerMap = /* @__PURE__ */ new Map();
392
+ this.observer = new MutationObserver(() => this.checkAllSelectors());
393
+ this.observerOptions = __spreadValues({
394
+ childList: true,
395
+ subtree: true
396
+ }, options);
397
+ }
398
+ checkAllSelectors() {
399
+ for (const [selector, listeners] of this.listenerMap.entries())
400
+ this.checkSelector(selector, listeners);
401
+ }
402
+ checkSelector(selector, listeners) {
403
+ var _a;
404
+ if (!this.enabled)
405
+ return;
406
+ const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
407
+ if (!baseElement)
408
+ return;
409
+ const all = listeners.some((listener) => listener.all);
410
+ const one = listeners.some((listener) => !listener.all);
411
+ const allElements = all ? baseElement.querySelectorAll(selector) : null;
412
+ const oneElement = one ? baseElement.querySelector(selector) : null;
413
+ for (const options of listeners) {
414
+ if (options.all) {
415
+ if (allElements && allElements.length > 0) {
416
+ options.listener(allElements);
417
+ if (!options.continuous)
418
+ this.removeListener(selector, options);
419
+ }
420
+ } else {
421
+ if (oneElement) {
422
+ options.listener(oneElement);
423
+ if (!options.continuous)
424
+ this.removeListener(selector, options);
425
+ }
410
426
  }
411
- } catch (err) {
412
- console.error(`Couldn't call listener for selector '${selector}'`, err);
427
+ if (((_a = this.listenerMap.get(selector)) == null ? void 0 : _a.length) === 0)
428
+ this.listenerMap.delete(selector);
413
429
  }
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);
430
+ }
431
+ debounce(func, time) {
432
+ let timeout;
433
+ return function(...args) {
434
+ clearTimeout(timeout);
435
+ timeout = setTimeout(() => func.apply(this, args), time);
436
+ };
437
+ }
438
+ /**
439
+ * Starts observing the children of the base element for changes to the given {@linkcode selector} according to the set {@linkcode options}
440
+ * @param selector The selector to observe
441
+ * @param options Options for the selector observation
442
+ * @param options.listener Gets called whenever the selector was found in the DOM
443
+ * @param [options.all] Whether to use `querySelectorAll()` instead - default is false
444
+ * @param [options.continuous] Whether to call the listener continuously instead of just once - default is false
445
+ * @param [options.debounce] Whether to debounce the listener to reduce calls to `querySelector` or `querySelectorAll` - set undefined or <=0 to disable (default)
446
+ */
447
+ addListener(selector, options) {
448
+ options = __spreadValues({ all: false, continuous: false, debounce: 0 }, options);
449
+ if (options.debounce && options.debounce > 0 || this.observerOptions.defaultDebounce && this.observerOptions.defaultDebounce > 0) {
450
+ options.listener = this.debounce(
451
+ options.listener,
452
+ options.debounce || this.observerOptions.defaultDebounce
453
+ );
421
454
  }
455
+ if (this.listenerMap.has(selector))
456
+ this.listenerMap.get(selector).push(options);
457
+ else
458
+ this.listenerMap.set(selector, [options]);
459
+ this.checkSelector(selector, [options]);
422
460
  }
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
- }
461
+ /** Disables the observation of the child elements */
462
+ disable() {
463
+ if (!this.enabled)
464
+ return;
465
+ this.enabled = false;
466
+ this.observer.disconnect();
467
+ }
468
+ /**
469
+ * Enables or reenables the observation of the child elements.
470
+ * @param immediatelyCheckSelectors Whether to immediately check if all previously registered selectors exist (default is true)
471
+ * @returns Returns true when the observation was enabled, false otherwise (e.g. when the base element wasn't found)
472
+ */
473
+ enable(immediatelyCheckSelectors = true) {
474
+ const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
475
+ if (this.enabled || !baseElement)
476
+ return false;
477
+ this.enabled = true;
478
+ this.observer.observe(baseElement, this.observerOptions);
479
+ if (immediatelyCheckSelectors)
480
+ this.checkAllSelectors();
481
+ return true;
482
+ }
483
+ /** Returns whether the observation of the child elements is currently enabled */
484
+ isEnabled() {
485
+ return this.enabled;
486
+ }
487
+ /** Removes all listeners that have been registered with {@linkcode addListener()} */
488
+ clearListeners() {
489
+ this.listenerMap.clear();
490
+ }
491
+ /**
492
+ * Removes all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()}
493
+ * @returns Returns true when all listeners for the associated selector were found and removed, false otherwise
494
+ */
495
+ removeAllListeners(selector) {
496
+ return this.listenerMap.delete(selector);
497
+ }
498
+ /**
499
+ * Removes a single listener for the given {@linkcode selector} and {@linkcode options} that has been registered with {@linkcode addListener()}
500
+ * @returns Returns true when the listener was found and removed, false otherwise
501
+ */
502
+ removeListener(selector, options) {
503
+ const listeners = this.listenerMap.get(selector);
504
+ if (!listeners)
505
+ return false;
506
+ const index = listeners.indexOf(options);
507
+ if (index > -1) {
508
+ listeners.splice(index, 1);
509
+ return true;
510
+ }
511
+ return false;
512
+ }
513
+ /** Returns all listeners that have been registered with {@linkcode addListener()} */
514
+ getAllListeners() {
515
+ return this.listenerMap;
516
+ }
517
+ /** Returns all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()} */
518
+ getListeners(selector) {
519
+ return this.listenerMap.get(selector);
520
+ }
521
+ };
437
522
 
438
523
  // lib/translation.ts
439
524
  var trans = {};
@@ -460,4 +545,4 @@ tr.getLanguage = () => {
460
545
  return curLang;
461
546
  };
462
547
 
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 };
548
+ export { ConfigManager, SelectorObserver, addGlobalStyle, addParent, autoPlural, clamp, compress, debounce, decompress, 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,11 +1,13 @@
1
+ /** Describes an array with at least one item */
2
+ export type NonEmptyArray<TArray = unknown> = [TArray, ...TArray[]];
1
3
  /** Returns a random item from the passed array */
2
- export declare function randomItem<T = unknown>(array: T[]): T | undefined;
4
+ export declare function randomItem<TItem = unknown>(array: TItem[]): TItem | undefined;
3
5
  /**
4
6
  * Returns a tuple of a random item and its index from the passed array
5
7
  * Returns `[undefined, undefined]` if the passed array is empty
6
8
  */
7
- export declare function randomItemIndex<T = unknown>(array: T[]): [item?: T, index?: number];
9
+ export declare function randomItemIndex<TItem = unknown>(array: TItem[]): [item?: TItem, index?: number];
8
10
  /** Returns a random item from the passed array and mutates the array to remove the item */
9
- export declare function takeRandomItem<T = unknown>(arr: T[]): T | undefined;
11
+ export declare function takeRandomItem<TItem = unknown>(arr: TItem[]): TItem | undefined;
10
12
  /** Returns a copy of the array with its items in a random order */
11
- export declare function randomizeArray<T = unknown>(array: T[]): T[];
13
+ export declare function randomizeArray<TItem = unknown>(array: TItem[]): TItem[];
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,19 @@ 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;
46
- /**
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.
49
- * This function supports any MediaElement instance like `<audio>` or `<video>`
50
- *
51
- * This is the audio processing workflow:
52
- * `MediaElement (source)` => `DynamicsCompressorNode (limiter)` => `GainNode` => `AudioDestinationNode (output)`
53
- *
54
- * ⚠️ 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
- * ⚠️ Make sure to call the returned function `enable()` after calling this function to actually enable the amplification.
56
- *
57
- * @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`)
59
- * @returns Returns an object with the following properties:
60
- * | Property | Description |
61
- * | :-- | :-- |
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 |
65
- * | `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 }` |
67
- * | `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 |
71
- */
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 */
76
- getGain(): number;
77
- /** Enable the amplification for the first time or if it was disabled before */
78
- enable(): void;
79
- /** Disable the amplification */
80
- 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: -12, knee: 30, 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
- };
91
- /** An object which contains the results of `amplifyMedia()` */
92
- export type AmplifyMediaResult = ReturnType<typeof amplifyMedia>;
47
+ export declare function interceptWindowEvent<TEvtKey extends keyof WindowEventMap>(eventName: TEvtKey, predicate?: (event: WindowEventMap[TEvtKey]) => boolean): void;
93
48
  /** Checks if an element is scrollable in the horizontal and vertical directions */
94
49
  export declare function isScrollable(element: Element): {
95
50
  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;