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
|
@@ -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 (
|
|
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
|
|
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) =>
|
|
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
|
-
!
|
|
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
|
-
|
|
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
|
|
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 = (
|
|
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 (
|
|
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 =
|
|
12580
|
+
const isJson = objChecker_isJsonObject(value);
|
|
12581
12581
|
if (isJson) {
|
|
12582
12582
|
if (value.__undefined__) return undefined;
|
|
12583
12583
|
if (value.__null__) return null;
|
|
@@ -12610,6 +12610,12 @@ class TinyLocalStorage {
|
|
|
12610
12610
|
/** @type {Storage} */
|
|
12611
12611
|
#localStorage = window.localStorage;
|
|
12612
12612
|
|
|
12613
|
+
/** @type {string|null} */
|
|
12614
|
+
#dbKey = null;
|
|
12615
|
+
|
|
12616
|
+
/** @type {number} */
|
|
12617
|
+
#version = 0;
|
|
12618
|
+
|
|
12613
12619
|
/** @type {(ev: StorageEvent) => any} */
|
|
12614
12620
|
#storageEvent = (ev) => this.emit('storage', ev);
|
|
12615
12621
|
|
|
@@ -12617,11 +12623,84 @@ class TinyLocalStorage {
|
|
|
12617
12623
|
* Initializes the TinyLocalStorage instance and sets up cross-tab sync.
|
|
12618
12624
|
*
|
|
12619
12625
|
* Adds listener for the native `storage` event to support tab synchronization.
|
|
12626
|
+
* @param {string} [dbName] - Unique database name.
|
|
12620
12627
|
*/
|
|
12621
|
-
constructor() {
|
|
12628
|
+
constructor(dbName) {
|
|
12629
|
+
if (typeof dbName !== 'undefined' && typeof dbName !== 'string')
|
|
12630
|
+
throw new TypeError('TinyLocalStorage: dbName must be a string if provided.');
|
|
12631
|
+
if (typeof dbName === 'string') this.#dbKey = `LSDB::${dbName}`;
|
|
12622
12632
|
window.addEventListener('storage', this.#storageEvent);
|
|
12623
12633
|
}
|
|
12624
12634
|
|
|
12635
|
+
/**
|
|
12636
|
+
* Validates that a given key does not conflict with internal database keys.
|
|
12637
|
+
*
|
|
12638
|
+
* This method is used to prevent accidental overwriting of reserved `LSDB::` keys
|
|
12639
|
+
* in `localStorage`, which are used internally by TinyLocalStorage for versioning
|
|
12640
|
+
* and data management.
|
|
12641
|
+
*
|
|
12642
|
+
* @param {string} name - The key to validate before writing to localStorage.
|
|
12643
|
+
* @throws {Error} If the key starts with `LSDB::`.
|
|
12644
|
+
*/
|
|
12645
|
+
#isProtectedDbKey(name) {
|
|
12646
|
+
if (typeof name === 'string' && name.startsWith('LSDB::'))
|
|
12647
|
+
throw new Error(`TinyLocalStorage: Key "${name}" may conflict with reserved dbKeys.`);
|
|
12648
|
+
}
|
|
12649
|
+
|
|
12650
|
+
/**
|
|
12651
|
+
* Updates the version of the storage and triggers migration if needed.
|
|
12652
|
+
*
|
|
12653
|
+
* @param {number} version - Desired version of the database.
|
|
12654
|
+
* @param {(oldVersion: number, newVersion: number) => void} onUpgrade - Callback to perform migration logic.
|
|
12655
|
+
* @throws {Error} If the database key has not been initialized.
|
|
12656
|
+
* @throws {TypeError} If `version` is not a valid positive number.
|
|
12657
|
+
* @throws {TypeError} If `onUpgrade` is not a function.
|
|
12658
|
+
*/
|
|
12659
|
+
updateStorageVersion(version, onUpgrade) {
|
|
12660
|
+
if (typeof this.#dbKey !== 'string')
|
|
12661
|
+
throw new Error(
|
|
12662
|
+
'TinyLocalStorage: Database key is not initialized. Set a valid dbName in the constructor.',
|
|
12663
|
+
);
|
|
12664
|
+
if (typeof version !== 'number' || Number.isNaN(version) || version < 1)
|
|
12665
|
+
throw new TypeError('TinyLocalStorage: version must be a positive number.');
|
|
12666
|
+
if (typeof onUpgrade !== 'function')
|
|
12667
|
+
throw new TypeError('TinyLocalStorage: onUpgrade must be a function.');
|
|
12668
|
+
|
|
12669
|
+
// @ts-ignore
|
|
12670
|
+
const savedVersion = parseInt(localStorage.getItem(this.#dbKey), 10) || 0;
|
|
12671
|
+
if (typeof savedVersion !== 'number' || Number.isNaN(savedVersion) || savedVersion < 0)
|
|
12672
|
+
throw new TypeError('TinyLocalStorage: saved version in localStorage is not a valid number.');
|
|
12673
|
+
|
|
12674
|
+
if (version < savedVersion)
|
|
12675
|
+
throw new Error(
|
|
12676
|
+
`TinyLocalStorage: Cannot downgrade database version from ${savedVersion} to ${version}.`,
|
|
12677
|
+
);
|
|
12678
|
+
|
|
12679
|
+
if (version > savedVersion) {
|
|
12680
|
+
onUpgrade(savedVersion, version);
|
|
12681
|
+
localStorage.setItem(this.#dbKey, String(version));
|
|
12682
|
+
this.#version = version;
|
|
12683
|
+
}
|
|
12684
|
+
}
|
|
12685
|
+
|
|
12686
|
+
/**
|
|
12687
|
+
* Gets the current database key used in localStorage.
|
|
12688
|
+
*
|
|
12689
|
+
* @returns {string|null} The database key, or null if not set.
|
|
12690
|
+
*/
|
|
12691
|
+
getDbKey() {
|
|
12692
|
+
return this.#dbKey;
|
|
12693
|
+
}
|
|
12694
|
+
|
|
12695
|
+
/**
|
|
12696
|
+
* Gets the current version of the database.
|
|
12697
|
+
*
|
|
12698
|
+
* @returns {number} The current version number.
|
|
12699
|
+
*/
|
|
12700
|
+
getVersion() {
|
|
12701
|
+
return this.#version;
|
|
12702
|
+
}
|
|
12703
|
+
|
|
12625
12704
|
/**
|
|
12626
12705
|
* Defines a custom storage interface (e.g. `sessionStorage`).
|
|
12627
12706
|
*
|
|
@@ -12629,7 +12708,7 @@ class TinyLocalStorage {
|
|
|
12629
12708
|
*/
|
|
12630
12709
|
setLocalStorage(localstorage) {
|
|
12631
12710
|
if (!(localstorage instanceof Storage))
|
|
12632
|
-
throw new
|
|
12711
|
+
throw new TypeError('Argument must be a valid instance of Storage.');
|
|
12633
12712
|
this.#localStorage = localstorage;
|
|
12634
12713
|
}
|
|
12635
12714
|
|
|
@@ -12651,7 +12730,7 @@ class TinyLocalStorage {
|
|
|
12651
12730
|
*/
|
|
12652
12731
|
#setJson(name, data) {
|
|
12653
12732
|
if (typeof name !== 'string' || !name.length)
|
|
12654
|
-
throw new
|
|
12733
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
12655
12734
|
return TinyLocalStorage.encodeSpecialJson(data);
|
|
12656
12735
|
}
|
|
12657
12736
|
|
|
@@ -12664,13 +12743,14 @@ class TinyLocalStorage {
|
|
|
12664
12743
|
* @param {LocalStorageJsonValue} data - The data to be serialized and stored.
|
|
12665
12744
|
*/
|
|
12666
12745
|
setJson(name, data) {
|
|
12746
|
+
this.#isProtectedDbKey(name);
|
|
12667
12747
|
if (
|
|
12668
|
-
!
|
|
12748
|
+
!objChecker_isJsonObject(data) &&
|
|
12669
12749
|
!Array.isArray(data) &&
|
|
12670
12750
|
!(data instanceof Map) &&
|
|
12671
12751
|
!(data instanceof Set)
|
|
12672
12752
|
) {
|
|
12673
|
-
throw new
|
|
12753
|
+
throw new TypeError('The storage value is not a valid JSON-compatible structure.');
|
|
12674
12754
|
}
|
|
12675
12755
|
const encoded = this.#setJson(name, data);
|
|
12676
12756
|
this.emit('setJson', name, data);
|
|
@@ -12688,7 +12768,7 @@ class TinyLocalStorage {
|
|
|
12688
12768
|
*/
|
|
12689
12769
|
#getJson(name, defaultData) {
|
|
12690
12770
|
if (typeof name !== 'string' || !name.length)
|
|
12691
|
-
throw new
|
|
12771
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
12692
12772
|
|
|
12693
12773
|
const raw = this.#localStorage.getItem(name);
|
|
12694
12774
|
const fallbackTypes = {
|
|
@@ -12730,7 +12810,7 @@ class TinyLocalStorage {
|
|
|
12730
12810
|
decoded instanceof Map ||
|
|
12731
12811
|
decoded instanceof Set ||
|
|
12732
12812
|
Array.isArray(decoded) ||
|
|
12733
|
-
|
|
12813
|
+
objChecker_isJsonObject(decoded)
|
|
12734
12814
|
)
|
|
12735
12815
|
return decoded;
|
|
12736
12816
|
return fallback;
|
|
@@ -12742,7 +12822,8 @@ class TinyLocalStorage {
|
|
|
12742
12822
|
* @param {Date} data
|
|
12743
12823
|
*/
|
|
12744
12824
|
setDate(name, data) {
|
|
12745
|
-
|
|
12825
|
+
this.#isProtectedDbKey(name);
|
|
12826
|
+
if (!(data instanceof Date)) throw new TypeError('Value must be a Date.');
|
|
12746
12827
|
const encoded = this.#setJson(name, data);
|
|
12747
12828
|
this.emit('setDate', name, data);
|
|
12748
12829
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -12764,7 +12845,8 @@ class TinyLocalStorage {
|
|
|
12764
12845
|
* @param {RegExp} data
|
|
12765
12846
|
*/
|
|
12766
12847
|
setRegExp(name, data) {
|
|
12767
|
-
|
|
12848
|
+
this.#isProtectedDbKey(name);
|
|
12849
|
+
if (!(data instanceof RegExp)) throw new TypeError('Value must be a RegExp.');
|
|
12768
12850
|
const encoded = this.#setJson(name, data);
|
|
12769
12851
|
this.emit('setRegExp', name, data);
|
|
12770
12852
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -12786,7 +12868,8 @@ class TinyLocalStorage {
|
|
|
12786
12868
|
* @param {bigint} data
|
|
12787
12869
|
*/
|
|
12788
12870
|
setBigInt(name, data) {
|
|
12789
|
-
|
|
12871
|
+
this.#isProtectedDbKey(name);
|
|
12872
|
+
if (typeof data !== 'bigint') throw new TypeError('Value must be a BigInt.');
|
|
12790
12873
|
const encoded = this.#setJson(name, data);
|
|
12791
12874
|
this.emit('setBigInt', name, data);
|
|
12792
12875
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -12809,7 +12892,8 @@ class TinyLocalStorage {
|
|
|
12809
12892
|
* @param {symbol} data
|
|
12810
12893
|
*/
|
|
12811
12894
|
setSymbol(name, data) {
|
|
12812
|
-
|
|
12895
|
+
this.#isProtectedDbKey(name);
|
|
12896
|
+
if (typeof data !== 'symbol') throw new TypeError('Value must be a Symbol.');
|
|
12813
12897
|
const encoded = this.#setJson(name, data);
|
|
12814
12898
|
this.emit('setSymbol', name, data);
|
|
12815
12899
|
return this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
@@ -12842,8 +12926,9 @@ class TinyLocalStorage {
|
|
|
12842
12926
|
* @param {any} data - The data to store.
|
|
12843
12927
|
*/
|
|
12844
12928
|
setItem(name, data) {
|
|
12929
|
+
this.#isProtectedDbKey(name);
|
|
12845
12930
|
if (typeof name !== 'string' || !name.length)
|
|
12846
|
-
throw new
|
|
12931
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
12847
12932
|
this.emit('setItem', name, data);
|
|
12848
12933
|
return this.#localStorage.setItem(name, data);
|
|
12849
12934
|
}
|
|
@@ -12856,7 +12941,7 @@ class TinyLocalStorage {
|
|
|
12856
12941
|
*/
|
|
12857
12942
|
getItem(name) {
|
|
12858
12943
|
if (typeof name !== 'string' || !name.length)
|
|
12859
|
-
throw new
|
|
12944
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
12860
12945
|
return this.#localStorage.getItem(name);
|
|
12861
12946
|
}
|
|
12862
12947
|
|
|
@@ -12867,9 +12952,10 @@ class TinyLocalStorage {
|
|
|
12867
12952
|
* @param {string} data - The string to store.
|
|
12868
12953
|
*/
|
|
12869
12954
|
setString(name, data) {
|
|
12955
|
+
this.#isProtectedDbKey(name);
|
|
12870
12956
|
if (typeof name !== 'string' || !name.length)
|
|
12871
|
-
throw new
|
|
12872
|
-
if (typeof data !== 'string') throw new
|
|
12957
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
12958
|
+
if (typeof data !== 'string') throw new TypeError('Value must be a string.');
|
|
12873
12959
|
|
|
12874
12960
|
this.emit('setString', name, data);
|
|
12875
12961
|
return this.#localStorage.setItem(
|
|
@@ -12889,13 +12975,13 @@ class TinyLocalStorage {
|
|
|
12889
12975
|
*/
|
|
12890
12976
|
getString(name) {
|
|
12891
12977
|
if (typeof name !== 'string' || !name.length)
|
|
12892
|
-
throw new
|
|
12978
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
12893
12979
|
let value = this.#localStorage.getItem(name);
|
|
12894
12980
|
try {
|
|
12895
12981
|
/** @type {{ value: string; __string__: boolean }} */
|
|
12896
12982
|
// @ts-ignore
|
|
12897
12983
|
value = JSON.parse(value);
|
|
12898
|
-
if (!
|
|
12984
|
+
if (!objChecker_isJsonObject(value) || !value.__string__ || typeof value.value !== 'string') return null;
|
|
12899
12985
|
value = value.value;
|
|
12900
12986
|
} catch {
|
|
12901
12987
|
value = null;
|
|
@@ -12911,9 +12997,10 @@ class TinyLocalStorage {
|
|
|
12911
12997
|
* @param {number} data - The number to store.
|
|
12912
12998
|
*/
|
|
12913
12999
|
setNumber(name, data) {
|
|
13000
|
+
this.#isProtectedDbKey(name);
|
|
12914
13001
|
if (typeof name !== 'string' || !name.length)
|
|
12915
|
-
throw new
|
|
12916
|
-
if (typeof data !== 'number') throw new
|
|
13002
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
13003
|
+
if (typeof data !== 'number') throw new TypeError('Value must be a number.');
|
|
12917
13004
|
this.emit('setNumber', name, data);
|
|
12918
13005
|
return this.#localStorage.setItem(name, String(data));
|
|
12919
13006
|
}
|
|
@@ -12926,7 +13013,7 @@ class TinyLocalStorage {
|
|
|
12926
13013
|
*/
|
|
12927
13014
|
getNumber(name) {
|
|
12928
13015
|
if (typeof name !== 'string' || !name.length)
|
|
12929
|
-
throw new
|
|
13016
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
12930
13017
|
|
|
12931
13018
|
/** @type {number|string|null} */
|
|
12932
13019
|
let number = this.#localStorage.getItem(name);
|
|
@@ -12945,9 +13032,10 @@ class TinyLocalStorage {
|
|
|
12945
13032
|
* @param {boolean} data - The boolean value to store.
|
|
12946
13033
|
*/
|
|
12947
13034
|
setBool(name, data) {
|
|
13035
|
+
this.#isProtectedDbKey(name);
|
|
12948
13036
|
if (typeof name !== 'string' || !name.length)
|
|
12949
|
-
throw new
|
|
12950
|
-
if (typeof data !== 'boolean') throw new
|
|
13037
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
13038
|
+
if (typeof data !== 'boolean') throw new TypeError('Value must be a boolean.');
|
|
12951
13039
|
this.emit('setBool', name, data);
|
|
12952
13040
|
return this.#localStorage.setItem(name, String(data));
|
|
12953
13041
|
}
|
|
@@ -12960,7 +13048,7 @@ class TinyLocalStorage {
|
|
|
12960
13048
|
*/
|
|
12961
13049
|
getBool(name) {
|
|
12962
13050
|
if (typeof name !== 'string' || !name.length)
|
|
12963
|
-
throw new
|
|
13051
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
12964
13052
|
|
|
12965
13053
|
const value = this.#localStorage.getItem(name);
|
|
12966
13054
|
if (typeof value === 'boolean') return value;
|
|
@@ -12979,7 +13067,7 @@ class TinyLocalStorage {
|
|
|
12979
13067
|
*/
|
|
12980
13068
|
removeItem(name) {
|
|
12981
13069
|
if (typeof name !== 'string' || !name.length)
|
|
12982
|
-
throw new
|
|
13070
|
+
throw new TypeError('Key must be a non-empty string.');
|
|
12983
13071
|
|
|
12984
13072
|
this.emit('removeItem', name);
|
|
12985
13073
|
return this.#localStorage.removeItem(name);
|
|
@@ -13095,7 +13183,904 @@ TinyLocalStorage.registerJsonType(
|
|
|
13095
13183
|
|
|
13096
13184
|
/* harmony default export */ const libs_TinyLocalStorage = ((/* unused pure expression or super */ null && (TinyLocalStorage)));
|
|
13097
13185
|
|
|
13098
|
-
;// ./src/v1/
|
|
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
|
+
|
|
14081
|
+
;// ./src/v1/index.mjs
|
|
14082
|
+
|
|
14083
|
+
|
|
13099
14084
|
|
|
13100
14085
|
|
|
13101
14086
|
|
|
@@ -13211,7 +14196,7 @@ class TinyUploadClicker {
|
|
|
13211
14196
|
* @throws {TypeError} If the config is invalid or required options are missing.
|
|
13212
14197
|
*/
|
|
13213
14198
|
constructor(options) {
|
|
13214
|
-
if (!
|
|
14199
|
+
if (!objChecker_isJsonObject(options))
|
|
13215
14200
|
throw new TypeError('TinyUploadClicker: "options" must be a valid object.');
|
|
13216
14201
|
|
|
13217
14202
|
this.#config = {
|
|
@@ -13277,10 +14262,10 @@ class TinyUploadClicker {
|
|
|
13277
14262
|
)
|
|
13278
14263
|
throw new TypeError('TinyUploadClicker: "onFileLoad" must be a function or null.');
|
|
13279
14264
|
|
|
13280
|
-
if (options.inputAttributes !== undefined && !
|
|
14265
|
+
if (options.inputAttributes !== undefined && !objChecker_isJsonObject(options.inputAttributes))
|
|
13281
14266
|
throw new TypeError('TinyUploadClicker: "inputAttributes" must be an object.');
|
|
13282
14267
|
|
|
13283
|
-
if (options.inputStyles !== undefined && !
|
|
14268
|
+
if (options.inputStyles !== undefined && !objChecker_isJsonObject(options.inputStyles))
|
|
13284
14269
|
throw new TypeError('TinyUploadClicker: "inputStyles" must be an object.');
|
|
13285
14270
|
|
|
13286
14271
|
this.#boundClick = this.#handleClick.bind(this);
|