@sv443-network/userutils 8.4.0 → 9.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.js CHANGED
@@ -92,7 +92,7 @@ function randRange(...args) {
92
92
  crypto.getRandomValues(uintArr);
93
93
  return Number(Array.from(
94
94
  uintArr,
95
- (v) => Math.round(mapRange(v, 0, 255, min, max)).toString(10).substring(0, 1)
95
+ (v) => Math.round(mapRange(v, 0, 255, min, max)).toString(10)
96
96
  ).join(""));
97
97
  } else
98
98
  return Math.floor(Math.random() * (max - min + 1)) + min;
@@ -112,14 +112,14 @@ function randomItem(array) {
112
112
  }
113
113
  function randomItemIndex(array) {
114
114
  if (array.length === 0)
115
- return [void 0, void 0];
115
+ return [undefined, undefined];
116
116
  const idx = randRange(array.length - 1);
117
117
  return [array[idx], idx];
118
118
  }
119
119
  function takeRandomItem(arr) {
120
120
  const [itm, idx] = randomItemIndex(arr);
121
- if (idx === void 0)
122
- return void 0;
121
+ if (idx === undefined)
122
+ return undefined;
123
123
  arr.splice(idx, 1);
124
124
  return itm;
125
125
  }
@@ -137,7 +137,7 @@ function randomizeArray(array) {
137
137
  // lib/colors.ts
138
138
  function hexToRgb(hex) {
139
139
  hex = (hex.startsWith("#") ? hex.slice(1) : hex).trim();
140
- const a = hex.length === 8 || hex.length === 4 ? parseInt(hex.slice(-(hex.length / 4)), 16) / (hex.length === 8 ? 255 : 15) : void 0;
140
+ const a = hex.length === 8 || hex.length === 4 ? parseInt(hex.slice(-(hex.length / 4)), 16) / (hex.length === 8 ? 255 : 15) : undefined;
141
141
  if (!isNaN(Number(a)))
142
142
  hex = hex.slice(0, -(hex.length / 4));
143
143
  if (hex.length === 3 || hex.length === 4)
@@ -146,7 +146,7 @@ function hexToRgb(hex) {
146
146
  const r = bigint >> 16 & 255;
147
147
  const g = bigint >> 8 & 255;
148
148
  const b = bigint & 255;
149
- return [clamp(r, 0, 255), clamp(g, 0, 255), clamp(b, 0, 255), typeof a === "number" ? clamp(a, 0, 1) : void 0];
149
+ return [clamp(r, 0, 255), clamp(g, 0, 255), clamp(b, 0, 255), typeof a === "number" ? clamp(a, 0, 1) : undefined];
150
150
  }
151
151
  function rgbToHex(red, green, blue, alpha, withHash = true, upperCase = false) {
152
152
  const toHexVal = (n) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, "0")[upperCase ? "toUpperCase" : "toLowerCase"]();
@@ -169,7 +169,7 @@ function darkenColor(color, percent, upperCase = false) {
169
169
  if (isHexCol)
170
170
  [r, g, b, a] = hexToRgb(color);
171
171
  else if (color.startsWith("rgb")) {
172
- const rgbValues = (_a = color.match(/\d+(\.\d+)?/g)) == null ? void 0 : _a.map(Number);
172
+ const rgbValues = (_a = color.match(/\d+(\.\d+)?/g)) == null ? undefined : _a.map(Number);
173
173
  if (!rgbValues)
174
174
  throw new Error("Invalid RGB/RGBA color format");
175
175
  [r, g, b, a] = rgbValues;
@@ -217,26 +217,32 @@ function preloadImages(srcUrls, rejects = false) {
217
217
  }));
218
218
  return Promise.allSettled(promises);
219
219
  }
220
- function openInNewTab(href, background) {
220
+ function openInNewTab(href, background, additionalProps) {
221
+ var _a;
221
222
  try {
222
- GM.openInTab(href, background);
223
+ (_a = GM.openInTab) == null ? void 0 : _a.call(GM, href, background);
223
224
  } catch (e) {
224
225
  const openElem = document.createElement("a");
225
- Object.assign(openElem, {
226
+ Object.assign(openElem, __spreadValues({
226
227
  className: "userutils-open-in-new-tab",
227
228
  target: "_blank",
228
229
  rel: "noopener noreferrer",
230
+ tabIndex: -1,
231
+ ariaHidden: "true",
229
232
  href
233
+ }, additionalProps));
234
+ Object.assign(openElem.style, {
235
+ display: "none",
236
+ pointerEvents: "none"
230
237
  });
231
- openElem.style.display = "none";
232
238
  document.body.appendChild(openElem);
233
239
  openElem.click();
234
- setTimeout(openElem.remove, 50);
240
+ setTimeout(openElem.remove, 0);
235
241
  }
236
242
  }
237
243
  function interceptEvent(eventObject, eventName, predicate = () => true) {
238
244
  var _a;
239
- if ((eventObject === window || eventObject === getUnsafeWindow()) && ((_a = GM == null ? void 0 : GM.info) == null ? void 0 : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey")
245
+ if ((eventObject === window || eventObject === getUnsafeWindow()) && ((_a = GM == null ? undefined : GM.info) == null ? undefined : _a.scriptHandler) && GM.info.scriptHandler === "FireMonkey")
240
246
  throw new Error("Intercepting window events is not supported on FireMonkey due to the isolated context the userscript runs in.");
241
247
  Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
242
248
  if (isNaN(Error.stackTraceLimit))
@@ -244,7 +250,7 @@ function interceptEvent(eventObject, eventName, predicate = () => true) {
244
250
  (function(original) {
245
251
  eventObject.__proto__.addEventListener = function(...args) {
246
252
  var _a2, _b;
247
- const origListener = typeof args[1] === "function" ? args[1] : (_b = (_a2 = args[1]) == null ? void 0 : _a2.handleEvent) != null ? _b : () => void 0;
253
+ const origListener = typeof args[1] === "function" ? args[1] : (_b = (_a2 = args[1]) == null ? undefined : _a2.handleEvent) != null ? _b : () => undefined;
248
254
  args[1] = function(...a) {
249
255
  if (args[0] === eventName && predicate(Array.isArray(a) ? a[0] : a))
250
256
  return;
@@ -272,12 +278,12 @@ function observeElementProp(element, property, callback) {
272
278
  Object.defineProperty(element, property, {
273
279
  get: function() {
274
280
  var _a;
275
- return (_a = descriptor == null ? void 0 : descriptor.get) == null ? void 0 : _a.apply(this, arguments);
281
+ return (_a = descriptor == null ? undefined : descriptor.get) == null ? undefined : _a.apply(this, arguments);
276
282
  },
277
283
  set: function() {
278
284
  var _a;
279
285
  const oldValue = this[property];
280
- (_a = descriptor == null ? void 0 : descriptor.set) == null ? void 0 : _a.apply(this, arguments);
286
+ (_a = descriptor == null ? undefined : descriptor.set) == null ? undefined : _a.apply(this, arguments);
281
287
  const newValue = this[property];
282
288
  if (typeof callback === "function") {
283
289
  callback.bind(this, oldValue, newValue);
@@ -289,7 +295,7 @@ function observeElementProp(element, property, callback) {
289
295
  }
290
296
  function getSiblingsFrame(refElement, siblingAmount, refElementAlignment = "center-top", includeRef = true) {
291
297
  var _a, _b;
292
- const siblings = [...(_b = (_a = refElement.parentNode) == null ? void 0 : _a.childNodes) != null ? _b : []];
298
+ const siblings = [...(_b = (_a = refElement.parentNode) == null ? undefined : _a.childNodes) != null ? _b : []];
293
299
  const elemSiblIdx = siblings.indexOf(refElement);
294
300
  if (elemSiblIdx === -1)
295
301
  throw new Error("Element doesn't have a parent node");
@@ -311,12 +317,12 @@ function getSiblingsFrame(refElement, siblingAmount, refElementAlignment = "cent
311
317
  var ttPolicy;
312
318
  function setInnerHtmlUnsafe(element, html) {
313
319
  var _a, _b, _c;
314
- if (!ttPolicy && typeof ((_a = window == null ? void 0 : window.trustedTypes) == null ? void 0 : _a.createPolicy) === "function") {
320
+ if (!ttPolicy && typeof ((_a = window == null ? undefined : window.trustedTypes) == null ? undefined : _a.createPolicy) === "function") {
315
321
  ttPolicy = window.trustedTypes.createPolicy("_uu_set_innerhtml_unsafe", {
316
322
  createHTML: (unsafeHtml) => unsafeHtml
317
323
  });
318
324
  }
319
- element.innerHTML = (_c = (_b = ttPolicy == null ? void 0 : ttPolicy.createHTML) == null ? void 0 : _b.call(ttPolicy, html)) != null ? _c : html;
325
+ element.innerHTML = (_c = (_b = ttPolicy == null ? undefined : ttPolicy.createHTML) == null ? undefined : _b.call(ttPolicy, html)) != null ? _c : html;
320
326
  return element;
321
327
  }
322
328
 
@@ -392,8 +398,8 @@ var DataStore = class {
392
398
  * Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
393
399
  * Supports migrating data from older versions to newer ones and populating the cache with default data if no persistent data is found.
394
400
  *
395
- * ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue` if the storageMethod is left as the default of `"GM"`
396
- * ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultData`
401
+ * - ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue` if the storageMethod is left as the default of `"GM"`
402
+ * - ⚠️ Make sure to call {@linkcode loadData()} at least once after creating an instance, or the returned data will be the same as `options.defaultData`
397
403
  *
398
404
  * @template TData The type of the data that is saved in persistent storage for the currently set format version (will be automatically inferred from `defaultData` if not provided) - **This has to be a JSON-compatible object!** (no undefined, circular references, etc.)
399
405
  * @param options The options for this DataStore instance
@@ -500,7 +506,7 @@ var DataStore = class {
500
506
  * The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
501
507
  * Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
502
508
  *
503
- * ⚠️ This requires the additional directive `@grant GM.deleteValue` if the storageMethod is left as the default of `"GM"`
509
+ * - ⚠️ This requires the additional directive `@grant GM.deleteValue` if the storageMethod is left as the default of `"GM"`
504
510
  */
505
511
  deleteData() {
506
512
  return __async(this, null, function* () {
@@ -565,7 +571,7 @@ var DataStore = class {
565
571
  const data = yield this.getValue(`_uucfg-${id}`, JSON.stringify(this.defaultData));
566
572
  const fmtVer = Number(yield this.getValue(`_uucfgver-${id}`, NaN));
567
573
  const isEncoded = Boolean(yield this.getValue(`_uucfgenc-${id}`, false));
568
- if (data === void 0 || isNaN(fmtVer))
574
+ if (data === undefined || isNaN(fmtVer))
569
575
  return;
570
576
  const parsed = yield this.deserializeData(data, isEncoded);
571
577
  yield Promise.allSettled([
@@ -595,7 +601,7 @@ var DataStore = class {
595
601
  /** Deserializes the data using the optional this.decodeData() and returns it as a JSON object */
596
602
  deserializeData(data, useEncoding = true) {
597
603
  return __async(this, null, function* () {
598
- let decRes = this.encodingEnabled() && useEncoding ? this.decodeData(data) : void 0;
604
+ let decRes = this.encodingEnabled() && useEncoding ? this.decodeData(data) : undefined;
599
605
  if (decRes instanceof Promise)
600
606
  decRes = yield decRes;
601
607
  return JSON.parse(decRes != null ? decRes : data);
@@ -607,7 +613,7 @@ var DataStore = class {
607
613
  return JSON.parse(JSON.stringify(obj));
608
614
  }
609
615
  //#region storage
610
- /** Gets a value from persistent storage - can be overwritten in a subclass if you want to use something other than GM storage */
616
+ /** Gets a value from persistent storage - can be overwritten in a subclass if you want to use something other than the default storage methods */
611
617
  getValue(name, defaultValue) {
612
618
  return __async(this, null, function* () {
613
619
  var _a, _b;
@@ -622,7 +628,7 @@ var DataStore = class {
622
628
  });
623
629
  }
624
630
  /**
625
- * Sets a value in persistent storage - can be overwritten in a subclass if you want to use something other than GM storage.
631
+ * Sets a value in persistent storage - can be overwritten in a subclass if you want to use something other than the default storage methods.
626
632
  * The default storage engines will stringify all passed values like numbers or booleans, so be aware of that.
627
633
  */
628
634
  setValue(name, value) {
@@ -637,7 +643,7 @@ var DataStore = class {
637
643
  }
638
644
  });
639
645
  }
640
- /** Deletes a value from persistent storage - can be overwritten in a subclass if you want to use something other than GM storage */
646
+ /** Deletes a value from persistent storage - can be overwritten in a subclass if you want to use something other than the default storage methods */
641
647
  deleteValue(name) {
642
648
  return __async(this, null, function* () {
643
649
  switch (this.storageMethod) {
@@ -675,7 +681,7 @@ var DataStoreSerializer = class {
675
681
  serializeStore(storeInst) {
676
682
  return __async(this, null, function* () {
677
683
  const data = storeInst.encodingEnabled() ? yield storeInst.encodeData(JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
678
- const checksum = this.options.addChecksum ? yield this.calcChecksum(data) : void 0;
684
+ const checksum = this.options.addChecksum ? yield this.calcChecksum(data) : undefined;
679
685
  return {
680
686
  id: storeInst.id,
681
687
  data,
@@ -781,7 +787,7 @@ var NanoEmitter = class {
781
787
  let unsub;
782
788
  const onceProxy = (...args) => {
783
789
  unsub();
784
- cb == null ? void 0 : cb(...args);
790
+ cb == null ? undefined : cb(...args);
785
791
  resolve(args);
786
792
  };
787
793
  unsub = this.on(event, onceProxy);
@@ -803,6 +809,106 @@ var NanoEmitter = class {
803
809
  }
804
810
  };
805
811
 
812
+ // lib/Debouncer.ts
813
+ var Debouncer = class extends NanoEmitter {
814
+ /**
815
+ * Creates a new debouncer with the specified timeout and edge type.
816
+ * @param timeout Timeout in milliseconds between letting through calls - defaults to 200
817
+ * @param type The edge type to use for the debouncer - see {@linkcode DebouncerType} for details or [the documentation for an explanation and diagram](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#debouncer) - defaults to "immediate"
818
+ */
819
+ constructor(timeout = 200, type = "immediate") {
820
+ super();
821
+ this.timeout = timeout;
822
+ this.type = type;
823
+ /** All registered listener functions and the time they were attached */
824
+ __publicField(this, "listeners", []);
825
+ /** The currently active timeout */
826
+ __publicField(this, "activeTimeout");
827
+ /** The latest queued call */
828
+ __publicField(this, "queuedCall");
829
+ }
830
+ //#region listeners
831
+ /** Adds a listener function that will be called on timeout */
832
+ addListener(fn) {
833
+ this.listeners.push(fn);
834
+ }
835
+ /** Removes the listener with the specified function reference */
836
+ removeListener(fn) {
837
+ const idx = this.listeners.findIndex((l) => l === fn);
838
+ idx !== -1 && this.listeners.splice(idx, 1);
839
+ }
840
+ /** Removes all listeners */
841
+ removeAllListeners() {
842
+ this.listeners = [];
843
+ }
844
+ //#region timeout
845
+ /** Sets the timeout for the debouncer */
846
+ setTimeout(timeout) {
847
+ this.emit("change", this.timeout = timeout, this.type);
848
+ }
849
+ /** Returns the current timeout */
850
+ getTimeout() {
851
+ return this.timeout;
852
+ }
853
+ /** Whether the timeout is currently active, meaning any latest call to the {@linkcode call()} method will be queued */
854
+ isTimeoutActive() {
855
+ return typeof this.activeTimeout !== "undefined";
856
+ }
857
+ //#region type
858
+ /** Sets the edge type for the debouncer */
859
+ setType(type) {
860
+ this.emit("change", this.timeout, this.type = type);
861
+ }
862
+ /** Returns the current edge type */
863
+ getType() {
864
+ return this.type;
865
+ }
866
+ //#region call
867
+ /** Use this to call the debouncer with the specified arguments that will be passed to all listener functions registered with {@linkcode addListener()} */
868
+ call(...args) {
869
+ const cl = (...a) => {
870
+ this.queuedCall = undefined;
871
+ this.emit("call", ...a);
872
+ this.listeners.forEach((l) => l.apply(this, a));
873
+ };
874
+ const setRepeatTimeout = () => {
875
+ this.activeTimeout = setTimeout(() => {
876
+ if (this.queuedCall) {
877
+ this.queuedCall();
878
+ setRepeatTimeout();
879
+ } else
880
+ this.activeTimeout = undefined;
881
+ }, this.timeout);
882
+ };
883
+ switch (this.type) {
884
+ case "immediate":
885
+ if (typeof this.activeTimeout === "undefined") {
886
+ cl(...args);
887
+ setRepeatTimeout();
888
+ } else
889
+ this.queuedCall = () => cl(...args);
890
+ break;
891
+ case "idle":
892
+ if (this.activeTimeout)
893
+ clearTimeout(this.activeTimeout);
894
+ this.activeTimeout = setTimeout(() => {
895
+ cl(...args);
896
+ this.activeTimeout = undefined;
897
+ }, this.timeout);
898
+ break;
899
+ default:
900
+ throw new Error(`Invalid debouncer type: ${this.type}`);
901
+ }
902
+ }
903
+ };
904
+ function debounce(fn, timeout = 200, type = "immediate") {
905
+ const debouncer = new Debouncer(timeout, type);
906
+ debouncer.addListener(fn);
907
+ const func = (...args) => debouncer.call(...args);
908
+ func.debouncer = debouncer;
909
+ return func;
910
+ }
911
+
806
912
  // lib/Dialog.ts
807
913
  var defaultDialogCss = `.uu-no-select {
808
914
  user-select: none;
@@ -1020,7 +1126,7 @@ var Dialog = class _Dialog extends NanoEmitter {
1020
1126
  `#uu-style-dialog-${this.id}`
1021
1127
  ];
1022
1128
  for (const sel of clearSelectors)
1023
- (_a = document.querySelector(sel)) == null ? void 0 : _a.remove();
1129
+ (_a = document.querySelector(sel)) == null ? undefined : _a.remove();
1024
1130
  this.events.emit("clear");
1025
1131
  }
1026
1132
  /** Clears the DOM of the dialog and then renders it again */
@@ -1037,8 +1143,8 @@ var Dialog = class _Dialog extends NanoEmitter {
1037
1143
  open(e) {
1038
1144
  return __async(this, null, function* () {
1039
1145
  var _a;
1040
- e == null ? void 0 : e.preventDefault();
1041
- e == null ? void 0 : e.stopImmediatePropagation();
1146
+ e == null ? undefined : e.preventDefault();
1147
+ e == null ? undefined : e.stopImmediatePropagation();
1042
1148
  if (this.isOpen())
1043
1149
  return;
1044
1150
  this.dialogOpen = true;
@@ -1056,7 +1162,7 @@ var Dialog = class _Dialog extends NanoEmitter {
1056
1162
  openDialogs.unshift(this.id);
1057
1163
  for (const dialogId of openDialogs)
1058
1164
  if (dialogId !== this.id)
1059
- (_a = document.querySelector(`#uu-${dialogId}-dialog-bg`)) == null ? void 0 : _a.setAttribute("inert", "true");
1165
+ (_a = document.querySelector(`#uu-${dialogId}-dialog-bg`)) == null ? undefined : _a.setAttribute("inert", "true");
1060
1166
  document.body.classList.remove("uu-no-select");
1061
1167
  document.body.setAttribute("inert", "true");
1062
1168
  this.events.emit("open");
@@ -1066,8 +1172,8 @@ var Dialog = class _Dialog extends NanoEmitter {
1066
1172
  /** Closes the dialog - prevents default action and immediate propagation of the passed event */
1067
1173
  close(e) {
1068
1174
  var _a, _b;
1069
- e == null ? void 0 : e.preventDefault();
1070
- e == null ? void 0 : e.stopImmediatePropagation();
1175
+ e == null ? undefined : e.preventDefault();
1176
+ e == null ? undefined : e.stopImmediatePropagation();
1071
1177
  if (!this.isOpen())
1072
1178
  return;
1073
1179
  this.dialogOpen = false;
@@ -1080,7 +1186,7 @@ var Dialog = class _Dialog extends NanoEmitter {
1080
1186
  openDialogs.splice(openDialogs.indexOf(this.id), 1);
1081
1187
  currentDialogId = (_a = openDialogs[0]) != null ? _a : null;
1082
1188
  if (currentDialogId)
1083
- (_b = document.querySelector(`#uu-${currentDialogId}-dialog-bg`)) == null ? void 0 : _b.removeAttribute("inert");
1189
+ (_b = document.querySelector(`#uu-${currentDialogId}-dialog-bg`)) == null ? undefined : _b.removeAttribute("inert");
1084
1190
  if (openDialogs.length === 0) {
1085
1191
  document.body.classList.add("uu-no-select");
1086
1192
  document.body.removeAttribute("inert");
@@ -1124,7 +1230,7 @@ var Dialog = class _Dialog extends NanoEmitter {
1124
1230
  if (this.options.closeOnBgClick) {
1125
1231
  bgElem.addEventListener("click", (e) => {
1126
1232
  var _a;
1127
- if (this.isOpen() && ((_a = e.target) == null ? void 0 : _a.id) === `uu-${this.id}-dialog-bg`)
1233
+ if (this.isOpen() && ((_a = e.target) == null ? undefined : _a.id) === `uu-${this.id}-dialog-bg`)
1128
1234
  this.close(e);
1129
1235
  });
1130
1236
  }
@@ -1154,8 +1260,8 @@ var Dialog = class _Dialog extends NanoEmitter {
1154
1260
  preventDefault && e.preventDefault();
1155
1261
  stopPropagation && e.stopPropagation();
1156
1262
  }
1157
- (listenerOpts == null ? void 0 : listenerOpts.once) && e.type === "keydown" && elem.removeEventListener("click", proxListener, listenerOpts);
1158
- (listenerOpts == null ? void 0 : listenerOpts.once) && e.type === "click" && elem.removeEventListener("keydown", proxListener, listenerOpts);
1263
+ (listenerOpts == null ? undefined : listenerOpts.once) && e.type === "keydown" && elem.removeEventListener("click", proxListener, listenerOpts);
1264
+ (listenerOpts == null ? undefined : listenerOpts.once) && e.type === "click" && elem.removeEventListener("keydown", proxListener, listenerOpts);
1159
1265
  listener(e);
1160
1266
  };
1161
1267
  elem.addEventListener("click", proxListener, listenerOpts);
@@ -1165,8 +1271,8 @@ var Dialog = class _Dialog extends NanoEmitter {
1165
1271
  getDialogContent() {
1166
1272
  return __async(this, null, function* () {
1167
1273
  var _a, _b, _c, _d;
1168
- const header = (_b = (_a = this.options).renderHeader) == null ? void 0 : _b.call(_a);
1169
- const footer = (_d = (_c = this.options).renderFooter) == null ? void 0 : _d.call(_c);
1274
+ const header = (_b = (_a = this.options).renderHeader) == null ? undefined : _b.call(_a);
1275
+ const footer = (_d = (_c = this.options).renderFooter) == null ? undefined : _d.call(_c);
1170
1276
  const dialogWrapperEl = document.createElement("div");
1171
1277
  dialogWrapperEl.id = `uu-${this.id}-dialog`;
1172
1278
  dialogWrapperEl.classList.add("uu-dialog");
@@ -1232,7 +1338,7 @@ function insertValues(input, ...values) {
1232
1338
  return input.replace(/%\d/gm, (match) => {
1233
1339
  var _a, _b;
1234
1340
  const argIndex = Number(match.substring(1)) - 1;
1235
- return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? void 0 : _b.toString();
1341
+ return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? undefined : _b.toString();
1236
1342
  });
1237
1343
  }
1238
1344
  function pauseFor(time) {
@@ -1240,27 +1346,13 @@ function pauseFor(time) {
1240
1346
  setTimeout(() => res(), time);
1241
1347
  });
1242
1348
  }
1243
- function debounce(func, timeout = 300, edge = "falling") {
1244
- let id;
1245
- return function(...args) {
1246
- if (edge === "rising") {
1247
- if (!id) {
1248
- func.apply(this, args);
1249
- id = setTimeout(() => id = void 0, timeout);
1250
- }
1251
- } else {
1252
- clearTimeout(id);
1253
- id = setTimeout(() => func.apply(this, args), timeout);
1254
- }
1255
- };
1256
- }
1257
1349
  function fetchAdvanced(_0) {
1258
1350
  return __async(this, arguments, function* (input, options = {}) {
1259
1351
  var _a;
1260
1352
  const { timeout = 1e4 } = options;
1261
1353
  const { signal, abort } = new AbortController();
1262
- (_a = options.signal) == null ? void 0 : _a.addEventListener("abort", abort);
1263
- let signalOpts = {}, id = void 0;
1354
+ (_a = options.signal) == null ? undefined : _a.addEventListener("abort", abort);
1355
+ let signalOpts = {}, id = undefined;
1264
1356
  if (timeout >= 0) {
1265
1357
  id = setTimeout(() => abort(), timeout);
1266
1358
  signalOpts = { signal };
@@ -1303,12 +1395,12 @@ var SelectorObserver = class {
1303
1395
  this.listenerMap = /* @__PURE__ */ new Map();
1304
1396
  const _a = options, {
1305
1397
  defaultDebounce,
1306
- defaultDebounceEdge,
1398
+ defaultDebounceType,
1307
1399
  disableOnNoListeners,
1308
1400
  enableOnAddListener
1309
1401
  } = _a, observerOptions = __objRest(_a, [
1310
1402
  "defaultDebounce",
1311
- "defaultDebounceEdge",
1403
+ "defaultDebounceType",
1312
1404
  "disableOnNoListeners",
1313
1405
  "enableOnAddListener"
1314
1406
  ]);
@@ -1318,7 +1410,7 @@ var SelectorObserver = class {
1318
1410
  }, observerOptions);
1319
1411
  this.customOptions = {
1320
1412
  defaultDebounce: defaultDebounce != null ? defaultDebounce : 0,
1321
- defaultDebounceEdge: defaultDebounceEdge != null ? defaultDebounceEdge : "rising",
1413
+ defaultDebounceType: defaultDebounceType != null ? defaultDebounceType : "immediate",
1322
1414
  disableOnNoListeners: disableOnNoListeners != null ? disableOnNoListeners : false,
1323
1415
  enableOnAddListener: enableOnAddListener != null ? enableOnAddListener : true
1324
1416
  };
@@ -1362,7 +1454,7 @@ var SelectorObserver = class {
1362
1454
  this.removeListener(selector, options);
1363
1455
  }
1364
1456
  }
1365
- if (((_a = this.listenerMap.get(selector)) == null ? void 0 : _a.length) === 0)
1457
+ if (((_a = this.listenerMap.get(selector)) == null ? undefined : _a.length) === 0)
1366
1458
  this.listenerMap.delete(selector);
1367
1459
  if (this.listenerMap.size === 0 && this.customOptions.disableOnNoListeners)
1368
1460
  this.disable();
@@ -1388,7 +1480,7 @@ var SelectorObserver = class {
1388
1480
  options.listener = debounce(
1389
1481
  options.listener,
1390
1482
  options.debounce || this.customOptions.defaultDebounce,
1391
- options.debounceEdge || this.customOptions.defaultDebounceEdge
1483
+ options.debounceType || this.customOptions.defaultDebounceType
1392
1484
  );
1393
1485
  }
1394
1486
  if (this.listenerMap.has(selector))
@@ -1406,7 +1498,7 @@ var SelectorObserver = class {
1406
1498
  if (!this.enabled)
1407
1499
  return;
1408
1500
  this.enabled = false;
1409
- (_a = this.observer) == null ? void 0 : _a.disconnect();
1501
+ (_a = this.observer) == null ? undefined : _a.disconnect();
1410
1502
  }
1411
1503
  /**
1412
1504
  * Enables or reenables the observation of the child elements.
@@ -1419,7 +1511,7 @@ var SelectorObserver = class {
1419
1511
  if (this.enabled || !baseElement)
1420
1512
  return false;
1421
1513
  this.enabled = true;
1422
- (_a = this.observer) == null ? void 0 : _a.observe(baseElement, this.observerOptions);
1514
+ (_a = this.observer) == null ? undefined : _a.observe(baseElement, this.observerOptions);
1423
1515
  if (immediatelyCheckSelectors)
1424
1516
  this.checkAllSelectors();
1425
1517
  return true;
@@ -1466,39 +1558,164 @@ var SelectorObserver = class {
1466
1558
 
1467
1559
  // lib/translation.ts
1468
1560
  var trans = {};
1469
- var curLang = "";
1470
- function translate(language, key, ...args) {
1471
- var _a;
1472
- const trObj = (_a = trans[language]) == null ? void 0 : _a.data;
1473
- if (typeof language !== "string" || typeof trObj !== "object" || trObj === null)
1474
- return key;
1561
+ var valTransforms = [];
1562
+ var fallbackLang;
1563
+ function translate(language, key, ...trArgs) {
1564
+ if (typeof language !== "string")
1565
+ language = fallbackLang != null ? fallbackLang : "";
1566
+ const trObj = trans[language];
1567
+ if (typeof language !== "string" || language.length === 0 || typeof trObj !== "object" || trObj === null)
1568
+ return fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
1569
+ const transformTrVal = (trKey, trValue) => {
1570
+ const tfs = valTransforms.filter(({ regex }) => new RegExp(regex).test(trValue));
1571
+ if (tfs.length === 0)
1572
+ return trValue;
1573
+ let retStr = String(trValue);
1574
+ for (const tf of tfs) {
1575
+ const re = new RegExp(tf.regex);
1576
+ const matches = [];
1577
+ let execRes;
1578
+ while ((execRes = re.exec(trValue)) !== null) {
1579
+ if (matches.some((m) => m[0] === (execRes == null ? undefined : execRes[0])))
1580
+ break;
1581
+ matches.push(execRes);
1582
+ }
1583
+ retStr = String(tf.fn({
1584
+ language,
1585
+ trValue,
1586
+ currentValue: retStr,
1587
+ matches,
1588
+ trKey,
1589
+ trArgs
1590
+ }));
1591
+ }
1592
+ return retStr;
1593
+ };
1475
1594
  const keyParts = key.split(".");
1476
1595
  let value = trObj;
1477
1596
  for (const part of keyParts) {
1478
- if (typeof value !== "object" || value === null)
1597
+ if (typeof value !== "object" || value === null) {
1598
+ value = undefined;
1479
1599
  break;
1480
- value = value == null ? void 0 : value[part];
1600
+ }
1601
+ value = value == null ? undefined : value[part];
1481
1602
  }
1482
1603
  if (typeof value === "string")
1483
- return insertValues(value, args);
1484
- value = trObj == null ? void 0 : trObj[key];
1604
+ return transformTrVal(key, value);
1605
+ value = trObj == null ? undefined : trObj[key];
1485
1606
  if (typeof value === "string")
1486
- return insertValues(value, args);
1487
- return key;
1607
+ return transformTrVal(key, value);
1608
+ return fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
1488
1609
  }
1489
- var tr = (key, ...args) => translate(curLang, key, ...args);
1490
- tr.forLang = translate;
1491
- tr.addLanguage = (language, translations) => {
1492
- trans[language] = translations;
1493
- };
1494
- tr.setLanguage = (language) => {
1495
- curLang = language;
1496
- };
1497
- tr.getLanguage = () => {
1498
- return curLang;
1610
+ function trFor(language, key, ...args) {
1611
+ const txt = translate(language, key, ...args);
1612
+ if (txt === key)
1613
+ return fallbackLang ? translate(fallbackLang, key, ...args) : key;
1614
+ return txt;
1615
+ }
1616
+ function useTr(language) {
1617
+ return (key, ...args) => translate(language, key, ...args);
1618
+ }
1619
+ function hasKey(language = fallbackLang != null ? fallbackLang : "", key) {
1620
+ return tr.for(language, key) !== key;
1621
+ }
1622
+ function addTranslations(language, translations) {
1623
+ trans[language] = JSON.parse(JSON.stringify(translations));
1624
+ }
1625
+ function getTranslations(language = fallbackLang != null ? fallbackLang : "") {
1626
+ return trans[language];
1627
+ }
1628
+ var deleteTranslations = (language) => {
1629
+ if (language in trans) {
1630
+ delete trans[language];
1631
+ return true;
1632
+ }
1633
+ return false;
1499
1634
  };
1500
- tr.getTranslations = (language) => {
1501
- return trans[language != null ? language : curLang];
1635
+ function setFallbackLanguage(fallbackLanguage) {
1636
+ fallbackLang = fallbackLanguage;
1637
+ }
1638
+ function getFallbackLanguage() {
1639
+ return fallbackLang;
1640
+ }
1641
+ function addTransform(transform) {
1642
+ const [pattern, fn] = transform;
1643
+ valTransforms.push({
1644
+ fn,
1645
+ regex: typeof pattern === "string" ? new RegExp(pattern, "gm") : pattern
1646
+ });
1647
+ }
1648
+ function deleteTransform(patternOrFn) {
1649
+ const idx = valTransforms.findIndex(
1650
+ (t) => typeof patternOrFn === "function" ? t.fn === patternOrFn : typeof patternOrFn === "string" ? t.regex.source === patternOrFn : t.regex === patternOrFn
1651
+ );
1652
+ if (idx !== -1) {
1653
+ valTransforms.splice(idx, 1);
1654
+ return true;
1655
+ }
1656
+ return false;
1657
+ }
1658
+ var templateLiteralTransform = [
1659
+ /\$\{([a-zA-Z0-9$_-]+)\}/gm,
1660
+ ({ matches, trArgs, trValue }) => {
1661
+ const patternStart = "${", patternEnd = "}", patternRegex = /\$\{.+\}/m;
1662
+ let str = String(trValue);
1663
+ const eachKeyInTrString = (keys) => keys.every((key) => trValue.includes(`${patternStart}${key}${patternEnd}`));
1664
+ const namedMapping = () => {
1665
+ var _a;
1666
+ if (!str.includes(patternStart) || typeof trArgs[0] === "undefined" || typeof trArgs[0] !== "object" || !eachKeyInTrString(Object.keys((_a = trArgs[0]) != null ? _a : {})))
1667
+ return;
1668
+ for (const match of matches) {
1669
+ const repl = match[1] !== undefined ? trArgs[0][match[1]] : undefined;
1670
+ if (typeof repl !== "undefined")
1671
+ str = str.replace(match[0], String(repl));
1672
+ }
1673
+ };
1674
+ const positionalMapping = () => {
1675
+ if (!patternRegex.test(str) || !trArgs[0])
1676
+ return;
1677
+ let matchNum = -1;
1678
+ for (const match of matches) {
1679
+ matchNum++;
1680
+ if (typeof trArgs[matchNum] !== "undefined")
1681
+ str = str.replace(match[0], String(trArgs[matchNum]));
1682
+ }
1683
+ };
1684
+ const isArgsObject = trArgs[0] && typeof trArgs[0] === "object" && trArgs[0] !== null && String(trArgs[0]).startsWith("[object");
1685
+ if (isArgsObject && eachKeyInTrString(Object.keys(trArgs[0])))
1686
+ namedMapping();
1687
+ else
1688
+ positionalMapping();
1689
+ return str;
1690
+ }
1691
+ ];
1692
+ var percentTransform = [
1693
+ /\$\{([a-zA-Z0-9$_-]+)\}/gm,
1694
+ ({ matches, trArgs, trValue }) => {
1695
+ let str = String(trValue);
1696
+ for (const match of matches) {
1697
+ const repl = match[1] !== undefined ? trArgs[0][match[1]] : undefined;
1698
+ if (typeof repl !== "undefined")
1699
+ str = str.replace(match[0], String(repl));
1700
+ }
1701
+ return str;
1702
+ }
1703
+ ];
1704
+ var tr = {
1705
+ for: (...params) => trFor(...params),
1706
+ use: (...params) => useTr(...params),
1707
+ hasKey: (language = fallbackLang != null ? fallbackLang : "", key) => hasKey(language, key),
1708
+ addTranslations,
1709
+ getTranslations,
1710
+ deleteTranslations,
1711
+ setFallbackLanguage,
1712
+ getFallbackLanguage,
1713
+ addTransform,
1714
+ deleteTransform,
1715
+ transforms: {
1716
+ templateLiteral: templateLiteralTransform,
1717
+ percent: percentTransform
1718
+ }
1502
1719
  };
1503
1720
 
1504
- export { DataStore, DataStoreSerializer, Dialog, NanoEmitter, SelectorObserver, addGlobalStyle, addParent, autoPlural, clamp, compress, computeHash, consumeGen, consumeStringGen, currentDialogId, darkenColor, debounce, decompress, defaultDialogCss, defaultStrings, digitCount, fetchAdvanced, getSiblingsFrame, getUnsafeWindow, hexToRgb, insertValues, interceptEvent, interceptWindowEvent, isScrollable, lightenColor, mapRange, observeElementProp, openDialogs, openInNewTab, pauseFor, preloadImages, randRange, randomId, randomItem, randomItemIndex, randomizeArray, rgbToHex, setInnerHtmlUnsafe, takeRandomItem, tr };
1721
+ export { DataStore, DataStoreSerializer, Debouncer, Dialog, NanoEmitter, SelectorObserver, addGlobalStyle, addParent, autoPlural, clamp, compress, computeHash, consumeGen, consumeStringGen, currentDialogId, darkenColor, debounce, decompress, defaultDialogCss, defaultStrings, digitCount, fetchAdvanced, getSiblingsFrame, getUnsafeWindow, hexToRgb, insertValues, interceptEvent, interceptWindowEvent, isScrollable, lightenColor, mapRange, observeElementProp, openDialogs, openInNewTab, pauseFor, preloadImages, randRange, randomId, randomItem, randomItemIndex, randomizeArray, rgbToHex, setInnerHtmlUnsafe, takeRandomItem, tr };