@sv443-network/userutils 8.3.3 → 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
@@ -28,10 +28,7 @@ var __objRest = (source, exclude) => {
28
28
  }
29
29
  return target;
30
30
  };
31
- var __publicField = (obj, key, value) => {
32
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
33
- return value;
34
- };
31
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
35
32
  var __async = (__this, __arguments, generator) => {
36
33
  return new Promise((resolve, reject) => {
37
34
  var fulfilled = (value) => {
@@ -55,14 +52,17 @@ var __async = (__this, __arguments, generator) => {
55
52
 
56
53
  // lib/math.ts
57
54
  function clamp(value, min, max) {
55
+ if (typeof max !== "number") {
56
+ max = min;
57
+ min = 0;
58
+ }
58
59
  return Math.max(Math.min(value, max), min);
59
60
  }
60
61
  function mapRange(value, range1min, range1max, range2min, range2max) {
61
- if (typeof range2min === "undefined" || range2max === void 0) {
62
+ if (typeof range2min === "undefined" || typeof range2max === "undefined") {
62
63
  range2max = range1max;
63
- range2min = 0;
64
64
  range1max = range1min;
65
- range1min = 0;
65
+ range2min = range1min = 0;
66
66
  }
67
67
  if (Number(range1min) === 0 && Number(range2min) === 0)
68
68
  return value * (range2max / range1max);
@@ -76,7 +76,7 @@ function randRange(...args) {
76
76
  min = 0;
77
77
  [max] = args;
78
78
  } else
79
- throw new TypeError(`Wrong parameter(s) provided - expected: "number" and "number|undefined", got: "${typeof args[0]}" and "${typeof args[1]}"`);
79
+ throw new TypeError(`Wrong parameter(s) provided - expected (number, boolean|undefined) or (number, number, boolean|undefined) but got (${args.map((a) => typeof a).join(", ")}) instead`);
80
80
  if (typeof args[2] === "boolean")
81
81
  enhancedEntropy = args[2];
82
82
  else if (typeof args[1] === "boolean")
@@ -92,11 +92,19 @@ 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;
99
99
  }
100
+ function digitCount(num) {
101
+ num = Number(!["string", "number"].includes(typeof num) ? String(num) : num);
102
+ if (typeof num === "number" && isNaN(num))
103
+ return NaN;
104
+ return num === 0 ? 1 : Math.floor(
105
+ Math.log10(Math.abs(Number(num))) + 1
106
+ );
107
+ }
100
108
 
101
109
  // lib/array.ts
102
110
  function randomItem(array) {
@@ -104,14 +112,14 @@ function randomItem(array) {
104
112
  }
105
113
  function randomItemIndex(array) {
106
114
  if (array.length === 0)
107
- return [void 0, void 0];
115
+ return [undefined, undefined];
108
116
  const idx = randRange(array.length - 1);
109
117
  return [array[idx], idx];
110
118
  }
111
119
  function takeRandomItem(arr) {
112
120
  const [itm, idx] = randomItemIndex(arr);
113
- if (idx === void 0)
114
- return void 0;
121
+ if (idx === undefined)
122
+ return undefined;
115
123
  arr.splice(idx, 1);
116
124
  return itm;
117
125
  }
@@ -129,7 +137,7 @@ function randomizeArray(array) {
129
137
  // lib/colors.ts
130
138
  function hexToRgb(hex) {
131
139
  hex = (hex.startsWith("#") ? hex.slice(1) : hex).trim();
132
- 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;
133
141
  if (!isNaN(Number(a)))
134
142
  hex = hex.slice(0, -(hex.length / 4));
135
143
  if (hex.length === 3 || hex.length === 4)
@@ -138,7 +146,7 @@ function hexToRgb(hex) {
138
146
  const r = bigint >> 16 & 255;
139
147
  const g = bigint >> 8 & 255;
140
148
  const b = bigint & 255;
141
- 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];
142
150
  }
143
151
  function rgbToHex(red, green, blue, alpha, withHash = true, upperCase = false) {
144
152
  const toHexVal = (n) => clamp(Math.round(n), 0, 255).toString(16).padStart(2, "0")[upperCase ? "toUpperCase" : "toLowerCase"]();
@@ -161,7 +169,7 @@ function darkenColor(color, percent, upperCase = false) {
161
169
  if (isHexCol)
162
170
  [r, g, b, a] = hexToRgb(color);
163
171
  else if (color.startsWith("rgb")) {
164
- 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);
165
173
  if (!rgbValues)
166
174
  throw new Error("Invalid RGB/RGBA color format");
167
175
  [r, g, b, a] = rgbValues;
@@ -209,26 +217,32 @@ function preloadImages(srcUrls, rejects = false) {
209
217
  }));
210
218
  return Promise.allSettled(promises);
211
219
  }
212
- function openInNewTab(href, background) {
220
+ function openInNewTab(href, background, additionalProps) {
221
+ var _a;
213
222
  try {
214
- GM.openInTab(href, background);
223
+ (_a = GM.openInTab) == null ? void 0 : _a.call(GM, href, background);
215
224
  } catch (e) {
216
225
  const openElem = document.createElement("a");
217
- Object.assign(openElem, {
226
+ Object.assign(openElem, __spreadValues({
218
227
  className: "userutils-open-in-new-tab",
219
228
  target: "_blank",
220
229
  rel: "noopener noreferrer",
230
+ tabIndex: -1,
231
+ ariaHidden: "true",
221
232
  href
233
+ }, additionalProps));
234
+ Object.assign(openElem.style, {
235
+ display: "none",
236
+ pointerEvents: "none"
222
237
  });
223
- openElem.style.display = "none";
224
238
  document.body.appendChild(openElem);
225
239
  openElem.click();
226
- setTimeout(openElem.remove, 50);
240
+ setTimeout(openElem.remove, 0);
227
241
  }
228
242
  }
229
243
  function interceptEvent(eventObject, eventName, predicate = () => true) {
230
244
  var _a;
231
- 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")
232
246
  throw new Error("Intercepting window events is not supported on FireMonkey due to the isolated context the userscript runs in.");
233
247
  Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
234
248
  if (isNaN(Error.stackTraceLimit))
@@ -236,7 +250,7 @@ function interceptEvent(eventObject, eventName, predicate = () => true) {
236
250
  (function(original) {
237
251
  eventObject.__proto__.addEventListener = function(...args) {
238
252
  var _a2, _b;
239
- 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;
240
254
  args[1] = function(...a) {
241
255
  if (args[0] === eventName && predicate(Array.isArray(a) ? a[0] : a))
242
256
  return;
@@ -264,12 +278,12 @@ function observeElementProp(element, property, callback) {
264
278
  Object.defineProperty(element, property, {
265
279
  get: function() {
266
280
  var _a;
267
- 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);
268
282
  },
269
283
  set: function() {
270
284
  var _a;
271
285
  const oldValue = this[property];
272
- (_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);
273
287
  const newValue = this[property];
274
288
  if (typeof callback === "function") {
275
289
  callback.bind(this, oldValue, newValue);
@@ -281,7 +295,7 @@ function observeElementProp(element, property, callback) {
281
295
  }
282
296
  function getSiblingsFrame(refElement, siblingAmount, refElementAlignment = "center-top", includeRef = true) {
283
297
  var _a, _b;
284
- 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 : []];
285
299
  const elemSiblIdx = siblings.indexOf(refElement);
286
300
  if (elemSiblIdx === -1)
287
301
  throw new Error("Element doesn't have a parent node");
@@ -303,12 +317,12 @@ function getSiblingsFrame(refElement, siblingAmount, refElementAlignment = "cent
303
317
  var ttPolicy;
304
318
  function setInnerHtmlUnsafe(element, html) {
305
319
  var _a, _b, _c;
306
- 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") {
307
321
  ttPolicy = window.trustedTypes.createPolicy("_uu_set_innerhtml_unsafe", {
308
322
  createHTML: (unsafeHtml) => unsafeHtml
309
323
  });
310
324
  }
311
- 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;
312
326
  return element;
313
327
  }
314
328
 
@@ -384,8 +398,8 @@ var DataStore = class {
384
398
  * Creates an instance of DataStore to manage a sync & async database that is cached in memory and persistently saved across sessions.
385
399
  * Supports migrating data from older versions to newer ones and populating the cache with default data if no persistent data is found.
386
400
  *
387
- * ⚠️ Requires the directives `@grant GM.getValue` and `@grant GM.setValue` if the storageMethod is left as the default of `"GM"`
388
- * ⚠️ 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`
389
403
  *
390
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.)
391
405
  * @param options The options for this DataStore instance
@@ -492,7 +506,7 @@ var DataStore = class {
492
506
  * The in-memory cache will be left untouched, so you may still access the data with {@linkcode getData()}
493
507
  * Calling {@linkcode loadData()} or {@linkcode setData()} after this method was called will recreate persistent storage with the cached or default data.
494
508
  *
495
- * ⚠️ 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"`
496
510
  */
497
511
  deleteData() {
498
512
  return __async(this, null, function* () {
@@ -557,7 +571,7 @@ var DataStore = class {
557
571
  const data = yield this.getValue(`_uucfg-${id}`, JSON.stringify(this.defaultData));
558
572
  const fmtVer = Number(yield this.getValue(`_uucfgver-${id}`, NaN));
559
573
  const isEncoded = Boolean(yield this.getValue(`_uucfgenc-${id}`, false));
560
- if (data === void 0 || isNaN(fmtVer))
574
+ if (data === undefined || isNaN(fmtVer))
561
575
  return;
562
576
  const parsed = yield this.deserializeData(data, isEncoded);
563
577
  yield Promise.allSettled([
@@ -587,7 +601,7 @@ var DataStore = class {
587
601
  /** Deserializes the data using the optional this.decodeData() and returns it as a JSON object */
588
602
  deserializeData(data, useEncoding = true) {
589
603
  return __async(this, null, function* () {
590
- let decRes = this.encodingEnabled() && useEncoding ? this.decodeData(data) : void 0;
604
+ let decRes = this.encodingEnabled() && useEncoding ? this.decodeData(data) : undefined;
591
605
  if (decRes instanceof Promise)
592
606
  decRes = yield decRes;
593
607
  return JSON.parse(decRes != null ? decRes : data);
@@ -599,7 +613,7 @@ var DataStore = class {
599
613
  return JSON.parse(JSON.stringify(obj));
600
614
  }
601
615
  //#region storage
602
- /** 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 */
603
617
  getValue(name, defaultValue) {
604
618
  return __async(this, null, function* () {
605
619
  var _a, _b;
@@ -614,7 +628,7 @@ var DataStore = class {
614
628
  });
615
629
  }
616
630
  /**
617
- * 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.
618
632
  * The default storage engines will stringify all passed values like numbers or booleans, so be aware of that.
619
633
  */
620
634
  setValue(name, value) {
@@ -629,7 +643,7 @@ var DataStore = class {
629
643
  }
630
644
  });
631
645
  }
632
- /** 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 */
633
647
  deleteValue(name) {
634
648
  return __async(this, null, function* () {
635
649
  switch (this.storageMethod) {
@@ -667,7 +681,7 @@ var DataStoreSerializer = class {
667
681
  serializeStore(storeInst) {
668
682
  return __async(this, null, function* () {
669
683
  const data = storeInst.encodingEnabled() ? yield storeInst.encodeData(JSON.stringify(storeInst.getData())) : JSON.stringify(storeInst.getData());
670
- const checksum = this.options.addChecksum ? yield this.calcChecksum(data) : void 0;
684
+ const checksum = this.options.addChecksum ? yield this.calcChecksum(data) : undefined;
671
685
  return {
672
686
  id: storeInst.id,
673
687
  data,
@@ -773,7 +787,7 @@ var NanoEmitter = class {
773
787
  let unsub;
774
788
  const onceProxy = (...args) => {
775
789
  unsub();
776
- cb == null ? void 0 : cb(...args);
790
+ cb == null ? undefined : cb(...args);
777
791
  resolve(args);
778
792
  };
779
793
  unsub = this.on(event, onceProxy);
@@ -795,6 +809,106 @@ var NanoEmitter = class {
795
809
  }
796
810
  };
797
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
+
798
912
  // lib/Dialog.ts
799
913
  var defaultDialogCss = `.uu-no-select {
800
914
  user-select: none;
@@ -1012,7 +1126,7 @@ var Dialog = class _Dialog extends NanoEmitter {
1012
1126
  `#uu-style-dialog-${this.id}`
1013
1127
  ];
1014
1128
  for (const sel of clearSelectors)
1015
- (_a = document.querySelector(sel)) == null ? void 0 : _a.remove();
1129
+ (_a = document.querySelector(sel)) == null ? undefined : _a.remove();
1016
1130
  this.events.emit("clear");
1017
1131
  }
1018
1132
  /** Clears the DOM of the dialog and then renders it again */
@@ -1029,8 +1143,8 @@ var Dialog = class _Dialog extends NanoEmitter {
1029
1143
  open(e) {
1030
1144
  return __async(this, null, function* () {
1031
1145
  var _a;
1032
- e == null ? void 0 : e.preventDefault();
1033
- e == null ? void 0 : e.stopImmediatePropagation();
1146
+ e == null ? undefined : e.preventDefault();
1147
+ e == null ? undefined : e.stopImmediatePropagation();
1034
1148
  if (this.isOpen())
1035
1149
  return;
1036
1150
  this.dialogOpen = true;
@@ -1048,7 +1162,7 @@ var Dialog = class _Dialog extends NanoEmitter {
1048
1162
  openDialogs.unshift(this.id);
1049
1163
  for (const dialogId of openDialogs)
1050
1164
  if (dialogId !== this.id)
1051
- (_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");
1052
1166
  document.body.classList.remove("uu-no-select");
1053
1167
  document.body.setAttribute("inert", "true");
1054
1168
  this.events.emit("open");
@@ -1058,8 +1172,8 @@ var Dialog = class _Dialog extends NanoEmitter {
1058
1172
  /** Closes the dialog - prevents default action and immediate propagation of the passed event */
1059
1173
  close(e) {
1060
1174
  var _a, _b;
1061
- e == null ? void 0 : e.preventDefault();
1062
- e == null ? void 0 : e.stopImmediatePropagation();
1175
+ e == null ? undefined : e.preventDefault();
1176
+ e == null ? undefined : e.stopImmediatePropagation();
1063
1177
  if (!this.isOpen())
1064
1178
  return;
1065
1179
  this.dialogOpen = false;
@@ -1072,7 +1186,7 @@ var Dialog = class _Dialog extends NanoEmitter {
1072
1186
  openDialogs.splice(openDialogs.indexOf(this.id), 1);
1073
1187
  currentDialogId = (_a = openDialogs[0]) != null ? _a : null;
1074
1188
  if (currentDialogId)
1075
- (_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");
1076
1190
  if (openDialogs.length === 0) {
1077
1191
  document.body.classList.add("uu-no-select");
1078
1192
  document.body.removeAttribute("inert");
@@ -1116,7 +1230,7 @@ var Dialog = class _Dialog extends NanoEmitter {
1116
1230
  if (this.options.closeOnBgClick) {
1117
1231
  bgElem.addEventListener("click", (e) => {
1118
1232
  var _a;
1119
- 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`)
1120
1234
  this.close(e);
1121
1235
  });
1122
1236
  }
@@ -1141,14 +1255,13 @@ var Dialog = class _Dialog extends NanoEmitter {
1141
1255
  if (interactionKeys.includes(e.key)) {
1142
1256
  preventDefault && e.preventDefault();
1143
1257
  stopPropagation && e.stopPropagation();
1144
- } else
1145
- return;
1258
+ } else return;
1146
1259
  } else if (e instanceof MouseEvent) {
1147
1260
  preventDefault && e.preventDefault();
1148
1261
  stopPropagation && e.stopPropagation();
1149
1262
  }
1150
- (listenerOpts == null ? void 0 : listenerOpts.once) && e.type === "keydown" && elem.removeEventListener("click", proxListener, listenerOpts);
1151
- (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);
1152
1265
  listener(e);
1153
1266
  };
1154
1267
  elem.addEventListener("click", proxListener, listenerOpts);
@@ -1158,8 +1271,8 @@ var Dialog = class _Dialog extends NanoEmitter {
1158
1271
  getDialogContent() {
1159
1272
  return __async(this, null, function* () {
1160
1273
  var _a, _b, _c, _d;
1161
- const header = (_b = (_a = this.options).renderHeader) == null ? void 0 : _b.call(_a);
1162
- 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);
1163
1276
  const dialogWrapperEl = document.createElement("div");
1164
1277
  dialogWrapperEl.id = `uu-${this.id}-dialog`;
1165
1278
  dialogWrapperEl.classList.add("uu-dialog");
@@ -1225,7 +1338,7 @@ function insertValues(input, ...values) {
1225
1338
  return input.replace(/%\d/gm, (match) => {
1226
1339
  var _a, _b;
1227
1340
  const argIndex = Number(match.substring(1)) - 1;
1228
- 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();
1229
1342
  });
1230
1343
  }
1231
1344
  function pauseFor(time) {
@@ -1233,27 +1346,13 @@ function pauseFor(time) {
1233
1346
  setTimeout(() => res(), time);
1234
1347
  });
1235
1348
  }
1236
- function debounce(func, timeout = 300, edge = "falling") {
1237
- let id;
1238
- return function(...args) {
1239
- if (edge === "rising") {
1240
- if (!id) {
1241
- func.apply(this, args);
1242
- id = setTimeout(() => id = void 0, timeout);
1243
- }
1244
- } else {
1245
- clearTimeout(id);
1246
- id = setTimeout(() => func.apply(this, args), timeout);
1247
- }
1248
- };
1249
- }
1250
1349
  function fetchAdvanced(_0) {
1251
1350
  return __async(this, arguments, function* (input, options = {}) {
1252
1351
  var _a;
1253
1352
  const { timeout = 1e4 } = options;
1254
1353
  const { signal, abort } = new AbortController();
1255
- (_a = options.signal) == null ? void 0 : _a.addEventListener("abort", abort);
1256
- let signalOpts = {}, id = void 0;
1354
+ (_a = options.signal) == null ? undefined : _a.addEventListener("abort", abort);
1355
+ let signalOpts = {}, id = undefined;
1257
1356
  if (timeout >= 0) {
1258
1357
  id = setTimeout(() => abort(), timeout);
1259
1358
  signalOpts = { signal };
@@ -1268,6 +1367,18 @@ function fetchAdvanced(_0) {
1268
1367
  }
1269
1368
  });
1270
1369
  }
1370
+ function consumeGen(valGen) {
1371
+ return __async(this, null, function* () {
1372
+ return yield typeof valGen === "function" ? valGen() : valGen;
1373
+ });
1374
+ }
1375
+ function consumeStringGen(strGen) {
1376
+ return __async(this, null, function* () {
1377
+ return typeof strGen === "string" ? strGen : String(
1378
+ typeof strGen === "function" ? yield strGen() : strGen
1379
+ );
1380
+ });
1381
+ }
1271
1382
 
1272
1383
  // lib/SelectorObserver.ts
1273
1384
  var domLoaded = false;
@@ -1284,12 +1395,12 @@ var SelectorObserver = class {
1284
1395
  this.listenerMap = /* @__PURE__ */ new Map();
1285
1396
  const _a = options, {
1286
1397
  defaultDebounce,
1287
- defaultDebounceEdge,
1398
+ defaultDebounceType,
1288
1399
  disableOnNoListeners,
1289
1400
  enableOnAddListener
1290
1401
  } = _a, observerOptions = __objRest(_a, [
1291
1402
  "defaultDebounce",
1292
- "defaultDebounceEdge",
1403
+ "defaultDebounceType",
1293
1404
  "disableOnNoListeners",
1294
1405
  "enableOnAddListener"
1295
1406
  ]);
@@ -1299,7 +1410,7 @@ var SelectorObserver = class {
1299
1410
  }, observerOptions);
1300
1411
  this.customOptions = {
1301
1412
  defaultDebounce: defaultDebounce != null ? defaultDebounce : 0,
1302
- defaultDebounceEdge: defaultDebounceEdge != null ? defaultDebounceEdge : "rising",
1413
+ defaultDebounceType: defaultDebounceType != null ? defaultDebounceType : "immediate",
1303
1414
  disableOnNoListeners: disableOnNoListeners != null ? disableOnNoListeners : false,
1304
1415
  enableOnAddListener: enableOnAddListener != null ? enableOnAddListener : true
1305
1416
  };
@@ -1343,7 +1454,7 @@ var SelectorObserver = class {
1343
1454
  this.removeListener(selector, options);
1344
1455
  }
1345
1456
  }
1346
- if (((_a = this.listenerMap.get(selector)) == null ? void 0 : _a.length) === 0)
1457
+ if (((_a = this.listenerMap.get(selector)) == null ? undefined : _a.length) === 0)
1347
1458
  this.listenerMap.delete(selector);
1348
1459
  if (this.listenerMap.size === 0 && this.customOptions.disableOnNoListeners)
1349
1460
  this.disable();
@@ -1369,7 +1480,7 @@ var SelectorObserver = class {
1369
1480
  options.listener = debounce(
1370
1481
  options.listener,
1371
1482
  options.debounce || this.customOptions.defaultDebounce,
1372
- options.debounceEdge || this.customOptions.defaultDebounceEdge
1483
+ options.debounceType || this.customOptions.defaultDebounceType
1373
1484
  );
1374
1485
  }
1375
1486
  if (this.listenerMap.has(selector))
@@ -1387,7 +1498,7 @@ var SelectorObserver = class {
1387
1498
  if (!this.enabled)
1388
1499
  return;
1389
1500
  this.enabled = false;
1390
- (_a = this.observer) == null ? void 0 : _a.disconnect();
1501
+ (_a = this.observer) == null ? undefined : _a.disconnect();
1391
1502
  }
1392
1503
  /**
1393
1504
  * Enables or reenables the observation of the child elements.
@@ -1400,7 +1511,7 @@ var SelectorObserver = class {
1400
1511
  if (this.enabled || !baseElement)
1401
1512
  return false;
1402
1513
  this.enabled = true;
1403
- (_a = this.observer) == null ? void 0 : _a.observe(baseElement, this.observerOptions);
1514
+ (_a = this.observer) == null ? undefined : _a.observe(baseElement, this.observerOptions);
1404
1515
  if (immediatelyCheckSelectors)
1405
1516
  this.checkAllSelectors();
1406
1517
  return true;
@@ -1447,34 +1558,164 @@ var SelectorObserver = class {
1447
1558
 
1448
1559
  // lib/translation.ts
1449
1560
  var trans = {};
1450
- var curLang;
1451
- var trLang = (language, key, ...args) => {
1452
- var _a;
1453
- if (!language)
1454
- return key;
1455
- const trText = (_a = trans[language]) == null ? void 0 : _a[key];
1456
- if (!trText)
1457
- return key;
1458
- if (args.length > 0 && trText.match(/%\d/)) {
1459
- return insertValues(trText, ...args);
1460
- }
1461
- return trText;
1462
- };
1463
- function tr(key, ...args) {
1464
- return trLang(curLang, key, ...args);
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
+ };
1594
+ const keyParts = key.split(".");
1595
+ let value = trObj;
1596
+ for (const part of keyParts) {
1597
+ if (typeof value !== "object" || value === null) {
1598
+ value = undefined;
1599
+ break;
1600
+ }
1601
+ value = value == null ? undefined : value[part];
1602
+ }
1603
+ if (typeof value === "string")
1604
+ return transformTrVal(key, value);
1605
+ value = trObj == null ? undefined : trObj[key];
1606
+ if (typeof value === "string")
1607
+ return transformTrVal(key, value);
1608
+ return fallbackLang ? translate(fallbackLang, key, ...trArgs) : key;
1465
1609
  }
1466
- tr.forLang = trLang;
1467
- tr.addLanguage = (language, translations) => {
1468
- trans[language] = translations;
1469
- };
1470
- tr.setLanguage = (language) => {
1471
- curLang = language;
1472
- };
1473
- tr.getLanguage = () => {
1474
- 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;
1475
1634
  };
1476
- tr.getTranslations = (language) => {
1477
- 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
+ }
1478
1719
  };
1479
1720
 
1480
- export { DataStore, DataStoreSerializer, Dialog, NanoEmitter, SelectorObserver, addGlobalStyle, addParent, autoPlural, clamp, compress, computeHash, currentDialogId, darkenColor, debounce, decompress, defaultDialogCss, defaultStrings, 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 };