tiny-essentials 1.19.3 → 1.20.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.
@@ -2914,7 +2914,7 @@ function countObj(obj) {
2914
2914
  // Is Array
2915
2915
  if (Array.isArray(obj)) return obj.length;
2916
2916
  // Object
2917
- if (isJsonObject(obj)) return Object.keys(obj).length;
2917
+ if (objChecker_isJsonObject(obj)) return Object.keys(obj).length;
2918
2918
  // Nothing
2919
2919
  return 0;
2920
2920
  }
@@ -2934,7 +2934,7 @@ function countObj(obj) {
2934
2934
  * @param {unknown} value - The value to test.
2935
2935
  * @returns {value is Record<string | number | symbol, unknown>} Returns true if the value is a pure object.
2936
2936
  */
2937
- function isJsonObject(value) {
2937
+ function objChecker_isJsonObject(value) {
2938
2938
  if (value === null || typeof value !== 'object') return false;
2939
2939
  if (Array.isArray(value)) return false;
2940
2940
  if (Object.prototype.toString.call(value) !== '[object Object]') return false;
@@ -3261,7 +3261,7 @@ extendObjType([
3261
3261
  [
3262
3262
  'object',
3263
3263
  /** @param {*} val @returns {val is Record<string | number | symbol, unknown>} */
3264
- (val) => isJsonObject(val),
3264
+ (val) => objChecker_isJsonObject(val),
3265
3265
  ],
3266
3266
  ]);
3267
3267
 
@@ -9811,7 +9811,7 @@ class TinyDragger {
9811
9811
  if (
9812
9812
  options.vibration !== undefined &&
9813
9813
  options.vibration !== false &&
9814
- !isJsonObject(options.vibration)
9814
+ !objChecker_isJsonObject(options.vibration)
9815
9815
  )
9816
9816
  throw new Error('The "vibration" option must be an object or false.');
9817
9817
 
@@ -9855,7 +9855,7 @@ class TinyDragger {
9855
9855
  const vibrationTemplate = { start: false, end: false, collide: false, move: false };
9856
9856
  this.#vibration = Object.assign(
9857
9857
  vibrationTemplate,
9858
- isJsonObject(options.vibration) ? options.vibration : {},
9858
+ objChecker_isJsonObject(options.vibration) ? options.vibration : {},
9859
9859
  );
9860
9860
 
9861
9861
  if (typeof options.classDragging === 'string') this.#classDragging = options.classDragging;
@@ -10719,7 +10719,7 @@ class TinyDragger {
10719
10719
  *
10720
10720
  * @class
10721
10721
  */
10722
- class TinyEvents {
10722
+ class TinyEvents_TinyEvents {
10723
10723
  /** @type {Map<string, { handler: handler; config: { once: boolean } }[]>} */
10724
10724
  #listeners = new Map();
10725
10725
 
@@ -11055,7 +11055,7 @@ class TinyEvents {
11055
11055
  }
11056
11056
  }
11057
11057
 
11058
- /* harmony default export */ const libs_TinyEvents = (TinyEvents);
11058
+ /* harmony default export */ const libs_TinyEvents = (TinyEvents_TinyEvents);
11059
11059
 
11060
11060
  ;// ./src/v1/libs/TinySmartScroller.mjs
11061
11061
 
@@ -12554,7 +12554,7 @@ class TinyLocalStorage {
12554
12554
  return value.map(TinyLocalStorage.encodeSpecialJson);
12555
12555
  }
12556
12556
 
12557
- if (isJsonObject(value)) {
12557
+ if (objChecker_isJsonObject(value)) {
12558
12558
  const encoded = {};
12559
12559
  for (const key in value) {
12560
12560
  // @ts-ignore
@@ -12577,7 +12577,7 @@ class TinyLocalStorage {
12577
12577
  * @type {decodeSpecialJson}
12578
12578
  */
12579
12579
  static decodeSpecialJson(value) {
12580
- const isJson = isJsonObject(value);
12580
+ const isJson = objChecker_isJsonObject(value);
12581
12581
  if (isJson) {
12582
12582
  if (value.__undefined__) return undefined;
12583
12583
  if (value.__null__) return null;
@@ -12745,7 +12745,7 @@ class TinyLocalStorage {
12745
12745
  setJson(name, data) {
12746
12746
  this.#isProtectedDbKey(name);
12747
12747
  if (
12748
- !isJsonObject(data) &&
12748
+ !objChecker_isJsonObject(data) &&
12749
12749
  !Array.isArray(data) &&
12750
12750
  !(data instanceof Map) &&
12751
12751
  !(data instanceof Set)
@@ -12810,7 +12810,7 @@ class TinyLocalStorage {
12810
12810
  decoded instanceof Map ||
12811
12811
  decoded instanceof Set ||
12812
12812
  Array.isArray(decoded) ||
12813
- isJsonObject(decoded)
12813
+ objChecker_isJsonObject(decoded)
12814
12814
  )
12815
12815
  return decoded;
12816
12816
  return fallback;
@@ -12981,7 +12981,7 @@ class TinyLocalStorage {
12981
12981
  /** @type {{ value: string; __string__: boolean }} */
12982
12982
  // @ts-ignore
12983
12983
  value = JSON.parse(value);
12984
- if (!isJsonObject(value) || !value.__string__ || typeof value.value !== 'string') return null;
12984
+ if (!objChecker_isJsonObject(value) || !value.__string__ || typeof value.value !== 'string') return null;
12985
12985
  value = value.value;
12986
12986
  } catch {
12987
12987
  value = null;
@@ -13183,6 +13183,901 @@ TinyLocalStorage.registerJsonType(
13183
13183
 
13184
13184
  /* harmony default export */ const libs_TinyLocalStorage = ((/* unused pure expression or super */ null && (TinyLocalStorage)));
13185
13185
 
13186
+ ;// ./src/v1/libs/TinyIframeEvents.mjs
13187
+
13188
+
13189
+
13190
+ /** @type {WeakMap<Window, TinyIframeEvents>} */
13191
+ const instances = new WeakMap();
13192
+
13193
+ /**
13194
+ * @callback handler
13195
+ * A function to handle incoming event payloads.
13196
+ * @param {any} payload - The data sent by the emitter.
13197
+ * @param {MessageEvent<any>} event - Metadata about the message.
13198
+ */
13199
+
13200
+ /**
13201
+ * A flexible event routing system for structured communication
13202
+ * between a parent window and its iframe using `postMessage`.
13203
+ *
13204
+ * This class abstracts the complexity of cross-origin and window-type handling,
13205
+ * allowing both the iframe and parent to:
13206
+ * - Send events with arbitrary payloads
13207
+ * - Listen to specific event names
13208
+ * - Filter events by origin and source
13209
+ * - Work symmetrically from both sides with automatic direction handling
13210
+ *
13211
+ * Use this class when building applications that require modular, event-driven
13212
+ * communication across embedded frames.
13213
+ */
13214
+ class TinyIframeEvents {
13215
+ #events = new TinyEvents();
13216
+
13217
+ /**
13218
+ * Enables or disables throwing an error when the maximum number of listeners is exceeded.
13219
+ *
13220
+ * @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
13221
+ */
13222
+ setThrowOnMaxListeners(shouldThrow) {
13223
+ return this.#events.setThrowOnMaxListeners(shouldThrow);
13224
+ }
13225
+
13226
+ /**
13227
+ * Checks whether an error will be thrown when the max listener limit is exceeded.
13228
+ *
13229
+ * @returns {boolean} True if an error will be thrown, false if only a warning is shown.
13230
+ */
13231
+ getThrowOnMaxListeners() {
13232
+ return this.#events.getThrowOnMaxListeners();
13233
+ }
13234
+
13235
+ /////////////////////////////////////////////////////////////
13236
+
13237
+ /**
13238
+ * Adds a listener to the beginning of the listeners array for the specified event.
13239
+ *
13240
+ * @param {string} event - Event name.
13241
+ * @param {handler} handler - The callback function.
13242
+ */
13243
+ prependListener(event, handler) {
13244
+ return this.#events.prependListener(event, handler);
13245
+ }
13246
+
13247
+ /**
13248
+ * Adds a one-time listener to the beginning of the listeners array for the specified event.
13249
+ *
13250
+ * @param {string} event - Event name.
13251
+ * @param {handler} handler - The callback function.
13252
+ * @returns {handler} - The wrapped handler used internally.
13253
+ */
13254
+ prependListenerOnce(event, handler) {
13255
+ return this.#events.prependListenerOnce(event, handler);
13256
+ }
13257
+
13258
+ //////////////////////////////////////////////////////////////////////
13259
+
13260
+ /**
13261
+ * Adds a event listener.
13262
+ *
13263
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
13264
+ * @param {handler} handler - Callback function to be called when event fires.
13265
+ */
13266
+ appendListener(event, handler) {
13267
+ return this.#events.appendListener(event, handler);
13268
+ }
13269
+
13270
+ /**
13271
+ * Registers an event listener that runs only once, then is removed.
13272
+ *
13273
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
13274
+ * @param {handler} handler - The callback function to run on event.
13275
+ * @returns {handler} - The wrapped version of the handler.
13276
+ */
13277
+ appendListenerOnce(event, handler) {
13278
+ return this.#events.appendListenerOnce(event, handler);
13279
+ }
13280
+
13281
+ /**
13282
+ * Adds a event listener.
13283
+ *
13284
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
13285
+ * @param {handler} handler - Callback function to be called when event fires.
13286
+ */
13287
+ on(event, handler) {
13288
+ return this.#events.on(event, handler);
13289
+ }
13290
+
13291
+ /**
13292
+ * Registers an event listener that runs only once, then is removed.
13293
+ *
13294
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
13295
+ * @param {handler} handler - The callback function to run on event.
13296
+ * @returns {handler} - The wrapped version of the handler.
13297
+ */
13298
+ once(event, handler) {
13299
+ return this.#events.once(event, handler);
13300
+ }
13301
+
13302
+ ////////////////////////////////////////////////////////////////////
13303
+
13304
+ /**
13305
+ * Removes a previously registered event listener.
13306
+ *
13307
+ * @param {string} event - The name of the event to remove the handler from.
13308
+ * @param {handler} handler - The specific callback function to remove.
13309
+ */
13310
+ off(event, handler) {
13311
+ return this.#events.off(event, handler);
13312
+ }
13313
+
13314
+ /**
13315
+ * Removes all event listeners of a specific type from the element.
13316
+ *
13317
+ * @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
13318
+ */
13319
+ offAll(event) {
13320
+ return this.#events.offAll(event);
13321
+ }
13322
+
13323
+ /**
13324
+ * Removes all event listeners of all types from the element.
13325
+ */
13326
+ offAllTypes() {
13327
+ return this.#events.offAllTypes();
13328
+ }
13329
+
13330
+ ////////////////////////////////////////////////////////////
13331
+
13332
+ /**
13333
+ * Returns the number of listeners for a given event.
13334
+ *
13335
+ * @param {string} event - The name of the event.
13336
+ * @returns {number} Number of listeners for the event.
13337
+ */
13338
+ listenerCount(event) {
13339
+ return this.#events.listenerCount(event);
13340
+ }
13341
+
13342
+ /**
13343
+ * Returns a copy of the array of listeners for the specified event.
13344
+ *
13345
+ * @param {string} event - The name of the event.
13346
+ * @returns {handler[]} Array of listener functions.
13347
+ */
13348
+ listeners(event) {
13349
+ return this.#events.listeners(event);
13350
+ }
13351
+
13352
+ /**
13353
+ * Returns a copy of the array of listeners for the specified event.
13354
+ *
13355
+ * @param {string} event - The name of the event.
13356
+ * @returns {handler[]} Array of listener functions.
13357
+ */
13358
+ onceListeners(event) {
13359
+ return this.#events.onceListeners(event);
13360
+ }
13361
+
13362
+ /**
13363
+ * Returns a copy of the internal listeners array for the specified event,
13364
+ * including wrapper functions like those used by `.once()`.
13365
+ * @param {string | symbol} event - The event name.
13366
+ * @returns {handler[]} An array of raw listener functions.
13367
+ */
13368
+ allListeners(event) {
13369
+ return this.#events.allListeners(event);
13370
+ }
13371
+
13372
+ /**
13373
+ * Returns an array of event names for which there are registered listeners.
13374
+ *
13375
+ * @returns {string[]} Array of registered event names.
13376
+ */
13377
+ eventNames() {
13378
+ return this.#events.eventNames();
13379
+ }
13380
+
13381
+ //////////////////////////////////////////////////////
13382
+
13383
+ /**
13384
+ * Sets the maximum number of listeners per event before a warning is shown.
13385
+ *
13386
+ * @param {number} n - The maximum number of listeners.
13387
+ */
13388
+ setMaxListeners(n) {
13389
+ return this.#events.setMaxListeners(n);
13390
+ }
13391
+
13392
+ /**
13393
+ * Gets the maximum number of listeners allowed per event.
13394
+ *
13395
+ * @returns {number} The maximum number of listeners.
13396
+ */
13397
+ getMaxListeners() {
13398
+ return this.#events.getMaxListeners();
13399
+ }
13400
+
13401
+ ///////////////////////////////////////////////////
13402
+
13403
+ /** @type {Window} */
13404
+ #targetWindow;
13405
+
13406
+ /** @type {string} */
13407
+ #targetOrigin;
13408
+
13409
+ /** @type {string} */
13410
+ #selfType;
13411
+
13412
+ /** @type {boolean} */
13413
+ #isDestroyed = false;
13414
+
13415
+ /** @type {boolean} */
13416
+ #ready = false;
13417
+
13418
+ /**
13419
+ * @typedef {object} IframeEventBase
13420
+ * @property {string} eventName - The name of the custom event route.
13421
+ * @property {any} payload - The data being sent (can be any type).
13422
+ * @property {'iframe' | 'parent'} direction - Indicates the sender: 'iframe' or 'parent'.
13423
+ */
13424
+
13425
+ /**
13426
+ * Queue of messages emitted before connection is ready
13427
+ * @type {IframeEventBase[]}
13428
+ */
13429
+ #pendingQueue = [];
13430
+
13431
+ /** @type {string} Internal message type for routed communication */
13432
+ #secretEventName = '__tinyIframeEvent__';
13433
+
13434
+ /**
13435
+ * Gets the internal secret iframe event name.
13436
+ * @returns {string}
13437
+ */
13438
+ get secretEventName() {
13439
+ return this.#secretEventName;
13440
+ }
13441
+
13442
+ /**
13443
+ * Sets the internal secret iframe event name.
13444
+ * @param {string} name
13445
+ * @throws {TypeError} If the value is not a string.
13446
+ */
13447
+ set secretEventName(name) {
13448
+ if (typeof name !== 'string') throw new TypeError('TinyIframeEvents: secretEventName must be a string.');
13449
+ this.#secretEventName = name;
13450
+ }
13451
+
13452
+ /**
13453
+ * Creates a new TinyIframeEvents instance to manage communication between iframe and parent.
13454
+ * Automatically determines the current context (`iframe` or `parent`) based on the `targetWindow`.
13455
+ *
13456
+ * @param {Object} config - Configuration object.
13457
+ * @param {HTMLIFrameElement} [config.targetIframe] - The target window to post messages to. Defaults to `window.parent` (assumes this is inside an iframe).
13458
+ * @param {string} [config.targetOrigin] - The target origin to restrict messages to. Defaults to `window.location.origin`.
13459
+ */
13460
+ constructor({ targetIframe, targetOrigin } = {}) {
13461
+ if (
13462
+ typeof targetIframe !== 'undefined' &&
13463
+ (!(targetIframe instanceof HTMLIFrameElement) || !targetIframe.contentWindow)
13464
+ )
13465
+ throw new TypeError(
13466
+ `[TinyIframeEvents] Invalid "targetIframe" provided: expected a HTML Iframe Element, received ${typeof targetIframe}`,
13467
+ );
13468
+ if (typeof targetOrigin !== 'undefined' && typeof targetOrigin !== 'string')
13469
+ throw new TypeError(
13470
+ `[TinyIframeEvents] Invalid "targetOrigin" provided: expected a string, received ${typeof targetOrigin}`,
13471
+ );
13472
+
13473
+ this.#targetWindow = targetIframe?.contentWindow ?? window.parent;
13474
+ this.#targetOrigin = targetOrigin ?? window.location.origin;
13475
+ this.#selfType = !targetIframe ? 'iframe' : 'parent';
13476
+ if (instances.has(this.#targetWindow)) throw new Error('Duplicate window reference.');
13477
+
13478
+ this._boundOnMessage = this.#onMessage.bind(this);
13479
+ this._boundOnceMessage = this.#onceMessage.bind(this);
13480
+
13481
+ if (
13482
+ this.#targetWindow.document.readyState === 'complete' ||
13483
+ this.#targetWindow.document.readyState === 'interactive'
13484
+ )
13485
+ this.#onceMessage();
13486
+ else {
13487
+ this.#targetWindow.addEventListener('load', this._boundOnceMessage, false);
13488
+ this.#targetWindow.addEventListener('DOMContentLoaded', this._boundOnceMessage, false);
13489
+ }
13490
+
13491
+ window.addEventListener('message', this._boundOnMessage, false);
13492
+ instances.set(this.#targetWindow, this);
13493
+ }
13494
+
13495
+ /**
13496
+ * Marks the communication as ready and flushes any queued messages.
13497
+ */
13498
+ #onceMessage() {
13499
+ if (this.#ready) return;
13500
+ this.#ready = true;
13501
+ this.#flushQueue();
13502
+ }
13503
+
13504
+ /**
13505
+ * Internal handler for the message event. Filters and dispatches incoming messages.
13506
+ *
13507
+ * @param {MessageEvent<any>} event - The message event received via `postMessage`.
13508
+ */
13509
+ #onMessage(event) {
13510
+ const { data, source } = event;
13511
+
13512
+ // Reject non-object or unrelated messages
13513
+ if (!isJsonObject(data) || !data[this.#secretEventName]) return;
13514
+
13515
+ const { eventName, payload, direction } = data;
13516
+
13517
+ // Reject if not from the expected window (for security)
13518
+ if (source !== this.#targetWindow) return;
13519
+
13520
+ // Reject if direction is not meant for this side
13521
+ if (
13522
+ (this.#selfType === 'iframe' && direction !== 'iframe') ||
13523
+ (this.#selfType === 'parent' && direction !== 'parent')
13524
+ )
13525
+ return;
13526
+
13527
+ this.#events.emit(/** @type {string} */ (eventName), payload, event);
13528
+ }
13529
+
13530
+ /**
13531
+ * Sends an event to the target window.
13532
+ *
13533
+ * @param {string} eventName - A unique name identifying the event.
13534
+ * @param {*} payload - The data to send with the event. Can be any serializable value.
13535
+ * @throws {Error} If `eventName` is not a string.
13536
+ */
13537
+ emit(eventName, payload) {
13538
+ if (typeof eventName !== 'string') throw new TypeError('Event name must be a string.');
13539
+ if (this.#isDestroyed) throw new Error('Cannot emit: instance has been destroyed.');
13540
+
13541
+ /** @type {IframeEventBase} */
13542
+ const message = {
13543
+ [this.#secretEventName]: true,
13544
+ eventName,
13545
+ payload,
13546
+ direction: this.#selfType === 'parent' ? 'iframe' : 'parent',
13547
+ };
13548
+
13549
+ if (!this.#ready) {
13550
+ this.#pendingQueue.push(message);
13551
+ return;
13552
+ }
13553
+
13554
+ this.#targetWindow.postMessage(message, this.#targetOrigin);
13555
+ }
13556
+
13557
+ /**
13558
+ * Sends all pending messages queued before handshake completion.
13559
+ *
13560
+ * @returns {void}
13561
+ */
13562
+ #flushQueue() {
13563
+ while (this.#pendingQueue.length) {
13564
+ const data = this.#pendingQueue.shift();
13565
+ if (data) this.#targetWindow.postMessage(data, this.#targetOrigin);
13566
+ }
13567
+ }
13568
+
13569
+ /**
13570
+ * Checks if the communication instance has been destroyed.
13571
+ *
13572
+ * @returns {boolean}
13573
+ */
13574
+ isDestroyed() {
13575
+ return this.#isDestroyed;
13576
+ }
13577
+
13578
+ /**
13579
+ * Unsubscribes all registered event listeners and removes the message handler.
13580
+ * Call this when the instance is no longer needed to prevent memory leaks.
13581
+ */
13582
+ destroy() {
13583
+ this.#isDestroyed = true;
13584
+ window.removeEventListener('message', this._boundOnMessage);
13585
+ this.#targetWindow.removeEventListener('load', this._boundOnceMessage, false);
13586
+ this.#targetWindow.removeEventListener('DOMContentLoaded', this._boundOnceMessage, false);
13587
+ this.#events.offAllTypes();
13588
+ this.#pendingQueue = [];
13589
+ instances.delete(this.#targetWindow);
13590
+ }
13591
+ }
13592
+
13593
+ /* harmony default export */ const libs_TinyIframeEvents = ((/* unused pure expression or super */ null && (TinyIframeEvents)));
13594
+
13595
+ ;// ./src/v1/libs/TinyNewWinEvents.mjs
13596
+
13597
+
13598
+ /**
13599
+ * Stores polling intervals associated with window references.
13600
+ * Used to detect when the window is closed.
13601
+ *
13602
+ * @type {WeakMap<Window, NodeJS.Timeout>}
13603
+ */
13604
+ const pollClosedInterval = new WeakMap();
13605
+
13606
+ /**
13607
+ * @callback handler
13608
+ * A function to handle incoming event payloads.
13609
+ * @param {any} payload - The data sent by the emitter.
13610
+ * @param {MessageEvent<any>} event - Metadata about the message.
13611
+ */
13612
+
13613
+ /**
13614
+ * TinyNewWinEvents provides structured communication between a main window
13615
+ * and a child window (created using window.open) using postMessage.
13616
+ *
13617
+ * It supports routing, queuing messages until handshake is ready,
13618
+ * connection status checks, and close detection.
13619
+ *
13620
+ * @class
13621
+ */
13622
+ class TinyNewWinEvents {
13623
+ #events = new TinyEvents();
13624
+
13625
+ /**
13626
+ * Enables or disables throwing an error when the maximum number of listeners is exceeded.
13627
+ *
13628
+ * @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
13629
+ */
13630
+ setThrowOnMaxListeners(shouldThrow) {
13631
+ return this.#events.setThrowOnMaxListeners(shouldThrow);
13632
+ }
13633
+
13634
+ /**
13635
+ * Checks whether an error will be thrown when the max listener limit is exceeded.
13636
+ *
13637
+ * @returns {boolean} True if an error will be thrown, false if only a warning is shown.
13638
+ */
13639
+ getThrowOnMaxListeners() {
13640
+ return this.#events.getThrowOnMaxListeners();
13641
+ }
13642
+
13643
+ /////////////////////////////////////////////////////////////
13644
+
13645
+ /**
13646
+ * Adds a listener to the beginning of the listeners array for the specified event.
13647
+ *
13648
+ * @param {string} event - Event name.
13649
+ * @param {handler} handler - The callback function.
13650
+ */
13651
+ prependListener(event, handler) {
13652
+ return this.#events.prependListener(event, handler);
13653
+ }
13654
+
13655
+ /**
13656
+ * Adds a one-time listener to the beginning of the listeners array for the specified event.
13657
+ *
13658
+ * @param {string} event - Event name.
13659
+ * @param {handler} handler - The callback function.
13660
+ * @returns {handler} - The wrapped handler used internally.
13661
+ */
13662
+ prependListenerOnce(event, handler) {
13663
+ return this.#events.prependListenerOnce(event, handler);
13664
+ }
13665
+
13666
+ //////////////////////////////////////////////////////////////////////
13667
+
13668
+ /**
13669
+ * Adds a event listener.
13670
+ *
13671
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
13672
+ * @param {handler} handler - Callback function to be called when event fires.
13673
+ */
13674
+ appendListener(event, handler) {
13675
+ return this.#events.appendListener(event, handler);
13676
+ }
13677
+
13678
+ /**
13679
+ * Registers an event listener that runs only once, then is removed.
13680
+ *
13681
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
13682
+ * @param {handler} handler - The callback function to run on event.
13683
+ * @returns {handler} - The wrapped version of the handler.
13684
+ */
13685
+ appendListenerOnce(event, handler) {
13686
+ return this.#events.appendListenerOnce(event, handler);
13687
+ }
13688
+
13689
+ /**
13690
+ * Adds a event listener.
13691
+ *
13692
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
13693
+ * @param {handler} handler - Callback function to be called when event fires.
13694
+ */
13695
+ on(event, handler) {
13696
+ return this.#events.on(event, handler);
13697
+ }
13698
+
13699
+ /**
13700
+ * Registers an event listener that runs only once, then is removed.
13701
+ *
13702
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
13703
+ * @param {handler} handler - The callback function to run on event.
13704
+ * @returns {handler} - The wrapped version of the handler.
13705
+ */
13706
+ once(event, handler) {
13707
+ return this.#events.once(event, handler);
13708
+ }
13709
+
13710
+ ////////////////////////////////////////////////////////////////////
13711
+
13712
+ /**
13713
+ * Removes a previously registered event listener.
13714
+ *
13715
+ * @param {string} event - The name of the event to remove the handler from.
13716
+ * @param {handler} handler - The specific callback function to remove.
13717
+ */
13718
+ off(event, handler) {
13719
+ return this.#events.off(event, handler);
13720
+ }
13721
+
13722
+ /**
13723
+ * Removes all event listeners of a specific type from the element.
13724
+ *
13725
+ * @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
13726
+ */
13727
+ offAll(event) {
13728
+ return this.#events.offAll(event);
13729
+ }
13730
+
13731
+ /**
13732
+ * Removes all event listeners of all types from the element.
13733
+ */
13734
+ offAllTypes() {
13735
+ return this.#events.offAllTypes();
13736
+ }
13737
+
13738
+ ////////////////////////////////////////////////////////////
13739
+
13740
+ /**
13741
+ * Returns the number of listeners for a given event.
13742
+ *
13743
+ * @param {string} event - The name of the event.
13744
+ * @returns {number} Number of listeners for the event.
13745
+ */
13746
+ listenerCount(event) {
13747
+ return this.#events.listenerCount(event);
13748
+ }
13749
+
13750
+ /**
13751
+ * Returns a copy of the array of listeners for the specified event.
13752
+ *
13753
+ * @param {string} event - The name of the event.
13754
+ * @returns {handler[]} Array of listener functions.
13755
+ */
13756
+ listeners(event) {
13757
+ return this.#events.listeners(event);
13758
+ }
13759
+
13760
+ /**
13761
+ * Returns a copy of the array of listeners for the specified event.
13762
+ *
13763
+ * @param {string} event - The name of the event.
13764
+ * @returns {handler[]} Array of listener functions.
13765
+ */
13766
+ onceListeners(event) {
13767
+ return this.#events.onceListeners(event);
13768
+ }
13769
+
13770
+ /**
13771
+ * Returns a copy of the internal listeners array for the specified event,
13772
+ * including wrapper functions like those used by `.once()`.
13773
+ * @param {string | symbol} event - The event name.
13774
+ * @returns {handler[]} An array of raw listener functions.
13775
+ */
13776
+ allListeners(event) {
13777
+ return this.#events.allListeners(event);
13778
+ }
13779
+
13780
+ /**
13781
+ * Returns an array of event names for which there are registered listeners.
13782
+ *
13783
+ * @returns {string[]} Array of registered event names.
13784
+ */
13785
+ eventNames() {
13786
+ return this.#events.eventNames();
13787
+ }
13788
+
13789
+ //////////////////////////////////////////////////////
13790
+
13791
+ /**
13792
+ * Sets the maximum number of listeners per event before a warning is shown.
13793
+ *
13794
+ * @param {number} n - The maximum number of listeners.
13795
+ */
13796
+ setMaxListeners(n) {
13797
+ return this.#events.setMaxListeners(n);
13798
+ }
13799
+
13800
+ /**
13801
+ * Gets the maximum number of listeners allowed per event.
13802
+ *
13803
+ * @returns {number} The maximum number of listeners.
13804
+ */
13805
+ getMaxListeners() {
13806
+ return this.#events.getMaxListeners();
13807
+ }
13808
+
13809
+ ///////////////////////////////////////////////////
13810
+
13811
+ /** @type {Window|null} Reference to the opened or parent window */
13812
+ #windowRef;
13813
+
13814
+ /** @type {string} Expected origin for postMessage communication */
13815
+ #targetOrigin;
13816
+
13817
+ /** @type {{ route: string, payload: any }[]} Queue of messages emitted before connection is ready */
13818
+ #pendingQueue = [];
13819
+
13820
+ /** @type {boolean} True if handshake between windows is complete */
13821
+ #ready = false;
13822
+
13823
+ /** @type {boolean} True if this instance is the main window (host) */
13824
+ #isHost = false;
13825
+
13826
+ /** @type {NodeJS.Timeout|null} Interval for polling child window closure */
13827
+ #pollClosedInterval = null;
13828
+
13829
+ /** @type {string} Internal message type for handshake */
13830
+ #readyEventName = '__TNE_READY__';
13831
+
13832
+ /** @type {string} Internal message type for routed communication */
13833
+ #routeEventName = '__TNE_ROUTE__';
13834
+
13835
+ /**
13836
+ * Gets the internal handshake event name.
13837
+ * @returns {string}
13838
+ */
13839
+ get readyEventName() {
13840
+ return this.#readyEventName;
13841
+ }
13842
+
13843
+ /**
13844
+ * Sets the internal handshake event name.
13845
+ * @param {string} name
13846
+ * @throws {TypeError} If the value is not a string.
13847
+ */
13848
+ set readyEventName(name) {
13849
+ if (typeof name !== 'string') throw new TypeError('TinyNewWinEvents: readyEventName must be a string.');
13850
+ this.#readyEventName = name;
13851
+ }
13852
+
13853
+ /**
13854
+ * Gets the internal route event name.
13855
+ * @returns {string}
13856
+ */
13857
+ get routeEventName() {
13858
+ return this.#routeEventName;
13859
+ }
13860
+
13861
+ /**
13862
+ * Sets the internal route event name.
13863
+ * @param {string} name
13864
+ * @throws {TypeError} If the value is not a string.
13865
+ */
13866
+ set routeEventName(name) {
13867
+ if (typeof name !== 'string') throw new TypeError('TinyNewWinEvents: routeEventName must be a string.');
13868
+ this.#routeEventName = name;
13869
+ }
13870
+
13871
+ /**
13872
+ * Initializes a TinyNewWinEvents instance for communication.
13873
+ *
13874
+ * @param {Object} [settings={}] Configuration object.
13875
+ * @param {string} [settings.targetOrigin] Origin to enforce in postMessage.
13876
+ * @param {string} [settings.url] URL string to open.
13877
+ * @param {string} [settings.name] Window name (required if opening a new window).
13878
+ * @param {string} [settings.features] Features string for `window.open`.
13879
+ *
13880
+ * @throws {Error} If `name` is "_blank", which is not allowed.
13881
+ * @throws {TypeError} If `targetOrigin`, `url`, or `features` are not strings (when provided).
13882
+ * @throws {Error} If the window reference is invalid or already being tracked.
13883
+ */
13884
+ constructor({ targetOrigin, url, name, features } = {}) {
13885
+ if (typeof name === 'string' && name === '_blank')
13886
+ throw new Error(
13887
+ 'TinyNewWinEvents: The window name "_blank" is not supported. Please use a custom name to allow tracking.',
13888
+ );
13889
+ if (typeof targetOrigin !== 'undefined' && typeof targetOrigin !== 'string')
13890
+ throw new TypeError('TinyNewWinEvents: The "targetOrigin" option must be a string.');
13891
+ if (typeof url !== 'undefined' && typeof url !== 'string')
13892
+ throw new TypeError('TinyNewWinEvents: The "url" option must be a string.');
13893
+ if (typeof features !== 'undefined' && typeof features !== 'string')
13894
+ throw new TypeError('TinyNewWinEvents: The "features" option must be a string if provided.');
13895
+
13896
+ // Open Page
13897
+ if (typeof url === 'undefined') this.#windowRef = window.opener;
13898
+ // Main Page
13899
+ else {
13900
+ this.#windowRef = typeof url === 'string' ? window.open(url, name, features) : url;
13901
+ this.#isHost = true;
13902
+ }
13903
+
13904
+ if (!this.#windowRef || pollClosedInterval.has(this.#windowRef))
13905
+ throw new Error('Invalid or duplicate window reference.');
13906
+ this.#targetOrigin = targetOrigin ?? window.location.origin;
13907
+ this._handleMessage = this.#handleMessage.bind(this);
13908
+ window.addEventListener('message', this._handleMessage, false);
13909
+
13910
+ // Sends handshake if it is host (main window)
13911
+ if (!this.#isHost) {
13912
+ this.#postRaw(this.#readyEventName, null);
13913
+ this.#startCloseWatcher();
13914
+ }
13915
+ }
13916
+
13917
+ /**
13918
+ * Returns the internal window reference.
13919
+ *
13920
+ * @returns {Window|null}
13921
+ */
13922
+ getWin() {
13923
+ return this.#windowRef;
13924
+ }
13925
+
13926
+ /**
13927
+ * Internal message handler.
13928
+ *
13929
+ * @param {MessageEvent} event
13930
+ * @returns {void}
13931
+ */
13932
+ #handleMessage(event) {
13933
+ if (!event.source || (this.#windowRef && event.source !== this.#windowRef)) return;
13934
+ const { type, route, payload } = event.data || {};
13935
+
13936
+ if (type === this.#readyEventName) {
13937
+ this.#ready = true;
13938
+ this.#flushQueue();
13939
+ this.#startCloseWatcher(); // start watcher after handshake (for child window)
13940
+ if (this.#isHost) this.#postRaw(this.#readyEventName, null);
13941
+ return;
13942
+ }
13943
+
13944
+ if (type === this.#routeEventName) this.#events.emit(route, payload, event);
13945
+ }
13946
+
13947
+ /**
13948
+ * Sends all pending messages queued before handshake completion.
13949
+ *
13950
+ * @returns {void}
13951
+ */
13952
+ #flushQueue() {
13953
+ while (this.#pendingQueue.length) {
13954
+ const data = this.#pendingQueue.shift();
13955
+ if (data) {
13956
+ const { route, payload } = data;
13957
+ this.emit(route, payload);
13958
+ }
13959
+ }
13960
+ }
13961
+
13962
+ /**
13963
+ * Sends a raw postMessage with given type and payload.
13964
+ *
13965
+ * @param {string} type Internal message type
13966
+ * @param {any} payload Data to send
13967
+ * @param {string} [route=''] Optional route name
13968
+ * @returns {void}
13969
+ */
13970
+ #postRaw(type, payload, route = '') {
13971
+ if (this.#windowRef && this.#windowRef.closed) return;
13972
+ this.#windowRef?.postMessage({ type, route, payload }, this.#targetOrigin);
13973
+ }
13974
+
13975
+ /**
13976
+ * Closes the child window (only allowed from the host).
13977
+ *
13978
+ * @returns {void}
13979
+ */
13980
+ close() {
13981
+ if (!this.#isHost) throw new Error('Only host can close the window.');
13982
+ if (this.#windowRef && !this.#windowRef.closed) this.#windowRef.close();
13983
+ }
13984
+
13985
+ /**
13986
+ * Emits a message to the other window on a specific route.
13987
+ * If the handshake is not yet complete, the message is queued.
13988
+ * Throws an error if the instance has already been destroyed.
13989
+ *
13990
+ * @param {string} route - Route name used to identify the message handler.
13991
+ * @param {any} payload - Data to send along with the message.
13992
+ * @throws {Error} If the instance is already destroyed.
13993
+ * @returns {void}
13994
+ */
13995
+ emit(route, payload) {
13996
+ if (typeof route !== 'string') throw new TypeError('Event name must be a string.');
13997
+ if (this.isDestroyed()) throw new Error('Cannot emit: instance has been destroyed.');
13998
+ if (!this.#ready) {
13999
+ this.#pendingQueue.push({ route, payload });
14000
+ return;
14001
+ }
14002
+ this.#postRaw(this.#routeEventName, payload, route);
14003
+ }
14004
+
14005
+ /**
14006
+ * Checks if the connection is active and the window is still open.
14007
+ *
14008
+ * @returns {boolean}
14009
+ */
14010
+ isConnected() {
14011
+ return this.#ready && this.#windowRef && !this.#windowRef.closed ? true : false;
14012
+ }
14013
+
14014
+ /**
14015
+ * Starts polling to detect when the window is closed.
14016
+ *
14017
+ * @returns {void}
14018
+ */
14019
+ #startCloseWatcher() {
14020
+ if (!this.#windowRef || this.#pollClosedInterval) return;
14021
+ this.#pollClosedInterval = setInterval(() => {
14022
+ if (this.#windowRef?.closed) {
14023
+ this.#events.emit('WINDOW_REF_CLOSED');
14024
+ this.destroy();
14025
+ }
14026
+ }, 500);
14027
+ pollClosedInterval.set(this.#windowRef, this.#pollClosedInterval);
14028
+ }
14029
+
14030
+ /**
14031
+ * Registers a callback for when the window is closed.
14032
+ *
14033
+ * @param {handler} callback Callback to run on close
14034
+ * @returns {void}
14035
+ */
14036
+ onClose(callback) {
14037
+ return this.#events.on('WINDOW_REF_CLOSED', callback);
14038
+ }
14039
+
14040
+ /**
14041
+ * Unregisters a previously registered close callback.
14042
+ *
14043
+ * @param {handler} callback Callback to remove
14044
+ * @returns {void}
14045
+ */
14046
+ offClose(callback) {
14047
+ return this.#events.off('WINDOW_REF_CLOSED', callback);
14048
+ }
14049
+
14050
+ /**
14051
+ * Checks if the communication instance has been destroyed.
14052
+ *
14053
+ * @returns {boolean}
14054
+ */
14055
+ isDestroyed() {
14056
+ return !this.#windowRef;
14057
+ }
14058
+
14059
+ /**
14060
+ * Destroys the communication instance, cleaning up all resources and listeners.
14061
+ *
14062
+ * @returns {void}
14063
+ */
14064
+ destroy() {
14065
+ if (!this.#windowRef) return;
14066
+ if (this.#pollClosedInterval) {
14067
+ clearInterval(this.#pollClosedInterval);
14068
+ this.#pollClosedInterval = null;
14069
+ pollClosedInterval.delete(this.#windowRef);
14070
+ }
14071
+ window.removeEventListener('message', this._handleMessage);
14072
+ this.#pendingQueue = [];
14073
+ this.#ready = false;
14074
+ this.#windowRef = null;
14075
+ this.#events.offAllTypes();
14076
+ }
14077
+ }
14078
+
14079
+ /* harmony default export */ const libs_TinyNewWinEvents = ((/* unused pure expression or super */ null && (TinyNewWinEvents)));
14080
+
13186
14081
  ;// ./src/v1/index.mjs
13187
14082
 
13188
14083
 
@@ -13221,6 +14116,8 @@ TinyLocalStorage.registerJsonType(
13221
14116
 
13222
14117
 
13223
14118
 
14119
+
14120
+
13224
14121
 
13225
14122
 
13226
14123
 
@@ -13299,7 +14196,7 @@ class TinyUploadClicker {
13299
14196
  * @throws {TypeError} If the config is invalid or required options are missing.
13300
14197
  */
13301
14198
  constructor(options) {
13302
- if (!isJsonObject(options))
14199
+ if (!objChecker_isJsonObject(options))
13303
14200
  throw new TypeError('TinyUploadClicker: "options" must be a valid object.');
13304
14201
 
13305
14202
  this.#config = {
@@ -13365,10 +14262,10 @@ class TinyUploadClicker {
13365
14262
  )
13366
14263
  throw new TypeError('TinyUploadClicker: "onFileLoad" must be a function or null.');
13367
14264
 
13368
- if (options.inputAttributes !== undefined && !isJsonObject(options.inputAttributes))
14265
+ if (options.inputAttributes !== undefined && !objChecker_isJsonObject(options.inputAttributes))
13369
14266
  throw new TypeError('TinyUploadClicker: "inputAttributes" must be an object.');
13370
14267
 
13371
- if (options.inputStyles !== undefined && !isJsonObject(options.inputStyles))
14268
+ if (options.inputStyles !== undefined && !objChecker_isJsonObject(options.inputStyles))
13372
14269
  throw new TypeError('TinyUploadClicker: "inputStyles" must be an object.');
13373
14270
 
13374
14271
  this.#boundClick = this.#handleClick.bind(this);