tiny-essentials 1.19.2 → 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.
- package/dist/v1/TinyEssentials.js +1009 -22
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyIframeEvents.js +854 -0
- package/dist/v1/TinyIframeEvents.min.js +1 -0
- package/dist/v1/TinyLocalStorage.js +109 -21
- package/dist/v1/TinyLocalStorage.min.js +1 -1
- package/dist/v1/TinyNewWinEvents.js +888 -0
- package/dist/v1/TinyNewWinEvents.min.js +1 -0
- package/dist/v1/TinyUploadClicker.js +1022 -37
- package/dist/v1/TinyUploadClicker.min.js +1 -1
- package/dist/v1/build/TinyIframeEvents.cjs +7 -0
- package/dist/v1/build/TinyIframeEvents.d.mts +3 -0
- package/dist/v1/build/TinyIframeEvents.mjs +2 -0
- package/dist/v1/build/TinyNewWinEvents.cjs +7 -0
- package/dist/v1/build/TinyNewWinEvents.d.mts +3 -0
- package/dist/v1/build/TinyNewWinEvents.mjs +2 -0
- package/dist/v1/index.cjs +4 -0
- package/dist/v1/index.d.mts +3 -1
- package/dist/v1/index.mjs +3 -1
- package/dist/v1/libs/TinyIframeEvents.cjs +409 -0
- package/dist/v1/libs/TinyIframeEvents.d.mts +193 -0
- package/dist/v1/libs/TinyIframeEvents.mjs +348 -0
- package/dist/v1/libs/TinyLocalStorage.cjs +109 -21
- package/dist/v1/libs/TinyLocalStorage.d.mts +29 -0
- package/dist/v1/libs/TinyLocalStorage.mjs +97 -21
- package/dist/v1/libs/TinyNewWinEvents.cjs +486 -0
- package/dist/v1/libs/TinyNewWinEvents.d.mts +241 -0
- package/dist/v1/libs/TinyNewWinEvents.mjs +437 -0
- package/docs/v1/README.md +2 -0
- package/docs/v1/libs/TinyIframeEvents.md +149 -0
- package/docs/v1/libs/TinyLocalStorage.md +53 -0
- package/docs/v1/libs/TinyNewWinEvents.md +186 -0
- package/docs/v1/libs/TinyNotifyCenter.md +1 -1
- package/package.json +1 -1
|
@@ -2894,8 +2894,10 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
2894
2894
|
TinyDragger: () => (/* reexport */ libs_TinyDragger),
|
|
2895
2895
|
TinyEvents: () => (/* reexport */ libs_TinyEvents),
|
|
2896
2896
|
TinyHtml: () => (/* reexport */ libs_TinyHtml),
|
|
2897
|
+
TinyIframeEvents: () => (/* reexport */ libs_TinyIframeEvents),
|
|
2897
2898
|
TinyLevelUp: () => (/* reexport */ userLevel),
|
|
2898
2899
|
TinyLocalStorage: () => (/* reexport */ libs_TinyLocalStorage),
|
|
2900
|
+
TinyNewWinEvents: () => (/* reexport */ libs_TinyNewWinEvents),
|
|
2899
2901
|
TinyNotifications: () => (/* reexport */ libs_TinyNotifications),
|
|
2900
2902
|
TinyNotifyCenter: () => (/* reexport */ libs_TinyNotifyCenter),
|
|
2901
2903
|
TinyPromiseQueue: () => (/* reexport */ libs_TinyPromiseQueue),
|
|
@@ -19272,6 +19274,12 @@ class TinyLocalStorage {
|
|
|
19272
19274
|
/** @type {Storage} */
|
|
19273
19275
|
#localStorage = window.localStorage;
|
|
19274
19276
|
|
|
19277
|
+
/** @type {string|null} */
|
|
19278
|
+
#dbKey = null;
|
|
19279
|
+
|
|
19280
|
+
/** @type {number} */
|
|
19281
|
+
#version = 0;
|
|
19282
|
+
|
|
19275
19283
|
/** @type {(ev: StorageEvent) => any} */
|
|
19276
19284
|
#storageEvent = (ev) => this.emit('storage', ev);
|
|
19277
19285
|
|
|
@@ -19279,11 +19287,84 @@ class TinyLocalStorage {
|
|
|
19279
19287
|
* Initializes the TinyLocalStorage instance and sets up cross-tab sync.
|
|
19280
19288
|
*
|
|
19281
19289
|
* Adds listener for the native `storage` event to support tab synchronization.
|
|
19290
|
+
* @param {string} [dbName] - Unique database name.
|
|
19282
19291
|
*/
|
|
19283
|
-
constructor() {
|
|
19292
|
+
constructor(dbName) {
|
|
19293
|
+
if (typeof dbName !== 'undefined' && typeof dbName !== 'string')
|
|
19294
|
+
throw new TypeError('TinyLocalStorage: dbName must be a string if provided.');
|
|
19295
|
+
if (typeof dbName === 'string') this.#dbKey = `LSDB::${dbName}`;
|
|
19284
19296
|
window.addEventListener('storage', this.#storageEvent);
|
|
19285
19297
|
}
|
|
19286
19298
|
|
|
19299
|
+
/**
|
|
19300
|
+
* Validates that a given key does not conflict with internal database keys.
|
|
19301
|
+
*
|
|
19302
|
+
* This method is used to prevent accidental overwriting of reserved `LSDB::` keys
|
|
19303
|
+
* in `localStorage`, which are used internally by TinyLocalStorage for versioning
|
|
19304
|
+
* and data management.
|
|
19305
|
+
*
|
|
19306
|
+
* @param {string} name - The key to validate before writing to localStorage.
|
|
19307
|
+
* @throws {Error} If the key starts with `LSDB::`.
|
|
19308
|
+
*/
|
|
19309
|
+
#isProtectedDbKey(name) {
|
|
19310
|
+
if (typeof name === 'string' && name.startsWith('LSDB::'))
|
|
19311
|
+
throw new Error(`TinyLocalStorage: Key "${name}" may conflict with reserved dbKeys.`);
|
|
19312
|
+
}
|
|
19313
|
+
|
|
19314
|
+
/**
|
|
19315
|
+
* Updates the version of the storage and triggers migration if needed.
|
|
19316
|
+
*
|
|
19317
|
+
* @param {number} version - Desired version of the database.
|
|
19318
|
+
* @param {(oldVersion: number, newVersion: number) => void} onUpgrade - Callback to perform migration logic.
|
|
19319
|
+
* @throws {Error} If the database key has not been initialized.
|
|
19320
|
+
* @throws {TypeError} If `version` is not a valid positive number.
|
|
19321
|
+
* @throws {TypeError} If `onUpgrade` is not a function.
|
|
19322
|
+
*/
|
|
19323
|
+
updateStorageVersion(version, onUpgrade) {
|
|
19324
|
+
if (typeof this.#dbKey !== 'string')
|
|
19325
|
+
throw new Error(
|
|
19326
|
+
'TinyLocalStorage: Database key is not initialized. Set a valid dbName in the constructor.',
|
|
19327
|
+
);
|
|
19328
|
+
if (typeof version !== 'number' || Number.isNaN(version) || version < 1)
|
|
19329
|
+
throw new TypeError('TinyLocalStorage: version must be a positive number.');
|
|
19330
|
+
if (typeof onUpgrade !== 'function')
|
|
19331
|
+
throw new TypeError('TinyLocalStorage: onUpgrade must be a function.');
|
|
19332
|
+
|
|
19333
|
+
// @ts-ignore
|
|
19334
|
+
const savedVersion = parseInt(localStorage.getItem(this.#dbKey), 10) || 0;
|
|
19335
|
+
if (typeof savedVersion !== 'number' || Number.isNaN(savedVersion) || savedVersion < 0)
|
|
19336
|
+
throw new TypeError('TinyLocalStorage: saved version in localStorage is not a valid number.');
|
|
19337
|
+
|
|
19338
|
+
if (version < savedVersion)
|
|
19339
|
+
throw new Error(
|
|
19340
|
+
`TinyLocalStorage: Cannot downgrade database version from ${savedVersion} to ${version}.`,
|
|
19341
|
+
);
|
|
19342
|
+
|
|
19343
|
+
if (version > savedVersion) {
|
|
19344
|
+
onUpgrade(savedVersion, version);
|
|
19345
|
+
localStorage.setItem(this.#dbKey, String(version));
|
|
19346
|
+
this.#version = version;
|
|
19347
|
+
}
|
|
19348
|
+
}
|
|
19349
|
+
|
|
19350
|
+
/**
|
|
19351
|
+
* Gets the current database key used in localStorage.
|
|
19352
|
+
*
|
|
19353
|
+
* @returns {string|null} The database key, or null if not set.
|
|
19354
|
+
*/
|
|
19355
|
+
getDbKey() {
|
|
19356
|
+
return this.#dbKey;
|
|
19357
|
+
}
|
|
19358
|
+
|
|
19359
|
+
/**
|
|
19360
|
+
* Gets the current version of the database.
|
|
19361
|
+
*
|
|
19362
|
+
* @returns {number} The current version number.
|
|
19363
|
+
*/
|
|
19364
|
+
getVersion() {
|
|
19365
|
+
return this.#version;
|
|
19366
|
+
}
|
|
19367
|
+
|
|
19287
19368
|
/**
|
|
19288
19369
|
* Defines a custom storage interface (e.g. `sessionStorage`).
|
|
19289
19370
|
*
|
|
@@ -19291,7 +19372,7 @@ class TinyLocalStorage {
|
|
|
19291
19372
|
*/
|
|
19292
19373
|
setLocalStorage(localstorage) {
|
|
19293
19374
|
if (!(localstorage instanceof Storage))
|
|
19294
|
-
throw new
|
|
19375
|
+
throw new TypeError('Argument must be a valid instance of Storage.');
|
|
19295
19376
|
this.#localStorage = localstorage;
|
|
19296
19377
|
}
|
|
19297
19378
|
|
|
@@ -19313,7 +19394,7 @@ class TinyLocalStorage {
|
|
|
19313
19394
|
*/
|
|
19314
19395
|
#setJson(name, data) {
|
|
19315
19396
|
if (typeof name !== 'string' || !name.length)
|
|
19316
|
-
throw new
|
|
19397
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
19317
19398
|
return TinyLocalStorage.encodeSpecialJson(data);
|
|
19318
19399
|
}
|
|
19319
19400
|
|
|
@@ -19326,13 +19407,14 @@ class TinyLocalStorage {
|
|
|
19326
19407
|
* @param {LocalStorageJsonValue} data - The data to be serialized and stored.
|
|
19327
19408
|
*/
|
|
19328
19409
|
setJson(name, data) {
|
|
19410
|
+
this.#isProtectedDbKey(name);
|
|
19329
19411
|
if (
|
|
19330
19412
|
!isJsonObject(data) &&
|
|
19331
19413
|
!Array.isArray(data) &&
|
|
19332
19414
|
!(data instanceof Map) &&
|
|
19333
19415
|
!(data instanceof Set)
|
|
19334
19416
|
) {
|
|
19335
|
-
throw new
|
|
19417
|
+
throw new TypeError('The storage value is not a valid JSON-compatible structure.');
|
|
19336
19418
|
}
|
|
19337
19419
|
const encoded = this.#setJson(name, data);
|
|
19338
19420
|
this.emit('setJson', name, data);
|
|
@@ -19350,7 +19432,7 @@ class TinyLocalStorage {
|
|
|
19350
19432
|
*/
|
|
19351
19433
|
#getJson(name, defaultData) {
|
|
19352
19434
|
if (typeof name !== 'string' || !name.length)
|
|
19353
|
-
throw new
|
|
19435
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
19354
19436
|
|
|
19355
19437
|
const raw = this.#localStorage.getItem(name);
|
|
19356
19438
|
const fallbackTypes = {
|
|
@@ -19404,7 +19486,8 @@ class TinyLocalStorage {
|
|
|
19404
19486
|
* @param {Date} data
|
|
19405
19487
|
*/
|
|
19406
19488
|
setDate(name, data) {
|
|
19407
|
-
|
|
19489
|
+
this.#isProtectedDbKey(name);
|
|
19490
|
+
if (!(data instanceof Date)) throw new TypeError('Value must be a Date.');
|
|
19408
19491
|
const encoded = this.#setJson(name, data);
|
|
19409
19492
|
this.emit('setDate', name, data);
|
|
19410
19493
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -19426,7 +19509,8 @@ class TinyLocalStorage {
|
|
|
19426
19509
|
* @param {RegExp} data
|
|
19427
19510
|
*/
|
|
19428
19511
|
setRegExp(name, data) {
|
|
19429
|
-
|
|
19512
|
+
this.#isProtectedDbKey(name);
|
|
19513
|
+
if (!(data instanceof RegExp)) throw new TypeError('Value must be a RegExp.');
|
|
19430
19514
|
const encoded = this.#setJson(name, data);
|
|
19431
19515
|
this.emit('setRegExp', name, data);
|
|
19432
19516
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -19448,7 +19532,8 @@ class TinyLocalStorage {
|
|
|
19448
19532
|
* @param {bigint} data
|
|
19449
19533
|
*/
|
|
19450
19534
|
setBigInt(name, data) {
|
|
19451
|
-
|
|
19535
|
+
this.#isProtectedDbKey(name);
|
|
19536
|
+
if (typeof data !== 'bigint') throw new TypeError('Value must be a BigInt.');
|
|
19452
19537
|
const encoded = this.#setJson(name, data);
|
|
19453
19538
|
this.emit('setBigInt', name, data);
|
|
19454
19539
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -19471,7 +19556,8 @@ class TinyLocalStorage {
|
|
|
19471
19556
|
* @param {symbol} data
|
|
19472
19557
|
*/
|
|
19473
19558
|
setSymbol(name, data) {
|
|
19474
|
-
|
|
19559
|
+
this.#isProtectedDbKey(name);
|
|
19560
|
+
if (typeof data !== 'symbol') throw new TypeError('Value must be a Symbol.');
|
|
19475
19561
|
const encoded = this.#setJson(name, data);
|
|
19476
19562
|
this.emit('setSymbol', name, data);
|
|
19477
19563
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -19504,8 +19590,9 @@ class TinyLocalStorage {
|
|
|
19504
19590
|
* @param {any} data - The data to store.
|
|
19505
19591
|
*/
|
|
19506
19592
|
setItem(name, data) {
|
|
19593
|
+
this.#isProtectedDbKey(name);
|
|
19507
19594
|
if (typeof name !== 'string' || !name.length)
|
|
19508
|
-
throw new
|
|
19595
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
19509
19596
|
this.emit('setItem', name, data);
|
|
19510
19597
|
return this.#localStorage.setItem(name, data);
|
|
19511
19598
|
}
|
|
@@ -19518,7 +19605,7 @@ class TinyLocalStorage {
|
|
|
19518
19605
|
*/
|
|
19519
19606
|
getItem(name) {
|
|
19520
19607
|
if (typeof name !== 'string' || !name.length)
|
|
19521
|
-
throw new
|
|
19608
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
19522
19609
|
return this.#localStorage.getItem(name);
|
|
19523
19610
|
}
|
|
19524
19611
|
|
|
@@ -19529,9 +19616,10 @@ class TinyLocalStorage {
|
|
|
19529
19616
|
* @param {string} data - The string to store.
|
|
19530
19617
|
*/
|
|
19531
19618
|
setString(name, data) {
|
|
19619
|
+
this.#isProtectedDbKey(name);
|
|
19532
19620
|
if (typeof name !== 'string' || !name.length)
|
|
19533
|
-
throw new
|
|
19534
|
-
if (typeof data !== 'string') throw new
|
|
19621
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
19622
|
+
if (typeof data !== 'string') throw new TypeError('Value must be a string.');
|
|
19535
19623
|
|
|
19536
19624
|
this.emit('setString', name, data);
|
|
19537
19625
|
return this.#localStorage.setItem(
|
|
@@ -19551,7 +19639,7 @@ class TinyLocalStorage {
|
|
|
19551
19639
|
*/
|
|
19552
19640
|
getString(name) {
|
|
19553
19641
|
if (typeof name !== 'string' || !name.length)
|
|
19554
|
-
throw new
|
|
19642
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
19555
19643
|
let value = this.#localStorage.getItem(name);
|
|
19556
19644
|
try {
|
|
19557
19645
|
/** @type {{ value: string; __string__: boolean }} */
|
|
@@ -19573,9 +19661,10 @@ class TinyLocalStorage {
|
|
|
19573
19661
|
* @param {number} data - The number to store.
|
|
19574
19662
|
*/
|
|
19575
19663
|
setNumber(name, data) {
|
|
19664
|
+
this.#isProtectedDbKey(name);
|
|
19576
19665
|
if (typeof name !== 'string' || !name.length)
|
|
19577
|
-
throw new
|
|
19578
|
-
if (typeof data !== 'number') throw new
|
|
19666
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
19667
|
+
if (typeof data !== 'number') throw new TypeError('Value must be a number.');
|
|
19579
19668
|
this.emit('setNumber', name, data);
|
|
19580
19669
|
return this.#localStorage.setItem(name, String(data));
|
|
19581
19670
|
}
|
|
@@ -19588,7 +19677,7 @@ class TinyLocalStorage {
|
|
|
19588
19677
|
*/
|
|
19589
19678
|
getNumber(name) {
|
|
19590
19679
|
if (typeof name !== 'string' || !name.length)
|
|
19591
|
-
throw new
|
|
19680
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
19592
19681
|
|
|
19593
19682
|
/** @type {number|string|null} */
|
|
19594
19683
|
let number = this.#localStorage.getItem(name);
|
|
@@ -19607,9 +19696,10 @@ class TinyLocalStorage {
|
|
|
19607
19696
|
* @param {boolean} data - The boolean value to store.
|
|
19608
19697
|
*/
|
|
19609
19698
|
setBool(name, data) {
|
|
19699
|
+
this.#isProtectedDbKey(name);
|
|
19610
19700
|
if (typeof name !== 'string' || !name.length)
|
|
19611
|
-
throw new
|
|
19612
|
-
if (typeof data !== 'boolean') throw new
|
|
19701
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
19702
|
+
if (typeof data !== 'boolean') throw new TypeError('Value must be a boolean.');
|
|
19613
19703
|
this.emit('setBool', name, data);
|
|
19614
19704
|
return this.#localStorage.setItem(name, String(data));
|
|
19615
19705
|
}
|
|
@@ -19622,7 +19712,7 @@ class TinyLocalStorage {
|
|
|
19622
19712
|
*/
|
|
19623
19713
|
getBool(name) {
|
|
19624
19714
|
if (typeof name !== 'string' || !name.length)
|
|
19625
|
-
throw new
|
|
19715
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
19626
19716
|
|
|
19627
19717
|
const value = this.#localStorage.getItem(name);
|
|
19628
19718
|
if (typeof value === 'boolean') return value;
|
|
@@ -19641,7 +19731,7 @@ class TinyLocalStorage {
|
|
|
19641
19731
|
*/
|
|
19642
19732
|
removeItem(name) {
|
|
19643
19733
|
if (typeof name !== 'string' || !name.length)
|
|
19644
|
-
throw new
|
|
19734
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
19645
19735
|
|
|
19646
19736
|
this.emit('removeItem', name);
|
|
19647
19737
|
return this.#localStorage.removeItem(name);
|
|
@@ -19757,7 +19847,904 @@ TinyLocalStorage.registerJsonType(
|
|
|
19757
19847
|
|
|
19758
19848
|
/* harmony default export */ const libs_TinyLocalStorage = (TinyLocalStorage);
|
|
19759
19849
|
|
|
19760
|
-
;// ./src/v1/
|
|
19850
|
+
;// ./src/v1/libs/TinyIframeEvents.mjs
|
|
19851
|
+
|
|
19852
|
+
|
|
19853
|
+
|
|
19854
|
+
/** @type {WeakMap<Window, TinyIframeEvents>} */
|
|
19855
|
+
const instances = new WeakMap();
|
|
19856
|
+
|
|
19857
|
+
/**
|
|
19858
|
+
* @callback handler
|
|
19859
|
+
* A function to handle incoming event payloads.
|
|
19860
|
+
* @param {any} payload - The data sent by the emitter.
|
|
19861
|
+
* @param {MessageEvent<any>} event - Metadata about the message.
|
|
19862
|
+
*/
|
|
19863
|
+
|
|
19864
|
+
/**
|
|
19865
|
+
* A flexible event routing system for structured communication
|
|
19866
|
+
* between a parent window and its iframe using `postMessage`.
|
|
19867
|
+
*
|
|
19868
|
+
* This class abstracts the complexity of cross-origin and window-type handling,
|
|
19869
|
+
* allowing both the iframe and parent to:
|
|
19870
|
+
* - Send events with arbitrary payloads
|
|
19871
|
+
* - Listen to specific event names
|
|
19872
|
+
* - Filter events by origin and source
|
|
19873
|
+
* - Work symmetrically from both sides with automatic direction handling
|
|
19874
|
+
*
|
|
19875
|
+
* Use this class when building applications that require modular, event-driven
|
|
19876
|
+
* communication across embedded frames.
|
|
19877
|
+
*/
|
|
19878
|
+
class TinyIframeEvents {
|
|
19879
|
+
#events = new libs_TinyEvents();
|
|
19880
|
+
|
|
19881
|
+
/**
|
|
19882
|
+
* Enables or disables throwing an error when the maximum number of listeners is exceeded.
|
|
19883
|
+
*
|
|
19884
|
+
* @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
|
|
19885
|
+
*/
|
|
19886
|
+
setThrowOnMaxListeners(shouldThrow) {
|
|
19887
|
+
return this.#events.setThrowOnMaxListeners(shouldThrow);
|
|
19888
|
+
}
|
|
19889
|
+
|
|
19890
|
+
/**
|
|
19891
|
+
* Checks whether an error will be thrown when the max listener limit is exceeded.
|
|
19892
|
+
*
|
|
19893
|
+
* @returns {boolean} True if an error will be thrown, false if only a warning is shown.
|
|
19894
|
+
*/
|
|
19895
|
+
getThrowOnMaxListeners() {
|
|
19896
|
+
return this.#events.getThrowOnMaxListeners();
|
|
19897
|
+
}
|
|
19898
|
+
|
|
19899
|
+
/////////////////////////////////////////////////////////////
|
|
19900
|
+
|
|
19901
|
+
/**
|
|
19902
|
+
* Adds a listener to the beginning of the listeners array for the specified event.
|
|
19903
|
+
*
|
|
19904
|
+
* @param {string} event - Event name.
|
|
19905
|
+
* @param {handler} handler - The callback function.
|
|
19906
|
+
*/
|
|
19907
|
+
prependListener(event, handler) {
|
|
19908
|
+
return this.#events.prependListener(event, handler);
|
|
19909
|
+
}
|
|
19910
|
+
|
|
19911
|
+
/**
|
|
19912
|
+
* Adds a one-time listener to the beginning of the listeners array for the specified event.
|
|
19913
|
+
*
|
|
19914
|
+
* @param {string} event - Event name.
|
|
19915
|
+
* @param {handler} handler - The callback function.
|
|
19916
|
+
* @returns {handler} - The wrapped handler used internally.
|
|
19917
|
+
*/
|
|
19918
|
+
prependListenerOnce(event, handler) {
|
|
19919
|
+
return this.#events.prependListenerOnce(event, handler);
|
|
19920
|
+
}
|
|
19921
|
+
|
|
19922
|
+
//////////////////////////////////////////////////////////////////////
|
|
19923
|
+
|
|
19924
|
+
/**
|
|
19925
|
+
* Adds a event listener.
|
|
19926
|
+
*
|
|
19927
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
19928
|
+
* @param {handler} handler - Callback function to be called when event fires.
|
|
19929
|
+
*/
|
|
19930
|
+
appendListener(event, handler) {
|
|
19931
|
+
return this.#events.appendListener(event, handler);
|
|
19932
|
+
}
|
|
19933
|
+
|
|
19934
|
+
/**
|
|
19935
|
+
* Registers an event listener that runs only once, then is removed.
|
|
19936
|
+
*
|
|
19937
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
19938
|
+
* @param {handler} handler - The callback function to run on event.
|
|
19939
|
+
* @returns {handler} - The wrapped version of the handler.
|
|
19940
|
+
*/
|
|
19941
|
+
appendListenerOnce(event, handler) {
|
|
19942
|
+
return this.#events.appendListenerOnce(event, handler);
|
|
19943
|
+
}
|
|
19944
|
+
|
|
19945
|
+
/**
|
|
19946
|
+
* Adds a event listener.
|
|
19947
|
+
*
|
|
19948
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
19949
|
+
* @param {handler} handler - Callback function to be called when event fires.
|
|
19950
|
+
*/
|
|
19951
|
+
on(event, handler) {
|
|
19952
|
+
return this.#events.on(event, handler);
|
|
19953
|
+
}
|
|
19954
|
+
|
|
19955
|
+
/**
|
|
19956
|
+
* Registers an event listener that runs only once, then is removed.
|
|
19957
|
+
*
|
|
19958
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
19959
|
+
* @param {handler} handler - The callback function to run on event.
|
|
19960
|
+
* @returns {handler} - The wrapped version of the handler.
|
|
19961
|
+
*/
|
|
19962
|
+
once(event, handler) {
|
|
19963
|
+
return this.#events.once(event, handler);
|
|
19964
|
+
}
|
|
19965
|
+
|
|
19966
|
+
////////////////////////////////////////////////////////////////////
|
|
19967
|
+
|
|
19968
|
+
/**
|
|
19969
|
+
* Removes a previously registered event listener.
|
|
19970
|
+
*
|
|
19971
|
+
* @param {string} event - The name of the event to remove the handler from.
|
|
19972
|
+
* @param {handler} handler - The specific callback function to remove.
|
|
19973
|
+
*/
|
|
19974
|
+
off(event, handler) {
|
|
19975
|
+
return this.#events.off(event, handler);
|
|
19976
|
+
}
|
|
19977
|
+
|
|
19978
|
+
/**
|
|
19979
|
+
* Removes all event listeners of a specific type from the element.
|
|
19980
|
+
*
|
|
19981
|
+
* @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
|
|
19982
|
+
*/
|
|
19983
|
+
offAll(event) {
|
|
19984
|
+
return this.#events.offAll(event);
|
|
19985
|
+
}
|
|
19986
|
+
|
|
19987
|
+
/**
|
|
19988
|
+
* Removes all event listeners of all types from the element.
|
|
19989
|
+
*/
|
|
19990
|
+
offAllTypes() {
|
|
19991
|
+
return this.#events.offAllTypes();
|
|
19992
|
+
}
|
|
19993
|
+
|
|
19994
|
+
////////////////////////////////////////////////////////////
|
|
19995
|
+
|
|
19996
|
+
/**
|
|
19997
|
+
* Returns the number of listeners for a given event.
|
|
19998
|
+
*
|
|
19999
|
+
* @param {string} event - The name of the event.
|
|
20000
|
+
* @returns {number} Number of listeners for the event.
|
|
20001
|
+
*/
|
|
20002
|
+
listenerCount(event) {
|
|
20003
|
+
return this.#events.listenerCount(event);
|
|
20004
|
+
}
|
|
20005
|
+
|
|
20006
|
+
/**
|
|
20007
|
+
* Returns a copy of the array of listeners for the specified event.
|
|
20008
|
+
*
|
|
20009
|
+
* @param {string} event - The name of the event.
|
|
20010
|
+
* @returns {handler[]} Array of listener functions.
|
|
20011
|
+
*/
|
|
20012
|
+
listeners(event) {
|
|
20013
|
+
return this.#events.listeners(event);
|
|
20014
|
+
}
|
|
20015
|
+
|
|
20016
|
+
/**
|
|
20017
|
+
* Returns a copy of the array of listeners for the specified event.
|
|
20018
|
+
*
|
|
20019
|
+
* @param {string} event - The name of the event.
|
|
20020
|
+
* @returns {handler[]} Array of listener functions.
|
|
20021
|
+
*/
|
|
20022
|
+
onceListeners(event) {
|
|
20023
|
+
return this.#events.onceListeners(event);
|
|
20024
|
+
}
|
|
20025
|
+
|
|
20026
|
+
/**
|
|
20027
|
+
* Returns a copy of the internal listeners array for the specified event,
|
|
20028
|
+
* including wrapper functions like those used by `.once()`.
|
|
20029
|
+
* @param {string | symbol} event - The event name.
|
|
20030
|
+
* @returns {handler[]} An array of raw listener functions.
|
|
20031
|
+
*/
|
|
20032
|
+
allListeners(event) {
|
|
20033
|
+
return this.#events.allListeners(event);
|
|
20034
|
+
}
|
|
20035
|
+
|
|
20036
|
+
/**
|
|
20037
|
+
* Returns an array of event names for which there are registered listeners.
|
|
20038
|
+
*
|
|
20039
|
+
* @returns {string[]} Array of registered event names.
|
|
20040
|
+
*/
|
|
20041
|
+
eventNames() {
|
|
20042
|
+
return this.#events.eventNames();
|
|
20043
|
+
}
|
|
20044
|
+
|
|
20045
|
+
//////////////////////////////////////////////////////
|
|
20046
|
+
|
|
20047
|
+
/**
|
|
20048
|
+
* Sets the maximum number of listeners per event before a warning is shown.
|
|
20049
|
+
*
|
|
20050
|
+
* @param {number} n - The maximum number of listeners.
|
|
20051
|
+
*/
|
|
20052
|
+
setMaxListeners(n) {
|
|
20053
|
+
return this.#events.setMaxListeners(n);
|
|
20054
|
+
}
|
|
20055
|
+
|
|
20056
|
+
/**
|
|
20057
|
+
* Gets the maximum number of listeners allowed per event.
|
|
20058
|
+
*
|
|
20059
|
+
* @returns {number} The maximum number of listeners.
|
|
20060
|
+
*/
|
|
20061
|
+
getMaxListeners() {
|
|
20062
|
+
return this.#events.getMaxListeners();
|
|
20063
|
+
}
|
|
20064
|
+
|
|
20065
|
+
///////////////////////////////////////////////////
|
|
20066
|
+
|
|
20067
|
+
/** @type {Window} */
|
|
20068
|
+
#targetWindow;
|
|
20069
|
+
|
|
20070
|
+
/** @type {string} */
|
|
20071
|
+
#targetOrigin;
|
|
20072
|
+
|
|
20073
|
+
/** @type {string} */
|
|
20074
|
+
#selfType;
|
|
20075
|
+
|
|
20076
|
+
/** @type {boolean} */
|
|
20077
|
+
#isDestroyed = false;
|
|
20078
|
+
|
|
20079
|
+
/** @type {boolean} */
|
|
20080
|
+
#ready = false;
|
|
20081
|
+
|
|
20082
|
+
/**
|
|
20083
|
+
* @typedef {object} IframeEventBase
|
|
20084
|
+
* @property {string} eventName - The name of the custom event route.
|
|
20085
|
+
* @property {any} payload - The data being sent (can be any type).
|
|
20086
|
+
* @property {'iframe' | 'parent'} direction - Indicates the sender: 'iframe' or 'parent'.
|
|
20087
|
+
*/
|
|
20088
|
+
|
|
20089
|
+
/**
|
|
20090
|
+
* Queue of messages emitted before connection is ready
|
|
20091
|
+
* @type {IframeEventBase[]}
|
|
20092
|
+
*/
|
|
20093
|
+
#pendingQueue = [];
|
|
20094
|
+
|
|
20095
|
+
/** @type {string} Internal message type for routed communication */
|
|
20096
|
+
#secretEventName = '__tinyIframeEvent__';
|
|
20097
|
+
|
|
20098
|
+
/**
|
|
20099
|
+
* Gets the internal secret iframe event name.
|
|
20100
|
+
* @returns {string}
|
|
20101
|
+
*/
|
|
20102
|
+
get secretEventName() {
|
|
20103
|
+
return this.#secretEventName;
|
|
20104
|
+
}
|
|
20105
|
+
|
|
20106
|
+
/**
|
|
20107
|
+
* Sets the internal secret iframe event name.
|
|
20108
|
+
* @param {string} name
|
|
20109
|
+
* @throws {TypeError} If the value is not a string.
|
|
20110
|
+
*/
|
|
20111
|
+
set secretEventName(name) {
|
|
20112
|
+
if (typeof name !== 'string') throw new TypeError('TinyIframeEvents: secretEventName must be a string.');
|
|
20113
|
+
this.#secretEventName = name;
|
|
20114
|
+
}
|
|
20115
|
+
|
|
20116
|
+
/**
|
|
20117
|
+
* Creates a new TinyIframeEvents instance to manage communication between iframe and parent.
|
|
20118
|
+
* Automatically determines the current context (`iframe` or `parent`) based on the `targetWindow`.
|
|
20119
|
+
*
|
|
20120
|
+
* @param {Object} config - Configuration object.
|
|
20121
|
+
* @param {HTMLIFrameElement} [config.targetIframe] - The target window to post messages to. Defaults to `window.parent` (assumes this is inside an iframe).
|
|
20122
|
+
* @param {string} [config.targetOrigin] - The target origin to restrict messages to. Defaults to `window.location.origin`.
|
|
20123
|
+
*/
|
|
20124
|
+
constructor({ targetIframe, targetOrigin } = {}) {
|
|
20125
|
+
if (
|
|
20126
|
+
typeof targetIframe !== 'undefined' &&
|
|
20127
|
+
(!(targetIframe instanceof HTMLIFrameElement) || !targetIframe.contentWindow)
|
|
20128
|
+
)
|
|
20129
|
+
throw new TypeError(
|
|
20130
|
+
`[TinyIframeEvents] Invalid "targetIframe" provided: expected a HTML Iframe Element, received ${typeof targetIframe}`,
|
|
20131
|
+
);
|
|
20132
|
+
if (typeof targetOrigin !== 'undefined' && typeof targetOrigin !== 'string')
|
|
20133
|
+
throw new TypeError(
|
|
20134
|
+
`[TinyIframeEvents] Invalid "targetOrigin" provided: expected a string, received ${typeof targetOrigin}`,
|
|
20135
|
+
);
|
|
20136
|
+
|
|
20137
|
+
this.#targetWindow = targetIframe?.contentWindow ?? window.parent;
|
|
20138
|
+
this.#targetOrigin = targetOrigin ?? window.location.origin;
|
|
20139
|
+
this.#selfType = !targetIframe ? 'iframe' : 'parent';
|
|
20140
|
+
if (instances.has(this.#targetWindow)) throw new Error('Duplicate window reference.');
|
|
20141
|
+
|
|
20142
|
+
this._boundOnMessage = this.#onMessage.bind(this);
|
|
20143
|
+
this._boundOnceMessage = this.#onceMessage.bind(this);
|
|
20144
|
+
|
|
20145
|
+
if (
|
|
20146
|
+
this.#targetWindow.document.readyState === 'complete' ||
|
|
20147
|
+
this.#targetWindow.document.readyState === 'interactive'
|
|
20148
|
+
)
|
|
20149
|
+
this.#onceMessage();
|
|
20150
|
+
else {
|
|
20151
|
+
this.#targetWindow.addEventListener('load', this._boundOnceMessage, false);
|
|
20152
|
+
this.#targetWindow.addEventListener('DOMContentLoaded', this._boundOnceMessage, false);
|
|
20153
|
+
}
|
|
20154
|
+
|
|
20155
|
+
window.addEventListener('message', this._boundOnMessage, false);
|
|
20156
|
+
instances.set(this.#targetWindow, this);
|
|
20157
|
+
}
|
|
20158
|
+
|
|
20159
|
+
/**
|
|
20160
|
+
* Marks the communication as ready and flushes any queued messages.
|
|
20161
|
+
*/
|
|
20162
|
+
#onceMessage() {
|
|
20163
|
+
if (this.#ready) return;
|
|
20164
|
+
this.#ready = true;
|
|
20165
|
+
this.#flushQueue();
|
|
20166
|
+
}
|
|
20167
|
+
|
|
20168
|
+
/**
|
|
20169
|
+
* Internal handler for the message event. Filters and dispatches incoming messages.
|
|
20170
|
+
*
|
|
20171
|
+
* @param {MessageEvent<any>} event - The message event received via `postMessage`.
|
|
20172
|
+
*/
|
|
20173
|
+
#onMessage(event) {
|
|
20174
|
+
const { data, source } = event;
|
|
20175
|
+
|
|
20176
|
+
// Reject non-object or unrelated messages
|
|
20177
|
+
if (!isJsonObject(data) || !data[this.#secretEventName]) return;
|
|
20178
|
+
|
|
20179
|
+
const { eventName, payload, direction } = data;
|
|
20180
|
+
|
|
20181
|
+
// Reject if not from the expected window (for security)
|
|
20182
|
+
if (source !== this.#targetWindow) return;
|
|
20183
|
+
|
|
20184
|
+
// Reject if direction is not meant for this side
|
|
20185
|
+
if (
|
|
20186
|
+
(this.#selfType === 'iframe' && direction !== 'iframe') ||
|
|
20187
|
+
(this.#selfType === 'parent' && direction !== 'parent')
|
|
20188
|
+
)
|
|
20189
|
+
return;
|
|
20190
|
+
|
|
20191
|
+
this.#events.emit(/** @type {string} */ (eventName), payload, event);
|
|
20192
|
+
}
|
|
20193
|
+
|
|
20194
|
+
/**
|
|
20195
|
+
* Sends an event to the target window.
|
|
20196
|
+
*
|
|
20197
|
+
* @param {string} eventName - A unique name identifying the event.
|
|
20198
|
+
* @param {*} payload - The data to send with the event. Can be any serializable value.
|
|
20199
|
+
* @throws {Error} If `eventName` is not a string.
|
|
20200
|
+
*/
|
|
20201
|
+
emit(eventName, payload) {
|
|
20202
|
+
if (typeof eventName !== 'string') throw new TypeError('Event name must be a string.');
|
|
20203
|
+
if (this.#isDestroyed) throw new Error('Cannot emit: instance has been destroyed.');
|
|
20204
|
+
|
|
20205
|
+
/** @type {IframeEventBase} */
|
|
20206
|
+
const message = {
|
|
20207
|
+
[this.#secretEventName]: true,
|
|
20208
|
+
eventName,
|
|
20209
|
+
payload,
|
|
20210
|
+
direction: this.#selfType === 'parent' ? 'iframe' : 'parent',
|
|
20211
|
+
};
|
|
20212
|
+
|
|
20213
|
+
if (!this.#ready) {
|
|
20214
|
+
this.#pendingQueue.push(message);
|
|
20215
|
+
return;
|
|
20216
|
+
}
|
|
20217
|
+
|
|
20218
|
+
this.#targetWindow.postMessage(message, this.#targetOrigin);
|
|
20219
|
+
}
|
|
20220
|
+
|
|
20221
|
+
/**
|
|
20222
|
+
* Sends all pending messages queued before handshake completion.
|
|
20223
|
+
*
|
|
20224
|
+
* @returns {void}
|
|
20225
|
+
*/
|
|
20226
|
+
#flushQueue() {
|
|
20227
|
+
while (this.#pendingQueue.length) {
|
|
20228
|
+
const data = this.#pendingQueue.shift();
|
|
20229
|
+
if (data) this.#targetWindow.postMessage(data, this.#targetOrigin);
|
|
20230
|
+
}
|
|
20231
|
+
}
|
|
20232
|
+
|
|
20233
|
+
/**
|
|
20234
|
+
* Checks if the communication instance has been destroyed.
|
|
20235
|
+
*
|
|
20236
|
+
* @returns {boolean}
|
|
20237
|
+
*/
|
|
20238
|
+
isDestroyed() {
|
|
20239
|
+
return this.#isDestroyed;
|
|
20240
|
+
}
|
|
20241
|
+
|
|
20242
|
+
/**
|
|
20243
|
+
* Unsubscribes all registered event listeners and removes the message handler.
|
|
20244
|
+
* Call this when the instance is no longer needed to prevent memory leaks.
|
|
20245
|
+
*/
|
|
20246
|
+
destroy() {
|
|
20247
|
+
this.#isDestroyed = true;
|
|
20248
|
+
window.removeEventListener('message', this._boundOnMessage);
|
|
20249
|
+
this.#targetWindow.removeEventListener('load', this._boundOnceMessage, false);
|
|
20250
|
+
this.#targetWindow.removeEventListener('DOMContentLoaded', this._boundOnceMessage, false);
|
|
20251
|
+
this.#events.offAllTypes();
|
|
20252
|
+
this.#pendingQueue = [];
|
|
20253
|
+
instances.delete(this.#targetWindow);
|
|
20254
|
+
}
|
|
20255
|
+
}
|
|
20256
|
+
|
|
20257
|
+
/* harmony default export */ const libs_TinyIframeEvents = (TinyIframeEvents);
|
|
20258
|
+
|
|
20259
|
+
;// ./src/v1/libs/TinyNewWinEvents.mjs
|
|
20260
|
+
|
|
20261
|
+
|
|
20262
|
+
/**
|
|
20263
|
+
* Stores polling intervals associated with window references.
|
|
20264
|
+
* Used to detect when the window is closed.
|
|
20265
|
+
*
|
|
20266
|
+
* @type {WeakMap<Window, NodeJS.Timeout>}
|
|
20267
|
+
*/
|
|
20268
|
+
const pollClosedInterval = new WeakMap();
|
|
20269
|
+
|
|
20270
|
+
/**
|
|
20271
|
+
* @callback handler
|
|
20272
|
+
* A function to handle incoming event payloads.
|
|
20273
|
+
* @param {any} payload - The data sent by the emitter.
|
|
20274
|
+
* @param {MessageEvent<any>} event - Metadata about the message.
|
|
20275
|
+
*/
|
|
20276
|
+
|
|
20277
|
+
/**
|
|
20278
|
+
* TinyNewWinEvents provides structured communication between a main window
|
|
20279
|
+
* and a child window (created using window.open) using postMessage.
|
|
20280
|
+
*
|
|
20281
|
+
* It supports routing, queuing messages until handshake is ready,
|
|
20282
|
+
* connection status checks, and close detection.
|
|
20283
|
+
*
|
|
20284
|
+
* @class
|
|
20285
|
+
*/
|
|
20286
|
+
class TinyNewWinEvents {
|
|
20287
|
+
#events = new libs_TinyEvents();
|
|
20288
|
+
|
|
20289
|
+
/**
|
|
20290
|
+
* Enables or disables throwing an error when the maximum number of listeners is exceeded.
|
|
20291
|
+
*
|
|
20292
|
+
* @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
|
|
20293
|
+
*/
|
|
20294
|
+
setThrowOnMaxListeners(shouldThrow) {
|
|
20295
|
+
return this.#events.setThrowOnMaxListeners(shouldThrow);
|
|
20296
|
+
}
|
|
20297
|
+
|
|
20298
|
+
/**
|
|
20299
|
+
* Checks whether an error will be thrown when the max listener limit is exceeded.
|
|
20300
|
+
*
|
|
20301
|
+
* @returns {boolean} True if an error will be thrown, false if only a warning is shown.
|
|
20302
|
+
*/
|
|
20303
|
+
getThrowOnMaxListeners() {
|
|
20304
|
+
return this.#events.getThrowOnMaxListeners();
|
|
20305
|
+
}
|
|
20306
|
+
|
|
20307
|
+
/////////////////////////////////////////////////////////////
|
|
20308
|
+
|
|
20309
|
+
/**
|
|
20310
|
+
* Adds a listener to the beginning of the listeners array for the specified event.
|
|
20311
|
+
*
|
|
20312
|
+
* @param {string} event - Event name.
|
|
20313
|
+
* @param {handler} handler - The callback function.
|
|
20314
|
+
*/
|
|
20315
|
+
prependListener(event, handler) {
|
|
20316
|
+
return this.#events.prependListener(event, handler);
|
|
20317
|
+
}
|
|
20318
|
+
|
|
20319
|
+
/**
|
|
20320
|
+
* Adds a one-time listener to the beginning of the listeners array for the specified event.
|
|
20321
|
+
*
|
|
20322
|
+
* @param {string} event - Event name.
|
|
20323
|
+
* @param {handler} handler - The callback function.
|
|
20324
|
+
* @returns {handler} - The wrapped handler used internally.
|
|
20325
|
+
*/
|
|
20326
|
+
prependListenerOnce(event, handler) {
|
|
20327
|
+
return this.#events.prependListenerOnce(event, handler);
|
|
20328
|
+
}
|
|
20329
|
+
|
|
20330
|
+
//////////////////////////////////////////////////////////////////////
|
|
20331
|
+
|
|
20332
|
+
/**
|
|
20333
|
+
* Adds a event listener.
|
|
20334
|
+
*
|
|
20335
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
20336
|
+
* @param {handler} handler - Callback function to be called when event fires.
|
|
20337
|
+
*/
|
|
20338
|
+
appendListener(event, handler) {
|
|
20339
|
+
return this.#events.appendListener(event, handler);
|
|
20340
|
+
}
|
|
20341
|
+
|
|
20342
|
+
/**
|
|
20343
|
+
* Registers an event listener that runs only once, then is removed.
|
|
20344
|
+
*
|
|
20345
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
20346
|
+
* @param {handler} handler - The callback function to run on event.
|
|
20347
|
+
* @returns {handler} - The wrapped version of the handler.
|
|
20348
|
+
*/
|
|
20349
|
+
appendListenerOnce(event, handler) {
|
|
20350
|
+
return this.#events.appendListenerOnce(event, handler);
|
|
20351
|
+
}
|
|
20352
|
+
|
|
20353
|
+
/**
|
|
20354
|
+
* Adds a event listener.
|
|
20355
|
+
*
|
|
20356
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
20357
|
+
* @param {handler} handler - Callback function to be called when event fires.
|
|
20358
|
+
*/
|
|
20359
|
+
on(event, handler) {
|
|
20360
|
+
return this.#events.on(event, handler);
|
|
20361
|
+
}
|
|
20362
|
+
|
|
20363
|
+
/**
|
|
20364
|
+
* Registers an event listener that runs only once, then is removed.
|
|
20365
|
+
*
|
|
20366
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
20367
|
+
* @param {handler} handler - The callback function to run on event.
|
|
20368
|
+
* @returns {handler} - The wrapped version of the handler.
|
|
20369
|
+
*/
|
|
20370
|
+
once(event, handler) {
|
|
20371
|
+
return this.#events.once(event, handler);
|
|
20372
|
+
}
|
|
20373
|
+
|
|
20374
|
+
////////////////////////////////////////////////////////////////////
|
|
20375
|
+
|
|
20376
|
+
/**
|
|
20377
|
+
* Removes a previously registered event listener.
|
|
20378
|
+
*
|
|
20379
|
+
* @param {string} event - The name of the event to remove the handler from.
|
|
20380
|
+
* @param {handler} handler - The specific callback function to remove.
|
|
20381
|
+
*/
|
|
20382
|
+
off(event, handler) {
|
|
20383
|
+
return this.#events.off(event, handler);
|
|
20384
|
+
}
|
|
20385
|
+
|
|
20386
|
+
/**
|
|
20387
|
+
* Removes all event listeners of a specific type from the element.
|
|
20388
|
+
*
|
|
20389
|
+
* @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
|
|
20390
|
+
*/
|
|
20391
|
+
offAll(event) {
|
|
20392
|
+
return this.#events.offAll(event);
|
|
20393
|
+
}
|
|
20394
|
+
|
|
20395
|
+
/**
|
|
20396
|
+
* Removes all event listeners of all types from the element.
|
|
20397
|
+
*/
|
|
20398
|
+
offAllTypes() {
|
|
20399
|
+
return this.#events.offAllTypes();
|
|
20400
|
+
}
|
|
20401
|
+
|
|
20402
|
+
////////////////////////////////////////////////////////////
|
|
20403
|
+
|
|
20404
|
+
/**
|
|
20405
|
+
* Returns the number of listeners for a given event.
|
|
20406
|
+
*
|
|
20407
|
+
* @param {string} event - The name of the event.
|
|
20408
|
+
* @returns {number} Number of listeners for the event.
|
|
20409
|
+
*/
|
|
20410
|
+
listenerCount(event) {
|
|
20411
|
+
return this.#events.listenerCount(event);
|
|
20412
|
+
}
|
|
20413
|
+
|
|
20414
|
+
/**
|
|
20415
|
+
* Returns a copy of the array of listeners for the specified event.
|
|
20416
|
+
*
|
|
20417
|
+
* @param {string} event - The name of the event.
|
|
20418
|
+
* @returns {handler[]} Array of listener functions.
|
|
20419
|
+
*/
|
|
20420
|
+
listeners(event) {
|
|
20421
|
+
return this.#events.listeners(event);
|
|
20422
|
+
}
|
|
20423
|
+
|
|
20424
|
+
/**
|
|
20425
|
+
* Returns a copy of the array of listeners for the specified event.
|
|
20426
|
+
*
|
|
20427
|
+
* @param {string} event - The name of the event.
|
|
20428
|
+
* @returns {handler[]} Array of listener functions.
|
|
20429
|
+
*/
|
|
20430
|
+
onceListeners(event) {
|
|
20431
|
+
return this.#events.onceListeners(event);
|
|
20432
|
+
}
|
|
20433
|
+
|
|
20434
|
+
/**
|
|
20435
|
+
* Returns a copy of the internal listeners array for the specified event,
|
|
20436
|
+
* including wrapper functions like those used by `.once()`.
|
|
20437
|
+
* @param {string | symbol} event - The event name.
|
|
20438
|
+
* @returns {handler[]} An array of raw listener functions.
|
|
20439
|
+
*/
|
|
20440
|
+
allListeners(event) {
|
|
20441
|
+
return this.#events.allListeners(event);
|
|
20442
|
+
}
|
|
20443
|
+
|
|
20444
|
+
/**
|
|
20445
|
+
* Returns an array of event names for which there are registered listeners.
|
|
20446
|
+
*
|
|
20447
|
+
* @returns {string[]} Array of registered event names.
|
|
20448
|
+
*/
|
|
20449
|
+
eventNames() {
|
|
20450
|
+
return this.#events.eventNames();
|
|
20451
|
+
}
|
|
20452
|
+
|
|
20453
|
+
//////////////////////////////////////////////////////
|
|
20454
|
+
|
|
20455
|
+
/**
|
|
20456
|
+
* Sets the maximum number of listeners per event before a warning is shown.
|
|
20457
|
+
*
|
|
20458
|
+
* @param {number} n - The maximum number of listeners.
|
|
20459
|
+
*/
|
|
20460
|
+
setMaxListeners(n) {
|
|
20461
|
+
return this.#events.setMaxListeners(n);
|
|
20462
|
+
}
|
|
20463
|
+
|
|
20464
|
+
/**
|
|
20465
|
+
* Gets the maximum number of listeners allowed per event.
|
|
20466
|
+
*
|
|
20467
|
+
* @returns {number} The maximum number of listeners.
|
|
20468
|
+
*/
|
|
20469
|
+
getMaxListeners() {
|
|
20470
|
+
return this.#events.getMaxListeners();
|
|
20471
|
+
}
|
|
20472
|
+
|
|
20473
|
+
///////////////////////////////////////////////////
|
|
20474
|
+
|
|
20475
|
+
/** @type {Window|null} Reference to the opened or parent window */
|
|
20476
|
+
#windowRef;
|
|
20477
|
+
|
|
20478
|
+
/** @type {string} Expected origin for postMessage communication */
|
|
20479
|
+
#targetOrigin;
|
|
20480
|
+
|
|
20481
|
+
/** @type {{ route: string, payload: any }[]} Queue of messages emitted before connection is ready */
|
|
20482
|
+
#pendingQueue = [];
|
|
20483
|
+
|
|
20484
|
+
/** @type {boolean} True if handshake between windows is complete */
|
|
20485
|
+
#ready = false;
|
|
20486
|
+
|
|
20487
|
+
/** @type {boolean} True if this instance is the main window (host) */
|
|
20488
|
+
#isHost = false;
|
|
20489
|
+
|
|
20490
|
+
/** @type {NodeJS.Timeout|null} Interval for polling child window closure */
|
|
20491
|
+
#pollClosedInterval = null;
|
|
20492
|
+
|
|
20493
|
+
/** @type {string} Internal message type for handshake */
|
|
20494
|
+
#readyEventName = '__TNE_READY__';
|
|
20495
|
+
|
|
20496
|
+
/** @type {string} Internal message type for routed communication */
|
|
20497
|
+
#routeEventName = '__TNE_ROUTE__';
|
|
20498
|
+
|
|
20499
|
+
/**
|
|
20500
|
+
* Gets the internal handshake event name.
|
|
20501
|
+
* @returns {string}
|
|
20502
|
+
*/
|
|
20503
|
+
get readyEventName() {
|
|
20504
|
+
return this.#readyEventName;
|
|
20505
|
+
}
|
|
20506
|
+
|
|
20507
|
+
/**
|
|
20508
|
+
* Sets the internal handshake event name.
|
|
20509
|
+
* @param {string} name
|
|
20510
|
+
* @throws {TypeError} If the value is not a string.
|
|
20511
|
+
*/
|
|
20512
|
+
set readyEventName(name) {
|
|
20513
|
+
if (typeof name !== 'string') throw new TypeError('TinyNewWinEvents: readyEventName must be a string.');
|
|
20514
|
+
this.#readyEventName = name;
|
|
20515
|
+
}
|
|
20516
|
+
|
|
20517
|
+
/**
|
|
20518
|
+
* Gets the internal route event name.
|
|
20519
|
+
* @returns {string}
|
|
20520
|
+
*/
|
|
20521
|
+
get routeEventName() {
|
|
20522
|
+
return this.#routeEventName;
|
|
20523
|
+
}
|
|
20524
|
+
|
|
20525
|
+
/**
|
|
20526
|
+
* Sets the internal route event name.
|
|
20527
|
+
* @param {string} name
|
|
20528
|
+
* @throws {TypeError} If the value is not a string.
|
|
20529
|
+
*/
|
|
20530
|
+
set routeEventName(name) {
|
|
20531
|
+
if (typeof name !== 'string') throw new TypeError('TinyNewWinEvents: routeEventName must be a string.');
|
|
20532
|
+
this.#routeEventName = name;
|
|
20533
|
+
}
|
|
20534
|
+
|
|
20535
|
+
/**
|
|
20536
|
+
* Initializes a TinyNewWinEvents instance for communication.
|
|
20537
|
+
*
|
|
20538
|
+
* @param {Object} [settings={}] Configuration object.
|
|
20539
|
+
* @param {string} [settings.targetOrigin] Origin to enforce in postMessage.
|
|
20540
|
+
* @param {string} [settings.url] URL string to open.
|
|
20541
|
+
* @param {string} [settings.name] Window name (required if opening a new window).
|
|
20542
|
+
* @param {string} [settings.features] Features string for `window.open`.
|
|
20543
|
+
*
|
|
20544
|
+
* @throws {Error} If `name` is "_blank", which is not allowed.
|
|
20545
|
+
* @throws {TypeError} If `targetOrigin`, `url`, or `features` are not strings (when provided).
|
|
20546
|
+
* @throws {Error} If the window reference is invalid or already being tracked.
|
|
20547
|
+
*/
|
|
20548
|
+
constructor({ targetOrigin, url, name, features } = {}) {
|
|
20549
|
+
if (typeof name === 'string' && name === '_blank')
|
|
20550
|
+
throw new Error(
|
|
20551
|
+
'TinyNewWinEvents: The window name "_blank" is not supported. Please use a custom name to allow tracking.',
|
|
20552
|
+
);
|
|
20553
|
+
if (typeof targetOrigin !== 'undefined' && typeof targetOrigin !== 'string')
|
|
20554
|
+
throw new TypeError('TinyNewWinEvents: The "targetOrigin" option must be a string.');
|
|
20555
|
+
if (typeof url !== 'undefined' && typeof url !== 'string')
|
|
20556
|
+
throw new TypeError('TinyNewWinEvents: The "url" option must be a string.');
|
|
20557
|
+
if (typeof features !== 'undefined' && typeof features !== 'string')
|
|
20558
|
+
throw new TypeError('TinyNewWinEvents: The "features" option must be a string if provided.');
|
|
20559
|
+
|
|
20560
|
+
// Open Page
|
|
20561
|
+
if (typeof url === 'undefined') this.#windowRef = window.opener;
|
|
20562
|
+
// Main Page
|
|
20563
|
+
else {
|
|
20564
|
+
this.#windowRef = typeof url === 'string' ? window.open(url, name, features) : url;
|
|
20565
|
+
this.#isHost = true;
|
|
20566
|
+
}
|
|
20567
|
+
|
|
20568
|
+
if (!this.#windowRef || pollClosedInterval.has(this.#windowRef))
|
|
20569
|
+
throw new Error('Invalid or duplicate window reference.');
|
|
20570
|
+
this.#targetOrigin = targetOrigin ?? window.location.origin;
|
|
20571
|
+
this._handleMessage = this.#handleMessage.bind(this);
|
|
20572
|
+
window.addEventListener('message', this._handleMessage, false);
|
|
20573
|
+
|
|
20574
|
+
// Sends handshake if it is host (main window)
|
|
20575
|
+
if (!this.#isHost) {
|
|
20576
|
+
this.#postRaw(this.#readyEventName, null);
|
|
20577
|
+
this.#startCloseWatcher();
|
|
20578
|
+
}
|
|
20579
|
+
}
|
|
20580
|
+
|
|
20581
|
+
/**
|
|
20582
|
+
* Returns the internal window reference.
|
|
20583
|
+
*
|
|
20584
|
+
* @returns {Window|null}
|
|
20585
|
+
*/
|
|
20586
|
+
getWin() {
|
|
20587
|
+
return this.#windowRef;
|
|
20588
|
+
}
|
|
20589
|
+
|
|
20590
|
+
/**
|
|
20591
|
+
* Internal message handler.
|
|
20592
|
+
*
|
|
20593
|
+
* @param {MessageEvent} event
|
|
20594
|
+
* @returns {void}
|
|
20595
|
+
*/
|
|
20596
|
+
#handleMessage(event) {
|
|
20597
|
+
if (!event.source || (this.#windowRef && event.source !== this.#windowRef)) return;
|
|
20598
|
+
const { type, route, payload } = event.data || {};
|
|
20599
|
+
|
|
20600
|
+
if (type === this.#readyEventName) {
|
|
20601
|
+
this.#ready = true;
|
|
20602
|
+
this.#flushQueue();
|
|
20603
|
+
this.#startCloseWatcher(); // start watcher after handshake (for child window)
|
|
20604
|
+
if (this.#isHost) this.#postRaw(this.#readyEventName, null);
|
|
20605
|
+
return;
|
|
20606
|
+
}
|
|
20607
|
+
|
|
20608
|
+
if (type === this.#routeEventName) this.#events.emit(route, payload, event);
|
|
20609
|
+
}
|
|
20610
|
+
|
|
20611
|
+
/**
|
|
20612
|
+
* Sends all pending messages queued before handshake completion.
|
|
20613
|
+
*
|
|
20614
|
+
* @returns {void}
|
|
20615
|
+
*/
|
|
20616
|
+
#flushQueue() {
|
|
20617
|
+
while (this.#pendingQueue.length) {
|
|
20618
|
+
const data = this.#pendingQueue.shift();
|
|
20619
|
+
if (data) {
|
|
20620
|
+
const { route, payload } = data;
|
|
20621
|
+
this.emit(route, payload);
|
|
20622
|
+
}
|
|
20623
|
+
}
|
|
20624
|
+
}
|
|
20625
|
+
|
|
20626
|
+
/**
|
|
20627
|
+
* Sends a raw postMessage with given type and payload.
|
|
20628
|
+
*
|
|
20629
|
+
* @param {string} type Internal message type
|
|
20630
|
+
* @param {any} payload Data to send
|
|
20631
|
+
* @param {string} [route=''] Optional route name
|
|
20632
|
+
* @returns {void}
|
|
20633
|
+
*/
|
|
20634
|
+
#postRaw(type, payload, route = '') {
|
|
20635
|
+
if (this.#windowRef && this.#windowRef.closed) return;
|
|
20636
|
+
this.#windowRef?.postMessage({ type, route, payload }, this.#targetOrigin);
|
|
20637
|
+
}
|
|
20638
|
+
|
|
20639
|
+
/**
|
|
20640
|
+
* Closes the child window (only allowed from the host).
|
|
20641
|
+
*
|
|
20642
|
+
* @returns {void}
|
|
20643
|
+
*/
|
|
20644
|
+
close() {
|
|
20645
|
+
if (!this.#isHost) throw new Error('Only host can close the window.');
|
|
20646
|
+
if (this.#windowRef && !this.#windowRef.closed) this.#windowRef.close();
|
|
20647
|
+
}
|
|
20648
|
+
|
|
20649
|
+
/**
|
|
20650
|
+
* Emits a message to the other window on a specific route.
|
|
20651
|
+
* If the handshake is not yet complete, the message is queued.
|
|
20652
|
+
* Throws an error if the instance has already been destroyed.
|
|
20653
|
+
*
|
|
20654
|
+
* @param {string} route - Route name used to identify the message handler.
|
|
20655
|
+
* @param {any} payload - Data to send along with the message.
|
|
20656
|
+
* @throws {Error} If the instance is already destroyed.
|
|
20657
|
+
* @returns {void}
|
|
20658
|
+
*/
|
|
20659
|
+
emit(route, payload) {
|
|
20660
|
+
if (typeof route !== 'string') throw new TypeError('Event name must be a string.');
|
|
20661
|
+
if (this.isDestroyed()) throw new Error('Cannot emit: instance has been destroyed.');
|
|
20662
|
+
if (!this.#ready) {
|
|
20663
|
+
this.#pendingQueue.push({ route, payload });
|
|
20664
|
+
return;
|
|
20665
|
+
}
|
|
20666
|
+
this.#postRaw(this.#routeEventName, payload, route);
|
|
20667
|
+
}
|
|
20668
|
+
|
|
20669
|
+
/**
|
|
20670
|
+
* Checks if the connection is active and the window is still open.
|
|
20671
|
+
*
|
|
20672
|
+
* @returns {boolean}
|
|
20673
|
+
*/
|
|
20674
|
+
isConnected() {
|
|
20675
|
+
return this.#ready && this.#windowRef && !this.#windowRef.closed ? true : false;
|
|
20676
|
+
}
|
|
20677
|
+
|
|
20678
|
+
/**
|
|
20679
|
+
* Starts polling to detect when the window is closed.
|
|
20680
|
+
*
|
|
20681
|
+
* @returns {void}
|
|
20682
|
+
*/
|
|
20683
|
+
#startCloseWatcher() {
|
|
20684
|
+
if (!this.#windowRef || this.#pollClosedInterval) return;
|
|
20685
|
+
this.#pollClosedInterval = setInterval(() => {
|
|
20686
|
+
if (this.#windowRef?.closed) {
|
|
20687
|
+
this.#events.emit('WINDOW_REF_CLOSED');
|
|
20688
|
+
this.destroy();
|
|
20689
|
+
}
|
|
20690
|
+
}, 500);
|
|
20691
|
+
pollClosedInterval.set(this.#windowRef, this.#pollClosedInterval);
|
|
20692
|
+
}
|
|
20693
|
+
|
|
20694
|
+
/**
|
|
20695
|
+
* Registers a callback for when the window is closed.
|
|
20696
|
+
*
|
|
20697
|
+
* @param {handler} callback Callback to run on close
|
|
20698
|
+
* @returns {void}
|
|
20699
|
+
*/
|
|
20700
|
+
onClose(callback) {
|
|
20701
|
+
return this.#events.on('WINDOW_REF_CLOSED', callback);
|
|
20702
|
+
}
|
|
20703
|
+
|
|
20704
|
+
/**
|
|
20705
|
+
* Unregisters a previously registered close callback.
|
|
20706
|
+
*
|
|
20707
|
+
* @param {handler} callback Callback to remove
|
|
20708
|
+
* @returns {void}
|
|
20709
|
+
*/
|
|
20710
|
+
offClose(callback) {
|
|
20711
|
+
return this.#events.off('WINDOW_REF_CLOSED', callback);
|
|
20712
|
+
}
|
|
20713
|
+
|
|
20714
|
+
/**
|
|
20715
|
+
* Checks if the communication instance has been destroyed.
|
|
20716
|
+
*
|
|
20717
|
+
* @returns {boolean}
|
|
20718
|
+
*/
|
|
20719
|
+
isDestroyed() {
|
|
20720
|
+
return !this.#windowRef;
|
|
20721
|
+
}
|
|
20722
|
+
|
|
20723
|
+
/**
|
|
20724
|
+
* Destroys the communication instance, cleaning up all resources and listeners.
|
|
20725
|
+
*
|
|
20726
|
+
* @returns {void}
|
|
20727
|
+
*/
|
|
20728
|
+
destroy() {
|
|
20729
|
+
if (!this.#windowRef) return;
|
|
20730
|
+
if (this.#pollClosedInterval) {
|
|
20731
|
+
clearInterval(this.#pollClosedInterval);
|
|
20732
|
+
this.#pollClosedInterval = null;
|
|
20733
|
+
pollClosedInterval.delete(this.#windowRef);
|
|
20734
|
+
}
|
|
20735
|
+
window.removeEventListener('message', this._handleMessage);
|
|
20736
|
+
this.#pendingQueue = [];
|
|
20737
|
+
this.#ready = false;
|
|
20738
|
+
this.#windowRef = null;
|
|
20739
|
+
this.#events.offAllTypes();
|
|
20740
|
+
}
|
|
20741
|
+
}
|
|
20742
|
+
|
|
20743
|
+
/* harmony default export */ const libs_TinyNewWinEvents = (TinyNewWinEvents);
|
|
20744
|
+
|
|
20745
|
+
;// ./src/v1/index.mjs
|
|
20746
|
+
|
|
20747
|
+
|
|
19761
20748
|
|
|
19762
20749
|
|
|
19763
20750
|
|