@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.js
CHANGED
|
@@ -48,28 +48,36 @@ var __async = (__this, __arguments, generator) => {
|
|
|
48
48
|
function clamp(value, min, max) {
|
|
49
49
|
return Math.max(Math.min(value, max), min);
|
|
50
50
|
}
|
|
51
|
-
function mapRange(value,
|
|
52
|
-
if (Number(
|
|
53
|
-
return value * (
|
|
54
|
-
return (value -
|
|
51
|
+
function mapRange(value, range1min, range1max, range2min, range2max) {
|
|
52
|
+
if (Number(range1min) === 0 && Number(range2min) === 0)
|
|
53
|
+
return value * (range2max / range1max);
|
|
54
|
+
return (value - range1min) * ((range2max - range2min) / (range1max - range1min)) + range2min;
|
|
55
55
|
}
|
|
56
56
|
function randRange(...args) {
|
|
57
57
|
let min, max;
|
|
58
|
-
if (typeof args[0] === "number" && typeof args[1] === "number")
|
|
58
|
+
if (typeof args[0] === "number" && typeof args[1] === "number")
|
|
59
59
|
[min, max] = args;
|
|
60
|
-
|
|
60
|
+
else if (typeof args[0] === "number" && typeof args[1] !== "number") {
|
|
61
61
|
min = 0;
|
|
62
|
-
max = args
|
|
62
|
+
[max] = args;
|
|
63
63
|
} else
|
|
64
64
|
throw new TypeError(`Wrong parameter(s) provided - expected: "number" and "number|undefined", got: "${typeof args[0]}" and "${typeof args[1]}"`);
|
|
65
65
|
min = Number(min);
|
|
66
66
|
max = Number(max);
|
|
67
67
|
if (isNaN(min) || isNaN(max))
|
|
68
|
-
|
|
68
|
+
return NaN;
|
|
69
69
|
if (min > max)
|
|
70
70
|
throw new TypeError(`Parameter "min" can't be bigger than "max"`);
|
|
71
71
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
72
72
|
}
|
|
73
|
+
function randomId(length = 16, radix = 16) {
|
|
74
|
+
const arr = new Uint8Array(length);
|
|
75
|
+
crypto.getRandomValues(arr);
|
|
76
|
+
return Array.from(
|
|
77
|
+
arr,
|
|
78
|
+
(v) => mapRange(v, 0, 255, 0, radix).toString(radix).substring(0, 1)
|
|
79
|
+
).join("");
|
|
80
|
+
}
|
|
73
81
|
|
|
74
82
|
// lib/array.ts
|
|
75
83
|
function randomItem(array) {
|
|
@@ -99,14 +107,14 @@ function randomizeArray(array) {
|
|
|
99
107
|
return retArray;
|
|
100
108
|
}
|
|
101
109
|
|
|
102
|
-
// lib/
|
|
110
|
+
// lib/ConfigManager.ts
|
|
103
111
|
var ConfigManager = class {
|
|
104
112
|
/**
|
|
105
113
|
* Creates an instance of ConfigManager to manage a user configuration that is cached in memory and persistently saved across sessions.
|
|
106
114
|
* 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.
|
|
107
115
|
*
|
|
108
116
|
* ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue`
|
|
109
|
-
* ⚠️ Make sure to call
|
|
117
|
+
* ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultConfig`
|
|
110
118
|
*
|
|
111
119
|
* @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`
|
|
112
120
|
* @param options The options for this ConfigManager instance
|
|
@@ -149,7 +157,10 @@ var ConfigManager = class {
|
|
|
149
157
|
}
|
|
150
158
|
});
|
|
151
159
|
}
|
|
152
|
-
/**
|
|
160
|
+
/**
|
|
161
|
+
* Returns a copy of the data from the in-memory cache.
|
|
162
|
+
* Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).
|
|
163
|
+
*/
|
|
153
164
|
getData() {
|
|
154
165
|
return this.deepCopy(this.cachedConfig);
|
|
155
166
|
}
|
|
@@ -179,8 +190,8 @@ var ConfigManager = class {
|
|
|
179
190
|
}
|
|
180
191
|
/**
|
|
181
192
|
* Call this method to clear all persistently stored data associated with this ConfigManager instance.
|
|
182
|
-
* The in-memory cache will be left untouched, so you may still access the data with
|
|
183
|
-
* Calling
|
|
193
|
+
* The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
|
|
194
|
+
* Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
|
|
184
195
|
*
|
|
185
196
|
* ⚠️ This requires the additional directive `@grant GM.deleteValue`
|
|
186
197
|
*/
|
|
@@ -273,7 +284,7 @@ function openInNewTab(href) {
|
|
|
273
284
|
openElem.click();
|
|
274
285
|
setTimeout(openElem.remove, 50);
|
|
275
286
|
}
|
|
276
|
-
function interceptEvent(eventObject, eventName, predicate) {
|
|
287
|
+
function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
277
288
|
if (typeof Error.stackTraceLimit === "number" && Error.stackTraceLimit < 1e3) {
|
|
278
289
|
Error.stackTraceLimit = 1e3;
|
|
279
290
|
}
|
|
@@ -291,54 +302,44 @@ function interceptEvent(eventObject, eventName, predicate) {
|
|
|
291
302
|
};
|
|
292
303
|
})(eventObject.__proto__.addEventListener);
|
|
293
304
|
}
|
|
294
|
-
function interceptWindowEvent(eventName, predicate) {
|
|
305
|
+
function interceptWindowEvent(eventName, predicate = () => true) {
|
|
295
306
|
return interceptEvent(getUnsafeWindow(), eventName, predicate);
|
|
296
307
|
}
|
|
297
|
-
function amplifyMedia(mediaElement,
|
|
308
|
+
function amplifyMedia(mediaElement, initialGain = 1) {
|
|
298
309
|
const context = new (window.AudioContext || window.webkitAudioContext)();
|
|
299
310
|
const props = {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
311
|
+
context,
|
|
312
|
+
sourceNode: context.createMediaElementSource(mediaElement),
|
|
313
|
+
gainNode: context.createGain(),
|
|
314
|
+
/** Sets the gain of the amplifying GainNode */
|
|
315
|
+
setGain(gain) {
|
|
316
|
+
props.gainNode.gain.value = gain;
|
|
303
317
|
},
|
|
304
|
-
/** Returns the current gain
|
|
318
|
+
/** Returns the current gain of the amplifying GainNode */
|
|
305
319
|
getGain() {
|
|
306
320
|
return props.gainNode.gain.value;
|
|
307
321
|
},
|
|
322
|
+
/** Whether the amplification is currently enabled */
|
|
323
|
+
enabled: false,
|
|
308
324
|
/** Enable the amplification for the first time or if it was disabled before */
|
|
309
325
|
enable() {
|
|
310
|
-
|
|
311
|
-
|
|
326
|
+
if (props.enabled)
|
|
327
|
+
return;
|
|
328
|
+
props.enabled = true;
|
|
329
|
+
props.sourceNode.connect(props.gainNode);
|
|
312
330
|
props.gainNode.connect(props.context.destination);
|
|
313
331
|
},
|
|
314
332
|
/** Disable the amplification */
|
|
315
333
|
disable() {
|
|
316
|
-
|
|
317
|
-
|
|
334
|
+
if (!props.enabled)
|
|
335
|
+
return;
|
|
336
|
+
props.enabled = false;
|
|
337
|
+
props.sourceNode.disconnect(props.gainNode);
|
|
318
338
|
props.gainNode.disconnect(props.context.destination);
|
|
319
|
-
props.
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Set the options of the [limiter / DynamicsCompressorNode](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode/DynamicsCompressorNode#options)
|
|
323
|
-
* The default is `{ threshold: -2, knee: 40, ratio: 12, attack: 0.003, release: 0.25 }`
|
|
324
|
-
*/
|
|
325
|
-
setLimiterOptions(options) {
|
|
326
|
-
for (const [key, val] of Object.entries(options))
|
|
327
|
-
props.limiterNode[key].setValueAtTime(val, props.context.currentTime);
|
|
328
|
-
},
|
|
329
|
-
context,
|
|
330
|
-
source: context.createMediaElementSource(mediaElement),
|
|
331
|
-
gainNode: context.createGain(),
|
|
332
|
-
limiterNode: context.createDynamicsCompressor()
|
|
339
|
+
props.sourceNode.connect(props.context.destination);
|
|
340
|
+
}
|
|
333
341
|
};
|
|
334
|
-
props.
|
|
335
|
-
threshold: -2,
|
|
336
|
-
knee: 40,
|
|
337
|
-
ratio: 12,
|
|
338
|
-
attack: 3e-3,
|
|
339
|
-
release: 0.25
|
|
340
|
-
});
|
|
341
|
-
props.setGain(initialMultiplier);
|
|
342
|
+
props.setGain(initialGain);
|
|
342
343
|
return props;
|
|
343
344
|
}
|
|
344
345
|
function isScrollable(element) {
|
|
@@ -387,55 +388,146 @@ function insertValues(str, ...values) {
|
|
|
387
388
|
});
|
|
388
389
|
}
|
|
389
390
|
|
|
390
|
-
// lib/
|
|
391
|
-
var
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
391
|
+
// lib/SelectorObserver.ts
|
|
392
|
+
var SelectorObserver = class {
|
|
393
|
+
constructor(baseElement, options = {}) {
|
|
394
|
+
__publicField(this, "enabled", false);
|
|
395
|
+
__publicField(this, "baseElement");
|
|
396
|
+
__publicField(this, "observer");
|
|
397
|
+
__publicField(this, "observerOptions");
|
|
398
|
+
__publicField(this, "listenerMap");
|
|
399
|
+
this.baseElement = baseElement;
|
|
400
|
+
this.listenerMap = /* @__PURE__ */ new Map();
|
|
401
|
+
this.observer = new MutationObserver(() => this.checkAllSelectors());
|
|
402
|
+
this.observerOptions = __spreadValues({
|
|
403
|
+
childList: true,
|
|
404
|
+
subtree: true
|
|
405
|
+
}, options);
|
|
406
|
+
}
|
|
407
|
+
checkAllSelectors() {
|
|
408
|
+
for (const [selector, listeners] of this.listenerMap.entries())
|
|
409
|
+
this.checkSelector(selector, listeners);
|
|
410
|
+
}
|
|
411
|
+
checkSelector(selector, listeners) {
|
|
412
|
+
var _a;
|
|
413
|
+
if (!this.enabled)
|
|
414
|
+
return;
|
|
415
|
+
const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
|
|
416
|
+
if (!baseElement)
|
|
417
|
+
return;
|
|
418
|
+
const all = listeners.some((listener) => listener.all);
|
|
419
|
+
const one = listeners.some((listener) => !listener.all);
|
|
420
|
+
const allElements = all ? baseElement.querySelectorAll(selector) : null;
|
|
421
|
+
const oneElement = one ? baseElement.querySelector(selector) : null;
|
|
422
|
+
for (const options of listeners) {
|
|
423
|
+
if (options.all) {
|
|
424
|
+
if (allElements && allElements.length > 0) {
|
|
425
|
+
options.listener(allElements);
|
|
426
|
+
if (!options.continuous)
|
|
427
|
+
this.removeListener(selector, options);
|
|
428
|
+
}
|
|
429
|
+
} else {
|
|
430
|
+
if (oneElement) {
|
|
431
|
+
options.listener(oneElement);
|
|
432
|
+
if (!options.continuous)
|
|
433
|
+
this.removeListener(selector, options);
|
|
434
|
+
}
|
|
412
435
|
}
|
|
413
|
-
|
|
414
|
-
|
|
436
|
+
if (((_a = this.listenerMap.get(selector)) == null ? void 0 : _a.length) === 0)
|
|
437
|
+
this.listenerMap.delete(selector);
|
|
415
438
|
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
439
|
+
}
|
|
440
|
+
debounce(func, time) {
|
|
441
|
+
let timeout;
|
|
442
|
+
return function(...args) {
|
|
443
|
+
clearTimeout(timeout);
|
|
444
|
+
timeout = setTimeout(() => func.apply(this, args), time);
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Starts observing the children of the base element for changes to the given {@linkcode selector} according to the set {@linkcode options}
|
|
449
|
+
* @param selector The selector to observe
|
|
450
|
+
* @param options Options for the selector observation
|
|
451
|
+
* @param options.listener Gets called whenever the selector was found in the DOM
|
|
452
|
+
* @param [options.all] Whether to use `querySelectorAll()` instead - default is false
|
|
453
|
+
* @param [options.continuous] Whether to call the listener continuously instead of just once - default is false
|
|
454
|
+
* @param [options.debounce] Whether to debounce the listener to reduce calls to `querySelector` or `querySelectorAll` - set undefined or <=0 to disable (default)
|
|
455
|
+
*/
|
|
456
|
+
addListener(selector, options) {
|
|
457
|
+
options = __spreadValues({ all: false, continuous: false, debounce: 0 }, options);
|
|
458
|
+
if (options.debounce && options.debounce > 0 || this.observerOptions.defaultDebounce && this.observerOptions.defaultDebounce > 0) {
|
|
459
|
+
options.listener = this.debounce(
|
|
460
|
+
options.listener,
|
|
461
|
+
options.debounce || this.observerOptions.defaultDebounce
|
|
462
|
+
);
|
|
423
463
|
}
|
|
464
|
+
if (this.listenerMap.has(selector))
|
|
465
|
+
this.listenerMap.get(selector).push(options);
|
|
466
|
+
else
|
|
467
|
+
this.listenerMap.set(selector, [options]);
|
|
468
|
+
this.checkSelector(selector, [options]);
|
|
424
469
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
470
|
+
/** Disables the observation of the child elements */
|
|
471
|
+
disable() {
|
|
472
|
+
if (!this.enabled)
|
|
473
|
+
return;
|
|
474
|
+
this.enabled = false;
|
|
475
|
+
this.observer.disconnect();
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Enables or reenables the observation of the child elements.
|
|
479
|
+
* @param immediatelyCheckSelectors Whether to immediately check if all previously registered selectors exist (default is true)
|
|
480
|
+
* @returns Returns true when the observation was enabled, false otherwise (e.g. when the base element wasn't found)
|
|
481
|
+
*/
|
|
482
|
+
enable(immediatelyCheckSelectors = true) {
|
|
483
|
+
const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
|
|
484
|
+
if (this.enabled || !baseElement)
|
|
485
|
+
return false;
|
|
486
|
+
this.enabled = true;
|
|
487
|
+
this.observer.observe(baseElement, this.observerOptions);
|
|
488
|
+
if (immediatelyCheckSelectors)
|
|
489
|
+
this.checkAllSelectors();
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
/** Returns whether the observation of the child elements is currently enabled */
|
|
493
|
+
isEnabled() {
|
|
494
|
+
return this.enabled;
|
|
495
|
+
}
|
|
496
|
+
/** Removes all listeners that have been registered with {@linkcode addListener()} */
|
|
497
|
+
clearListeners() {
|
|
498
|
+
this.listenerMap.clear();
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Removes all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()}
|
|
502
|
+
* @returns Returns true when all listeners for the associated selector were found and removed, false otherwise
|
|
503
|
+
*/
|
|
504
|
+
removeAllListeners(selector) {
|
|
505
|
+
return this.listenerMap.delete(selector);
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Removes a single listener for the given {@linkcode selector} and {@linkcode options} that has been registered with {@linkcode addListener()}
|
|
509
|
+
* @returns Returns true when the listener was found and removed, false otherwise
|
|
510
|
+
*/
|
|
511
|
+
removeListener(selector, options) {
|
|
512
|
+
const listeners = this.listenerMap.get(selector);
|
|
513
|
+
if (!listeners)
|
|
514
|
+
return false;
|
|
515
|
+
const index = listeners.indexOf(options);
|
|
516
|
+
if (index > -1) {
|
|
517
|
+
listeners.splice(index, 1);
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
/** Returns all listeners that have been registered with {@linkcode addListener()} */
|
|
523
|
+
getAllListeners() {
|
|
524
|
+
return this.listenerMap;
|
|
525
|
+
}
|
|
526
|
+
/** Returns all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()} */
|
|
527
|
+
getListeners(selector) {
|
|
528
|
+
return this.listenerMap.get(selector);
|
|
529
|
+
}
|
|
530
|
+
};
|
|
439
531
|
|
|
440
532
|
// lib/translation.ts
|
|
441
533
|
var trans = {};
|
|
@@ -463,6 +555,7 @@ tr.getLanguage = () => {
|
|
|
463
555
|
};
|
|
464
556
|
|
|
465
557
|
exports.ConfigManager = ConfigManager;
|
|
558
|
+
exports.SelectorObserver = SelectorObserver;
|
|
466
559
|
exports.addGlobalStyle = addGlobalStyle;
|
|
467
560
|
exports.addParent = addParent;
|
|
468
561
|
exports.amplifyMedia = amplifyMedia;
|
|
@@ -470,23 +563,20 @@ exports.autoPlural = autoPlural;
|
|
|
470
563
|
exports.clamp = clamp;
|
|
471
564
|
exports.debounce = debounce;
|
|
472
565
|
exports.fetchAdvanced = fetchAdvanced;
|
|
473
|
-
exports.getSelectorMap = getSelectorMap;
|
|
474
566
|
exports.getUnsafeWindow = getUnsafeWindow;
|
|
475
|
-
exports.initOnSelector = initOnSelector;
|
|
476
567
|
exports.insertAfter = insertAfter;
|
|
477
568
|
exports.insertValues = insertValues;
|
|
478
569
|
exports.interceptEvent = interceptEvent;
|
|
479
570
|
exports.interceptWindowEvent = interceptWindowEvent;
|
|
480
571
|
exports.isScrollable = isScrollable;
|
|
481
572
|
exports.mapRange = mapRange;
|
|
482
|
-
exports.onSelector = onSelector;
|
|
483
573
|
exports.openInNewTab = openInNewTab;
|
|
484
574
|
exports.pauseFor = pauseFor;
|
|
485
575
|
exports.preloadImages = preloadImages;
|
|
486
576
|
exports.randRange = randRange;
|
|
577
|
+
exports.randomId = randomId;
|
|
487
578
|
exports.randomItem = randomItem;
|
|
488
579
|
exports.randomItemIndex = randomItemIndex;
|
|
489
580
|
exports.randomizeArray = randomizeArray;
|
|
490
|
-
exports.removeOnSelector = removeOnSelector;
|
|
491
581
|
exports.takeRandomItem = takeRandomItem;
|
|
492
582
|
exports.tr = tr;
|