@sv443-network/userutils 9.0.4 → 9.2.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.
@@ -7,8 +7,8 @@
7
7
 
8
8
  // ==UserLibrary==
9
9
  // @name UserUtils
10
- // @description Lightweight library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and much more
11
- // @version 9.0.4
10
+ // @description General purpose DOM/GreaseMonkey library that allows you to register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and much more
11
+ // @version 9.2.0
12
12
  // @license MIT
13
13
  // @copyright Sv443 (https://github.com/Sv443)
14
14
 
@@ -117,13 +117,21 @@ var UserUtils = (function (exports) {
117
117
  } else
118
118
  return Math.floor(Math.random() * (max - min + 1)) + min;
119
119
  }
120
- function digitCount(num) {
120
+ function digitCount(num, withDecimals = true) {
121
121
  num = Number(!["string", "number"].includes(typeof num) ? String(num) : num);
122
122
  if (typeof num === "number" && isNaN(num))
123
123
  return NaN;
124
- return num === 0 ? 1 : Math.floor(
125
- Math.log10(Math.abs(Number(num))) + 1
126
- );
124
+ const [intPart, decPart] = num.toString().split(".");
125
+ const intDigits = intPart === "0" ? 1 : Math.floor(Math.log10(Math.abs(Number(intPart))) + 1);
126
+ const decDigits = withDecimals && decPart ? decPart.length : 0;
127
+ return intDigits + decDigits;
128
+ }
129
+ function roundFixed(num, fractionDigits) {
130
+ const scale = 10 ** fractionDigits;
131
+ return Math.round(num * scale) / scale;
132
+ }
133
+ function bitSetHas(bitSet, checkVal) {
134
+ return (bitSet & checkVal) === checkVal;
127
135
  }
128
136
 
129
137
  // lib/array.ts
@@ -148,7 +156,7 @@ var UserUtils = (function (exports) {
148
156
  if (array.length === 0)
149
157
  return retArray;
150
158
  for (let i = retArray.length - 1; i > 0; i--) {
151
- const j = Math.floor(randRange(0, 1e4) / 1e4 * (i + 1));
159
+ const j = Math.floor(Math.random() * (i + 1));
152
160
  [retArray[i], retArray[j]] = [retArray[j], retArray[i]];
153
161
  }
154
162
  return retArray;
@@ -191,10 +199,10 @@ var UserUtils = (function (exports) {
191
199
  else if (color.startsWith("rgb")) {
192
200
  const rgbValues = (_a = color.match(/\d+(\.\d+)?/g)) == null ? undefined : _a.map(Number);
193
201
  if (!rgbValues)
194
- throw new Error("Invalid RGB/RGBA color format");
202
+ throw new TypeError("Invalid RGB/RGBA color format");
195
203
  [r, g, b, a] = rgbValues;
196
204
  } else
197
- throw new Error("Unsupported color format");
205
+ throw new TypeError("Unsupported color format");
198
206
  [r, g, b] = darkenRgb(r, g, b, percent);
199
207
  if (isHexCol)
200
208
  return rgbToHex(r, g, b, a, color.startsWith("#"), upperCase);
@@ -203,10 +211,39 @@ var UserUtils = (function (exports) {
203
211
  else if (color.startsWith("rgb"))
204
212
  return `rgb(${r}, ${g}, ${b})`;
205
213
  else
206
- throw new Error("Unsupported color format");
214
+ throw new TypeError("Unsupported color format");
207
215
  }
208
216
 
217
+ // lib/errors.ts
218
+ var UUError = class extends Error {
219
+ constructor(message, options) {
220
+ super(message, options);
221
+ __publicField(this, "date");
222
+ this.date = /* @__PURE__ */ new Date();
223
+ }
224
+ };
225
+ var ChecksumMismatchError = class extends UUError {
226
+ constructor(message, options) {
227
+ super(message, options);
228
+ this.name = "ChecksumMismatchError";
229
+ }
230
+ };
231
+ var MigrationError = class extends UUError {
232
+ constructor(message, options) {
233
+ super(message, options);
234
+ this.name = "MigrationError";
235
+ }
236
+ };
237
+ var PlatformError = class extends UUError {
238
+ constructor(message, options) {
239
+ super(message, options);
240
+ this.name = "PlatformError";
241
+ }
242
+ };
243
+
209
244
  // lib/dom.ts
245
+ var domReady = false;
246
+ document.addEventListener("DOMContentLoaded", () => domReady = true);
210
247
  function getUnsafeWindow() {
211
248
  try {
212
249
  return unsafeWindow;
@@ -262,8 +299,8 @@ var UserUtils = (function (exports) {
262
299
  }
263
300
  function interceptEvent(eventObject, eventName, predicate = () => true) {
264
301
  var _a;
265
- if ((eventObject === window || eventObject === getUnsafeWindow()) && ((_a = GM == null ? undefined : GM.info) == null ? undefined : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey")
266
- throw new Error("Intercepting window events is not supported on FireMonkey due to the isolated context the userscript runs in.");
302
+ if (((_a = GM == null ? undefined : GM.info) == null ? undefined : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey" && (eventObject === window || eventObject === getUnsafeWindow()))
303
+ throw new PlatformError("Intercepting window events is not supported on FireMonkey due to the isolated context the userscript runs in.");
267
304
  Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
268
305
  if (isNaN(Error.stackTraceLimit))
269
306
  Error.stackTraceLimit = 100;
@@ -345,6 +382,36 @@ var UserUtils = (function (exports) {
345
382
  element.innerHTML = (_c = (_b = ttPolicy == null ? undefined : ttPolicy.createHTML) == null ? undefined : _b.call(ttPolicy, html)) != null ? _c : html;
346
383
  return element;
347
384
  }
385
+ function probeElementStyle(probeStyle, element, hideOffscreen = true, parentElement = document.body) {
386
+ const el = element ? typeof element === "function" ? element() : element : document.createElement("span");
387
+ if (hideOffscreen) {
388
+ el.style.position = "absolute";
389
+ el.style.left = "-9999px";
390
+ el.style.top = "-9999px";
391
+ el.style.zIndex = "-9999";
392
+ }
393
+ el.classList.add("_uu_probe_element");
394
+ parentElement.appendChild(el);
395
+ const style = window.getComputedStyle(el);
396
+ const result = probeStyle(style, el);
397
+ setTimeout(() => el.remove(), 1);
398
+ return result;
399
+ }
400
+ function isDomLoaded() {
401
+ return domReady;
402
+ }
403
+ function onDomLoad(cb) {
404
+ return new Promise((res) => {
405
+ if (domReady) {
406
+ cb == null ? undefined : cb();
407
+ res();
408
+ } else
409
+ document.addEventListener("DOMContentLoaded", () => {
410
+ cb == null ? undefined : cb();
411
+ res();
412
+ });
413
+ });
414
+ }
348
415
 
349
416
  // lib/crypto.ts
350
417
  function compress(input, compressionFormat, outputType = "string") {
@@ -392,6 +459,8 @@ var UserUtils = (function (exports) {
392
459
  });
393
460
  }
394
461
  function randomId(length = 16, radix = 16, enhancedEntropy = false, randomCase = true) {
462
+ if (radix < 2 || radix > 36)
463
+ throw new RangeError("The radix argument must be between 2 and 36");
395
464
  let arr = [];
396
465
  const caseArr = randomCase ? [0, 1] : [0];
397
466
  if (enhancedEntropy) {
@@ -565,8 +634,7 @@ var UserUtils = (function (exports) {
565
634
  lastFmtVer = oldFmtVer = ver;
566
635
  } catch (err) {
567
636
  if (!resetOnError)
568
- throw new Error(`Error while running migration function for format version '${fmtVer}'`);
569
- console.error(`Error while running migration function for format version '${fmtVer}' - resetting to the default value.`, err);
637
+ throw new MigrationError(`Error while running migration function for format version '${fmtVer}'`, { cause: err });
570
638
  yield this.saveDefaultData();
571
639
  return this.getData();
572
640
  }
@@ -627,7 +695,6 @@ var UserUtils = (function (exports) {
627
695
  return JSON.parse(decRes != null ? decRes : data);
628
696
  });
629
697
  }
630
- //#region misc
631
698
  /** Copies a JSON-compatible object and loses all its internal references in the process */
632
699
  deepCopy(obj) {
633
700
  return JSON.parse(JSON.stringify(obj));
@@ -679,7 +746,7 @@ var UserUtils = (function (exports) {
679
746
  };
680
747
 
681
748
  // lib/DataStoreSerializer.ts
682
- var DataStoreSerializer = class {
749
+ var DataStoreSerializer = class _DataStoreSerializer {
683
750
  constructor(stores, options = {}) {
684
751
  __publicField(this, "stores");
685
752
  __publicField(this, "options");
@@ -697,27 +764,25 @@ var UserUtils = (function (exports) {
697
764
  return computeHash(input, "SHA-256");
698
765
  });
699
766
  }
700
- /** Serializes a DataStore instance */
701
- serializeStore(storeInst) {
702
- return __async(this, null, function* () {
703
- const data = storeInst.encodingEnabled() ? yield storeInst.encodeData(JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
704
- const checksum = this.options.addChecksum ? yield this.calcChecksum(data) : undefined;
705
- return {
706
- id: storeInst.id,
707
- data,
708
- formatVersion: storeInst.formatVersion,
709
- encoded: storeInst.encodingEnabled(),
710
- checksum
711
- };
712
- });
713
- }
714
- /** Serializes the data stores into a string */
715
- serialize() {
767
+ /**
768
+ * Serializes the data stores into a string.
769
+ * @param useEncoding Whether to encode the data using each DataStore's `encodeData()` method
770
+ * @param stringified Whether to return the result as a string or as an array of `SerializedDataStore` objects
771
+ */
772
+ serialize(useEncoding = true, stringified = true) {
716
773
  return __async(this, null, function* () {
717
774
  const serData = [];
718
- for (const store of this.stores)
719
- serData.push(yield this.serializeStore(store));
720
- return JSON.stringify(serData);
775
+ for (const storeInst of this.stores) {
776
+ const data = useEncoding && storeInst.encodingEnabled() ? yield storeInst.encodeData(JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
777
+ serData.push({
778
+ id: storeInst.id,
779
+ data,
780
+ formatVersion: storeInst.formatVersion,
781
+ encoded: useEncoding && storeInst.encodingEnabled(),
782
+ checksum: this.options.addChecksum ? yield this.calcChecksum(data) : undefined
783
+ });
784
+ }
785
+ return stringified ? JSON.stringify(serData) : serData;
721
786
  });
722
787
  }
723
788
  /**
@@ -726,7 +791,9 @@ var UserUtils = (function (exports) {
726
791
  */
727
792
  deserialize(serializedData) {
728
793
  return __async(this, null, function* () {
729
- const deserStores = JSON.parse(serializedData);
794
+ const deserStores = typeof serializedData === "string" ? JSON.parse(serializedData) : serializedData;
795
+ if (!Array.isArray(deserStores) || !deserStores.every(_DataStoreSerializer.isSerializedDataStore))
796
+ throw new TypeError("Invalid serialized data format! Expected an array of SerializedDataStore objects.");
730
797
  for (const storeData of deserStores) {
731
798
  const storeInst = this.stores.find((s) => s.id === storeData.id);
732
799
  if (!storeInst)
@@ -734,7 +801,7 @@ var UserUtils = (function (exports) {
734
801
  if (this.options.ensureIntegrity && typeof storeData.checksum === "string") {
735
802
  const checksum = yield this.calcChecksum(storeData.data);
736
803
  if (checksum !== storeData.checksum)
737
- throw new Error(`Checksum mismatch for DataStore with ID "${storeData.id}"!
804
+ throw new ChecksumMismatchError(`Checksum mismatch for DataStore with ID "${storeData.id}"!
738
805
  Expected: ${storeData.checksum}
739
806
  Has: ${checksum}`);
740
807
  }
@@ -778,6 +845,10 @@ Has: ${checksum}`);
778
845
  return Promise.allSettled(this.stores.map((store) => store.deleteData()));
779
846
  });
780
847
  }
848
+ /** Checks if a given value is a SerializedDataStore object */
849
+ static isSerializedDataStore(obj) {
850
+ return typeof obj === "object" && obj !== null && "id" in obj && "data" in obj && "formatVersion" in obj && "encoded" in obj;
851
+ }
781
852
  };
782
853
 
783
854
  // node_modules/.pnpm/nanoevents@9.1.0/node_modules/nanoevents/index.js
@@ -937,7 +1008,7 @@ Has: ${checksum}`);
937
1008
  }, this.timeout);
938
1009
  break;
939
1010
  default:
940
- throw new Error(`Invalid debouncer type: ${this.type}`);
1011
+ throw new TypeError(`Invalid debouncer type: ${this.type}`);
941
1012
  }
942
1013
  }
943
1014
  };
@@ -1369,10 +1440,23 @@ Has: ${checksum}`);
1369
1440
  };
1370
1441
 
1371
1442
  // lib/misc.ts
1372
- function autoPlural(word, num) {
1373
- if (Array.isArray(num) || num instanceof NodeList)
1374
- num = num.length;
1375
- return `${word}${num === 1 ? "" : "s"}`;
1443
+ function autoPlural(term, num, pluralType = "auto") {
1444
+ let n = num;
1445
+ if (typeof n !== "number")
1446
+ n = getListLength(n, false);
1447
+ if (!["-s", "-ies"].includes(pluralType))
1448
+ pluralType = "auto";
1449
+ if (isNaN(n))
1450
+ n = 2;
1451
+ const pType = pluralType === "auto" ? String(term).endsWith("y") ? "-ies" : "-s" : pluralType;
1452
+ switch (pType) {
1453
+ case "-s":
1454
+ return `${term}${n === 1 ? "" : "s"}`;
1455
+ case "-ies":
1456
+ return `${String(term).slice(0, -1)}${n === 1 ? "y" : "ies"}`;
1457
+ default:
1458
+ return String(term);
1459
+ }
1376
1460
  }
1377
1461
  function insertValues(input, ...values) {
1378
1462
  return input.replace(/%\d/gm, (match) => {
@@ -1381,29 +1465,33 @@ Has: ${checksum}`);
1381
1465
  return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? undefined : _b.toString();
1382
1466
  });
1383
1467
  }
1384
- function pauseFor(time) {
1385
- return new Promise((res) => {
1386
- setTimeout(() => res(), time);
1468
+ function pauseFor(time, signal, rejectOnAbort = false) {
1469
+ return new Promise((res, rej) => {
1470
+ const timeout = setTimeout(() => res(), time);
1471
+ signal == null ? undefined : signal.addEventListener("abort", () => {
1472
+ clearTimeout(timeout);
1473
+ rejectOnAbort ? rej(new Error("The pause was aborted")) : res();
1474
+ });
1387
1475
  });
1388
1476
  }
1389
1477
  function fetchAdvanced(_0) {
1390
1478
  return __async(this, arguments, function* (input, options = {}) {
1391
- var _a;
1392
1479
  const { timeout = 1e4 } = options;
1393
- const { signal, abort } = new AbortController();
1394
- (_a = options.signal) == null ? undefined : _a.addEventListener("abort", abort);
1395
- let signalOpts = {}, id = undefined;
1480
+ const ctl = new AbortController();
1481
+ const _a = options, { signal } = _a, restOpts = __objRest(_a, ["signal"]);
1482
+ signal == null ? undefined : signal.addEventListener("abort", () => ctl.abort());
1483
+ let sigOpts = {}, id = undefined;
1396
1484
  if (timeout >= 0) {
1397
- id = setTimeout(() => abort(), timeout);
1398
- signalOpts = { signal };
1485
+ id = setTimeout(() => ctl.abort(), timeout);
1486
+ sigOpts = { signal: ctl.signal };
1399
1487
  }
1400
1488
  try {
1401
- const res = yield fetch(input, __spreadValues(__spreadValues({}, options), signalOpts));
1402
- id && clearTimeout(id);
1489
+ const res = yield fetch(input, __spreadValues(__spreadValues({}, restOpts), sigOpts));
1490
+ typeof id !== "undefined" && clearTimeout(id);
1403
1491
  return res;
1404
1492
  } catch (err) {
1405
- id && clearTimeout(id);
1406
- throw err;
1493
+ typeof id !== "undefined" && clearTimeout(id);
1494
+ throw new Error("Error while calling fetch", { cause: err });
1407
1495
  }
1408
1496
  });
1409
1497
  }
@@ -1419,10 +1507,11 @@ Has: ${checksum}`);
1419
1507
  );
1420
1508
  });
1421
1509
  }
1510
+ function getListLength(obj, zeroOnInvalid = true) {
1511
+ return "length" in obj ? obj.length : "size" in obj ? obj.size : "count" in obj ? obj.count : zeroOnInvalid ? 0 : NaN;
1512
+ }
1422
1513
 
1423
1514
  // lib/SelectorObserver.ts
1424
- var domLoaded = false;
1425
- document.addEventListener("DOMContentLoaded", () => domLoaded = true);
1426
1515
  var SelectorObserver = class {
1427
1516
  constructor(baseElement, options = {}) {
1428
1517
  __publicField(this, "enabled", false);
@@ -1463,7 +1552,7 @@ Has: ${checksum}`);
1463
1552
  }
1464
1553
  /** Call to check all selectors in the {@linkcode listenerMap} using {@linkcode checkSelector()} */
1465
1554
  checkAllSelectors() {
1466
- if (!this.enabled || !domLoaded)
1555
+ if (!this.enabled || !isDomLoaded())
1467
1556
  return;
1468
1557
  for (const [selector, listeners] of this.listenerMap.entries())
1469
1558
  this.checkSelector(selector, listeners);
@@ -1607,9 +1696,9 @@ Has: ${checksum}`);
1607
1696
  if (typeof language !== "string" || language.length === 0 || typeof trObj !== "object" || trObj === null)
1608
1697
  return fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
1609
1698
  const transformTrVal = (trKey, trValue) => {
1610
- const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(trValue));
1699
+ const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(String(trValue)));
1611
1700
  if (tfs.length === 0)
1612
- return trValue;
1701
+ return String(trValue);
1613
1702
  let retStr = String(trValue);
1614
1703
  for (const tf of tfs) {
1615
1704
  const re = new RegExp(tf.regex);
@@ -1758,15 +1847,20 @@ Has: ${checksum}`);
1758
1847
  }
1759
1848
  };
1760
1849
 
1850
+ exports.ChecksumMismatchError = ChecksumMismatchError;
1761
1851
  exports.DataStore = DataStore;
1762
1852
  exports.DataStoreSerializer = DataStoreSerializer;
1763
1853
  exports.Debouncer = Debouncer;
1764
1854
  exports.Dialog = Dialog;
1855
+ exports.MigrationError = MigrationError;
1765
1856
  exports.NanoEmitter = NanoEmitter;
1857
+ exports.PlatformError = PlatformError;
1766
1858
  exports.SelectorObserver = SelectorObserver;
1859
+ exports.UUError = UUError;
1767
1860
  exports.addGlobalStyle = addGlobalStyle;
1768
1861
  exports.addParent = addParent;
1769
1862
  exports.autoPlural = autoPlural;
1863
+ exports.bitSetHas = bitSetHas;
1770
1864
  exports.clamp = clamp;
1771
1865
  exports.compress = compress;
1772
1866
  exports.computeHash = computeHash;
@@ -1779,26 +1873,31 @@ Has: ${checksum}`);
1779
1873
  exports.defaultStrings = defaultStrings;
1780
1874
  exports.digitCount = digitCount;
1781
1875
  exports.fetchAdvanced = fetchAdvanced;
1876
+ exports.getListLength = getListLength;
1782
1877
  exports.getSiblingsFrame = getSiblingsFrame;
1783
1878
  exports.getUnsafeWindow = getUnsafeWindow;
1784
1879
  exports.hexToRgb = hexToRgb;
1785
1880
  exports.insertValues = insertValues;
1786
1881
  exports.interceptEvent = interceptEvent;
1787
1882
  exports.interceptWindowEvent = interceptWindowEvent;
1883
+ exports.isDomLoaded = isDomLoaded;
1788
1884
  exports.isScrollable = isScrollable;
1789
1885
  exports.lightenColor = lightenColor;
1790
1886
  exports.mapRange = mapRange;
1791
1887
  exports.observeElementProp = observeElementProp;
1888
+ exports.onDomLoad = onDomLoad;
1792
1889
  exports.openDialogs = openDialogs;
1793
1890
  exports.openInNewTab = openInNewTab;
1794
1891
  exports.pauseFor = pauseFor;
1795
1892
  exports.preloadImages = preloadImages;
1893
+ exports.probeElementStyle = probeElementStyle;
1796
1894
  exports.randRange = randRange;
1797
1895
  exports.randomId = randomId;
1798
1896
  exports.randomItem = randomItem;
1799
1897
  exports.randomItemIndex = randomItemIndex;
1800
1898
  exports.randomizeArray = randomizeArray;
1801
1899
  exports.rgbToHex = rgbToHex;
1900
+ exports.roundFixed = roundFixed;
1802
1901
  exports.setInnerHtmlUnsafe = setInnerHtmlUnsafe;
1803
1902
  exports.takeRandomItem = takeRandomItem;
1804
1903
  exports.tr = tr;