@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.global.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// ==UserLibrary==
|
|
10
10
|
// @name UserUtils
|
|
11
11
|
// @description Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, manage persistent user configurations, modify the DOM more easily and more
|
|
12
|
-
// @version
|
|
12
|
+
// @version 3.0.0
|
|
13
13
|
// @license MIT
|
|
14
14
|
// @copyright Sv443 (https://github.com/Sv443)
|
|
15
15
|
|
|
@@ -69,28 +69,36 @@ var UserUtils = (function (exports) {
|
|
|
69
69
|
function clamp(value, min, max) {
|
|
70
70
|
return Math.max(Math.min(value, max), min);
|
|
71
71
|
}
|
|
72
|
-
function mapRange(value,
|
|
73
|
-
if (Number(
|
|
74
|
-
return value * (
|
|
75
|
-
return (value -
|
|
72
|
+
function mapRange(value, range1min, range1max, range2min, range2max) {
|
|
73
|
+
if (Number(range1min) === 0 && Number(range2min) === 0)
|
|
74
|
+
return value * (range2max / range1max);
|
|
75
|
+
return (value - range1min) * ((range2max - range2min) / (range1max - range1min)) + range2min;
|
|
76
76
|
}
|
|
77
77
|
function randRange(...args) {
|
|
78
78
|
let min, max;
|
|
79
|
-
if (typeof args[0] === "number" && typeof args[1] === "number")
|
|
79
|
+
if (typeof args[0] === "number" && typeof args[1] === "number")
|
|
80
80
|
[min, max] = args;
|
|
81
|
-
|
|
81
|
+
else if (typeof args[0] === "number" && typeof args[1] !== "number") {
|
|
82
82
|
min = 0;
|
|
83
|
-
max = args
|
|
83
|
+
[max] = args;
|
|
84
84
|
} else
|
|
85
85
|
throw new TypeError(`Wrong parameter(s) provided - expected: "number" and "number|undefined", got: "${typeof args[0]}" and "${typeof args[1]}"`);
|
|
86
86
|
min = Number(min);
|
|
87
87
|
max = Number(max);
|
|
88
88
|
if (isNaN(min) || isNaN(max))
|
|
89
|
-
|
|
89
|
+
return NaN;
|
|
90
90
|
if (min > max)
|
|
91
91
|
throw new TypeError(`Parameter "min" can't be bigger than "max"`);
|
|
92
92
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
93
93
|
}
|
|
94
|
+
function randomId(length = 16, radix = 16) {
|
|
95
|
+
const arr = new Uint8Array(length);
|
|
96
|
+
crypto.getRandomValues(arr);
|
|
97
|
+
return Array.from(
|
|
98
|
+
arr,
|
|
99
|
+
(v) => mapRange(v, 0, 255, 0, radix).toString(radix).substring(0, 1)
|
|
100
|
+
).join("");
|
|
101
|
+
}
|
|
94
102
|
|
|
95
103
|
// lib/array.ts
|
|
96
104
|
function randomItem(array) {
|
|
@@ -120,14 +128,14 @@ var UserUtils = (function (exports) {
|
|
|
120
128
|
return retArray;
|
|
121
129
|
}
|
|
122
130
|
|
|
123
|
-
// lib/
|
|
131
|
+
// lib/ConfigManager.ts
|
|
124
132
|
var ConfigManager = class {
|
|
125
133
|
/**
|
|
126
134
|
* Creates an instance of ConfigManager to manage a user configuration that is cached in memory and persistently saved across sessions.
|
|
127
135
|
* 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.
|
|
128
136
|
*
|
|
129
137
|
* ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue`
|
|
130
|
-
* ⚠️ Make sure to call
|
|
138
|
+
* ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultConfig`
|
|
131
139
|
*
|
|
132
140
|
* @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`
|
|
133
141
|
* @param options The options for this ConfigManager instance
|
|
@@ -170,7 +178,10 @@ var UserUtils = (function (exports) {
|
|
|
170
178
|
}
|
|
171
179
|
});
|
|
172
180
|
}
|
|
173
|
-
/**
|
|
181
|
+
/**
|
|
182
|
+
* Returns a copy of the data from the in-memory cache.
|
|
183
|
+
* Use {@linkcode loadData()} to get fresh data from persistent storage (usually not necessary since the cache should always exactly reflect persistent storage).
|
|
184
|
+
*/
|
|
174
185
|
getData() {
|
|
175
186
|
return this.deepCopy(this.cachedConfig);
|
|
176
187
|
}
|
|
@@ -200,8 +211,8 @@ var UserUtils = (function (exports) {
|
|
|
200
211
|
}
|
|
201
212
|
/**
|
|
202
213
|
* Call this method to clear all persistently stored data associated with this ConfigManager instance.
|
|
203
|
-
* The in-memory cache will be left untouched, so you may still access the data with
|
|
204
|
-
* Calling
|
|
214
|
+
* The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
|
|
215
|
+
* Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
|
|
205
216
|
*
|
|
206
217
|
* ⚠️ This requires the additional directive `@grant GM.deleteValue`
|
|
207
218
|
*/
|
|
@@ -294,7 +305,7 @@ var UserUtils = (function (exports) {
|
|
|
294
305
|
openElem.click();
|
|
295
306
|
setTimeout(openElem.remove, 50);
|
|
296
307
|
}
|
|
297
|
-
function interceptEvent(eventObject, eventName, predicate) {
|
|
308
|
+
function interceptEvent(eventObject, eventName, predicate = () => true) {
|
|
298
309
|
if (typeof Error.stackTraceLimit === "number" && Error.stackTraceLimit < 1e3) {
|
|
299
310
|
Error.stackTraceLimit = 1e3;
|
|
300
311
|
}
|
|
@@ -312,54 +323,44 @@ var UserUtils = (function (exports) {
|
|
|
312
323
|
};
|
|
313
324
|
})(eventObject.__proto__.addEventListener);
|
|
314
325
|
}
|
|
315
|
-
function interceptWindowEvent(eventName, predicate) {
|
|
326
|
+
function interceptWindowEvent(eventName, predicate = () => true) {
|
|
316
327
|
return interceptEvent(getUnsafeWindow(), eventName, predicate);
|
|
317
328
|
}
|
|
318
|
-
function amplifyMedia(mediaElement,
|
|
329
|
+
function amplifyMedia(mediaElement, initialGain = 1) {
|
|
319
330
|
const context = new (window.AudioContext || window.webkitAudioContext)();
|
|
320
331
|
const props = {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
332
|
+
context,
|
|
333
|
+
sourceNode: context.createMediaElementSource(mediaElement),
|
|
334
|
+
gainNode: context.createGain(),
|
|
335
|
+
/** Sets the gain of the amplifying GainNode */
|
|
336
|
+
setGain(gain) {
|
|
337
|
+
props.gainNode.gain.value = gain;
|
|
324
338
|
},
|
|
325
|
-
/** Returns the current gain
|
|
339
|
+
/** Returns the current gain of the amplifying GainNode */
|
|
326
340
|
getGain() {
|
|
327
341
|
return props.gainNode.gain.value;
|
|
328
342
|
},
|
|
343
|
+
/** Whether the amplification is currently enabled */
|
|
344
|
+
enabled: false,
|
|
329
345
|
/** Enable the amplification for the first time or if it was disabled before */
|
|
330
346
|
enable() {
|
|
331
|
-
|
|
332
|
-
|
|
347
|
+
if (props.enabled)
|
|
348
|
+
return;
|
|
349
|
+
props.enabled = true;
|
|
350
|
+
props.sourceNode.connect(props.gainNode);
|
|
333
351
|
props.gainNode.connect(props.context.destination);
|
|
334
352
|
},
|
|
335
353
|
/** Disable the amplification */
|
|
336
354
|
disable() {
|
|
337
|
-
|
|
338
|
-
|
|
355
|
+
if (!props.enabled)
|
|
356
|
+
return;
|
|
357
|
+
props.enabled = false;
|
|
358
|
+
props.sourceNode.disconnect(props.gainNode);
|
|
339
359
|
props.gainNode.disconnect(props.context.destination);
|
|
340
|
-
props.
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Set the options of the [limiter / DynamicsCompressorNode](https://developer.mozilla.org/en-US/docs/Web/API/DynamicsCompressorNode/DynamicsCompressorNode#options)
|
|
344
|
-
* The default is `{ threshold: -2, knee: 40, ratio: 12, attack: 0.003, release: 0.25 }`
|
|
345
|
-
*/
|
|
346
|
-
setLimiterOptions(options) {
|
|
347
|
-
for (const [key, val] of Object.entries(options))
|
|
348
|
-
props.limiterNode[key].setValueAtTime(val, props.context.currentTime);
|
|
349
|
-
},
|
|
350
|
-
context,
|
|
351
|
-
source: context.createMediaElementSource(mediaElement),
|
|
352
|
-
gainNode: context.createGain(),
|
|
353
|
-
limiterNode: context.createDynamicsCompressor()
|
|
360
|
+
props.sourceNode.connect(props.context.destination);
|
|
361
|
+
}
|
|
354
362
|
};
|
|
355
|
-
props.
|
|
356
|
-
threshold: -2,
|
|
357
|
-
knee: 40,
|
|
358
|
-
ratio: 12,
|
|
359
|
-
attack: 3e-3,
|
|
360
|
-
release: 0.25
|
|
361
|
-
});
|
|
362
|
-
props.setGain(initialMultiplier);
|
|
363
|
+
props.setGain(initialGain);
|
|
363
364
|
return props;
|
|
364
365
|
}
|
|
365
366
|
function isScrollable(element) {
|
|
@@ -408,55 +409,146 @@ var UserUtils = (function (exports) {
|
|
|
408
409
|
});
|
|
409
410
|
}
|
|
410
411
|
|
|
411
|
-
// lib/
|
|
412
|
-
var
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
412
|
+
// lib/SelectorObserver.ts
|
|
413
|
+
var SelectorObserver = class {
|
|
414
|
+
constructor(baseElement, options = {}) {
|
|
415
|
+
__publicField(this, "enabled", false);
|
|
416
|
+
__publicField(this, "baseElement");
|
|
417
|
+
__publicField(this, "observer");
|
|
418
|
+
__publicField(this, "observerOptions");
|
|
419
|
+
__publicField(this, "listenerMap");
|
|
420
|
+
this.baseElement = baseElement;
|
|
421
|
+
this.listenerMap = /* @__PURE__ */ new Map();
|
|
422
|
+
this.observer = new MutationObserver(() => this.checkAllSelectors());
|
|
423
|
+
this.observerOptions = __spreadValues({
|
|
424
|
+
childList: true,
|
|
425
|
+
subtree: true
|
|
426
|
+
}, options);
|
|
427
|
+
}
|
|
428
|
+
checkAllSelectors() {
|
|
429
|
+
for (const [selector, listeners] of this.listenerMap.entries())
|
|
430
|
+
this.checkSelector(selector, listeners);
|
|
431
|
+
}
|
|
432
|
+
checkSelector(selector, listeners) {
|
|
433
|
+
var _a;
|
|
434
|
+
if (!this.enabled)
|
|
435
|
+
return;
|
|
436
|
+
const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
|
|
437
|
+
if (!baseElement)
|
|
438
|
+
return;
|
|
439
|
+
const all = listeners.some((listener) => listener.all);
|
|
440
|
+
const one = listeners.some((listener) => !listener.all);
|
|
441
|
+
const allElements = all ? baseElement.querySelectorAll(selector) : null;
|
|
442
|
+
const oneElement = one ? baseElement.querySelector(selector) : null;
|
|
443
|
+
for (const options of listeners) {
|
|
444
|
+
if (options.all) {
|
|
445
|
+
if (allElements && allElements.length > 0) {
|
|
446
|
+
options.listener(allElements);
|
|
447
|
+
if (!options.continuous)
|
|
448
|
+
this.removeListener(selector, options);
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
if (oneElement) {
|
|
452
|
+
options.listener(oneElement);
|
|
453
|
+
if (!options.continuous)
|
|
454
|
+
this.removeListener(selector, options);
|
|
455
|
+
}
|
|
433
456
|
}
|
|
434
|
-
|
|
435
|
-
|
|
457
|
+
if (((_a = this.listenerMap.get(selector)) == null ? void 0 : _a.length) === 0)
|
|
458
|
+
this.listenerMap.delete(selector);
|
|
436
459
|
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
460
|
+
}
|
|
461
|
+
debounce(func, time) {
|
|
462
|
+
let timeout;
|
|
463
|
+
return function(...args) {
|
|
464
|
+
clearTimeout(timeout);
|
|
465
|
+
timeout = setTimeout(() => func.apply(this, args), time);
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Starts observing the children of the base element for changes to the given {@linkcode selector} according to the set {@linkcode options}
|
|
470
|
+
* @param selector The selector to observe
|
|
471
|
+
* @param options Options for the selector observation
|
|
472
|
+
* @param options.listener Gets called whenever the selector was found in the DOM
|
|
473
|
+
* @param [options.all] Whether to use `querySelectorAll()` instead - default is false
|
|
474
|
+
* @param [options.continuous] Whether to call the listener continuously instead of just once - default is false
|
|
475
|
+
* @param [options.debounce] Whether to debounce the listener to reduce calls to `querySelector` or `querySelectorAll` - set undefined or <=0 to disable (default)
|
|
476
|
+
*/
|
|
477
|
+
addListener(selector, options) {
|
|
478
|
+
options = __spreadValues({ all: false, continuous: false, debounce: 0 }, options);
|
|
479
|
+
if (options.debounce && options.debounce > 0 || this.observerOptions.defaultDebounce && this.observerOptions.defaultDebounce > 0) {
|
|
480
|
+
options.listener = this.debounce(
|
|
481
|
+
options.listener,
|
|
482
|
+
options.debounce || this.observerOptions.defaultDebounce
|
|
483
|
+
);
|
|
444
484
|
}
|
|
485
|
+
if (this.listenerMap.has(selector))
|
|
486
|
+
this.listenerMap.get(selector).push(options);
|
|
487
|
+
else
|
|
488
|
+
this.listenerMap.set(selector, [options]);
|
|
489
|
+
this.checkSelector(selector, [options]);
|
|
445
490
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
491
|
+
/** Disables the observation of the child elements */
|
|
492
|
+
disable() {
|
|
493
|
+
if (!this.enabled)
|
|
494
|
+
return;
|
|
495
|
+
this.enabled = false;
|
|
496
|
+
this.observer.disconnect();
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Enables or reenables the observation of the child elements.
|
|
500
|
+
* @param immediatelyCheckSelectors Whether to immediately check if all previously registered selectors exist (default is true)
|
|
501
|
+
* @returns Returns true when the observation was enabled, false otherwise (e.g. when the base element wasn't found)
|
|
502
|
+
*/
|
|
503
|
+
enable(immediatelyCheckSelectors = true) {
|
|
504
|
+
const baseElement = typeof this.baseElement === "string" ? document.querySelector(this.baseElement) : this.baseElement;
|
|
505
|
+
if (this.enabled || !baseElement)
|
|
506
|
+
return false;
|
|
507
|
+
this.enabled = true;
|
|
508
|
+
this.observer.observe(baseElement, this.observerOptions);
|
|
509
|
+
if (immediatelyCheckSelectors)
|
|
510
|
+
this.checkAllSelectors();
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
/** Returns whether the observation of the child elements is currently enabled */
|
|
514
|
+
isEnabled() {
|
|
515
|
+
return this.enabled;
|
|
516
|
+
}
|
|
517
|
+
/** Removes all listeners that have been registered with {@linkcode addListener()} */
|
|
518
|
+
clearListeners() {
|
|
519
|
+
this.listenerMap.clear();
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Removes all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()}
|
|
523
|
+
* @returns Returns true when all listeners for the associated selector were found and removed, false otherwise
|
|
524
|
+
*/
|
|
525
|
+
removeAllListeners(selector) {
|
|
526
|
+
return this.listenerMap.delete(selector);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Removes a single listener for the given {@linkcode selector} and {@linkcode options} that has been registered with {@linkcode addListener()}
|
|
530
|
+
* @returns Returns true when the listener was found and removed, false otherwise
|
|
531
|
+
*/
|
|
532
|
+
removeListener(selector, options) {
|
|
533
|
+
const listeners = this.listenerMap.get(selector);
|
|
534
|
+
if (!listeners)
|
|
535
|
+
return false;
|
|
536
|
+
const index = listeners.indexOf(options);
|
|
537
|
+
if (index > -1) {
|
|
538
|
+
listeners.splice(index, 1);
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
/** Returns all listeners that have been registered with {@linkcode addListener()} */
|
|
544
|
+
getAllListeners() {
|
|
545
|
+
return this.listenerMap;
|
|
546
|
+
}
|
|
547
|
+
/** Returns all listeners for the given {@linkcode selector} that have been registered with {@linkcode addListener()} */
|
|
548
|
+
getListeners(selector) {
|
|
549
|
+
return this.listenerMap.get(selector);
|
|
550
|
+
}
|
|
551
|
+
};
|
|
460
552
|
|
|
461
553
|
// lib/translation.ts
|
|
462
554
|
var trans = {};
|
|
@@ -484,6 +576,7 @@ var UserUtils = (function (exports) {
|
|
|
484
576
|
};
|
|
485
577
|
|
|
486
578
|
exports.ConfigManager = ConfigManager;
|
|
579
|
+
exports.SelectorObserver = SelectorObserver;
|
|
487
580
|
exports.addGlobalStyle = addGlobalStyle;
|
|
488
581
|
exports.addParent = addParent;
|
|
489
582
|
exports.amplifyMedia = amplifyMedia;
|
|
@@ -491,24 +584,21 @@ var UserUtils = (function (exports) {
|
|
|
491
584
|
exports.clamp = clamp;
|
|
492
585
|
exports.debounce = debounce;
|
|
493
586
|
exports.fetchAdvanced = fetchAdvanced;
|
|
494
|
-
exports.getSelectorMap = getSelectorMap;
|
|
495
587
|
exports.getUnsafeWindow = getUnsafeWindow;
|
|
496
|
-
exports.initOnSelector = initOnSelector;
|
|
497
588
|
exports.insertAfter = insertAfter;
|
|
498
589
|
exports.insertValues = insertValues;
|
|
499
590
|
exports.interceptEvent = interceptEvent;
|
|
500
591
|
exports.interceptWindowEvent = interceptWindowEvent;
|
|
501
592
|
exports.isScrollable = isScrollable;
|
|
502
593
|
exports.mapRange = mapRange;
|
|
503
|
-
exports.onSelector = onSelector;
|
|
504
594
|
exports.openInNewTab = openInNewTab;
|
|
505
595
|
exports.pauseFor = pauseFor;
|
|
506
596
|
exports.preloadImages = preloadImages;
|
|
507
597
|
exports.randRange = randRange;
|
|
598
|
+
exports.randomId = randomId;
|
|
508
599
|
exports.randomItem = randomItem;
|
|
509
600
|
exports.randomItemIndex = randomItemIndex;
|
|
510
601
|
exports.randomizeArray = randomizeArray;
|
|
511
|
-
exports.removeOnSelector = removeOnSelector;
|
|
512
602
|
exports.takeRandomItem = takeRandomItem;
|
|
513
603
|
exports.tr = tr;
|
|
514
604
|
|