tiny-essentials 1.18.1 → 1.19.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/README.md +17 -3
- package/dist/node_modules/firebase-functions/lib/common/trace.cjs +0 -1
- package/dist/node_modules/firebase-functions/lib/logger/index.cjs +1 -0
- package/dist/v1/ColorSafeStringify.min.js +1 -1
- package/dist/v1/TinyAfterScrollWatcher.min.js +1 -1
- package/dist/v1/TinyBasicsEs.js +13 -6
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyClipboard.min.js +1 -1
- package/dist/v1/TinyColorConverter.js +617 -0
- package/dist/v1/TinyColorConverter.min.js +1 -0
- package/dist/v1/TinyDomReadyManager.min.js +1 -1
- package/dist/v1/TinyDragger.min.js +1 -1
- package/dist/v1/TinyEssentials.js +2635 -482
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyEvents.js +402 -0
- package/dist/v1/TinyEvents.min.js +1 -0
- package/dist/v1/TinyHtml.min.js +1 -1
- package/dist/v1/TinyLocalStorage.js +1292 -0
- package/dist/v1/TinyLocalStorage.min.js +1 -0
- package/dist/v1/TinyNotifications.min.js +1 -1
- package/dist/v1/TinyNotifyCenter.min.js +1 -1
- package/dist/v1/TinyPromiseQueue.min.js +1 -1
- package/dist/v1/TinyRateLimiter.js +2 -1
- package/dist/v1/TinyRateLimiter.min.js +1 -1
- package/dist/v1/TinySmartScroller.js +570 -52
- package/dist/v1/TinySmartScroller.min.js +1 -1
- package/dist/v1/TinyTextRangeEditor.min.js +1 -1
- package/dist/v1/TinyTimeout.js +233 -0
- package/dist/v1/TinyTimeout.min.js +1 -0
- package/dist/v1/TinyToastNotify.min.js +1 -1
- package/dist/v1/TinyUploadClicker.js +1457 -106
- package/dist/v1/TinyUploadClicker.min.js +1 -1
- package/dist/v1/UltraRandomMsgGen.min.js +1 -1
- package/dist/v1/basics/html.cjs +13 -6
- package/dist/v1/basics/html.d.mts +12 -4
- package/dist/v1/basics/html.mjs +13 -6
- package/dist/v1/build/TinyColorConverter.cjs +7 -0
- package/dist/v1/build/TinyColorConverter.d.mts +3 -0
- package/dist/v1/build/TinyColorConverter.mjs +2 -0
- package/dist/v1/build/TinyEvents.cjs +7 -0
- package/dist/v1/build/TinyEvents.d.mts +3 -0
- package/dist/v1/build/TinyEvents.mjs +2 -0
- package/dist/v1/build/TinyLocalStorage.cjs +7 -0
- package/dist/v1/build/TinyLocalStorage.d.mts +3 -0
- package/dist/v1/build/TinyLocalStorage.mjs +2 -0
- package/dist/v1/build/TinyTimeout.cjs +7 -0
- package/dist/v1/build/TinyTimeout.d.mts +3 -0
- package/dist/v1/build/TinyTimeout.mjs +2 -0
- package/dist/v1/index.cjs +8 -0
- package/dist/v1/index.d.mts +5 -1
- package/dist/v1/index.mjs +5 -1
- package/dist/v1/libs/TinyColorConverter.cjs +578 -0
- package/dist/v1/libs/TinyColorConverter.d.mts +396 -0
- package/dist/v1/libs/TinyColorConverter.mjs +520 -0
- package/dist/v1/libs/TinyEvents.cjs +363 -0
- package/dist/v1/libs/TinyEvents.d.mts +160 -0
- package/dist/v1/libs/TinyEvents.mjs +328 -0
- package/dist/v1/libs/TinyLocalStorage.cjs +847 -0
- package/dist/v1/libs/TinyLocalStorage.d.mts +407 -0
- package/dist/v1/libs/TinyLocalStorage.mjs +740 -0
- package/dist/v1/libs/TinySmartScroller.cjs +207 -52
- package/dist/v1/libs/TinySmartScroller.d.mts +164 -16
- package/dist/v1/libs/TinySmartScroller.mjs +181 -52
- package/dist/v1/libs/TinyTimeout.cjs +194 -0
- package/dist/v1/libs/TinyTimeout.d.mts +89 -0
- package/dist/v1/libs/TinyTimeout.mjs +179 -0
- package/dist/v1/libs/TinyUploadClicker.cjs +1 -0
- package/docs/v1/README.md +4 -0
- package/docs/v1/libs/TinyColorConverter.md +220 -0
- package/docs/v1/libs/TinyEvents.md +199 -0
- package/docs/v1/libs/TinyLocalStorage.md +350 -0
- package/docs/v1/libs/TinyRateLimiter.md +0 -3
- package/docs/v1/libs/TinyTimeout.md +190 -0
- package/package.json +28 -5
|
@@ -2888,17 +2888,21 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
2888
2888
|
ColorSafeStringify: () => (/* reexport */ libs_ColorSafeStringify),
|
|
2889
2889
|
TinyAfterScrollWatcher: () => (/* reexport */ libs_TinyAfterScrollWatcher),
|
|
2890
2890
|
TinyClipboard: () => (/* reexport */ libs_TinyClipboard),
|
|
2891
|
+
TinyColorConverter: () => (/* reexport */ libs_TinyColorConverter),
|
|
2891
2892
|
TinyDomReadyManager: () => (/* reexport */ libs_TinyDomReadyManager),
|
|
2892
2893
|
TinyDragDropDetector: () => (/* reexport */ libs_TinyDragDropDetector),
|
|
2893
2894
|
TinyDragger: () => (/* reexport */ libs_TinyDragger),
|
|
2895
|
+
TinyEvents: () => (/* reexport */ libs_TinyEvents),
|
|
2894
2896
|
TinyHtml: () => (/* reexport */ libs_TinyHtml),
|
|
2895
2897
|
TinyLevelUp: () => (/* reexport */ userLevel),
|
|
2898
|
+
TinyLocalStorage: () => (/* reexport */ libs_TinyLocalStorage),
|
|
2896
2899
|
TinyNotifications: () => (/* reexport */ libs_TinyNotifications),
|
|
2897
2900
|
TinyNotifyCenter: () => (/* reexport */ libs_TinyNotifyCenter),
|
|
2898
2901
|
TinyPromiseQueue: () => (/* reexport */ libs_TinyPromiseQueue),
|
|
2899
2902
|
TinyRateLimiter: () => (/* reexport */ libs_TinyRateLimiter),
|
|
2900
2903
|
TinySmartScroller: () => (/* reexport */ libs_TinySmartScroller),
|
|
2901
2904
|
TinyTextRangeEditor: () => (/* reexport */ libs_TinyTextRangeEditor),
|
|
2905
|
+
TinyTimeout: () => (/* reexport */ libs_TinyTimeout),
|
|
2902
2906
|
TinyToastNotify: () => (/* reexport */ libs_TinyToastNotify),
|
|
2903
2907
|
UltraRandomMsgGen: () => (/* reexport */ libs_UltraRandomMsgGen),
|
|
2904
2908
|
addAiMarkerShortcut: () => (/* reexport */ addAiMarkerShortcut),
|
|
@@ -5153,7 +5157,8 @@ class TinyRateLimiter {
|
|
|
5153
5157
|
*/
|
|
5154
5158
|
reset(userId) {
|
|
5155
5159
|
if (false)
|
|
5156
|
-
|
|
5160
|
+
// removed by dead control flow
|
|
5161
|
+
{}
|
|
5157
5162
|
return this.resetUserGroup(userId);
|
|
5158
5163
|
}
|
|
5159
5164
|
|
|
@@ -6527,8 +6532,8 @@ async function fetchBlob(url, allowedMimeTypes, options) {
|
|
|
6527
6532
|
* @param {Element} [settings.element=document.body] - The element to receive visibility classes.
|
|
6528
6533
|
* @param {string} [settings.hiddenClass='windowHidden'] - CSS class applied when the page is hidden.
|
|
6529
6534
|
* @param {string} [settings.visibleClass='windowVisible'] - CSS class applied when the page is visible.
|
|
6530
|
-
* @param {() => void} [settings.onVisible] - Callback called when page becomes visible.
|
|
6531
|
-
* @param {() => void} [settings.onHidden] - Callback called when page becomes hidden.
|
|
6535
|
+
* @param {(data: { type: string; oldType: string; oldClass: string; }) => void} [settings.onVisible] - Callback called when page becomes visible.
|
|
6536
|
+
* @param {(data: { type: string; oldType: string; oldClass: string; }) => void} [settings.onHidden] - Callback called when page becomes hidden.
|
|
6532
6537
|
* @returns {() => void} Function that removes all installed event listeners.
|
|
6533
6538
|
* @throws {TypeError} If any provided setting is invalid.
|
|
6534
6539
|
*/
|
|
@@ -6548,6 +6553,8 @@ function installWindowHiddenScript({
|
|
|
6548
6553
|
if (onHidden !== undefined && typeof onHidden !== 'function')
|
|
6549
6554
|
throw new TypeError(`"onHidden" must be a function if provided.`);
|
|
6550
6555
|
|
|
6556
|
+
let oldType = '';
|
|
6557
|
+
let oldClass = '';
|
|
6551
6558
|
const removeClass = () => {
|
|
6552
6559
|
element.classList.remove(hiddenClass);
|
|
6553
6560
|
element.classList.remove(visibleClass);
|
|
@@ -6585,19 +6592,24 @@ function installWindowHiddenScript({
|
|
|
6585
6592
|
|
|
6586
6593
|
if (visibleEvents.includes(type)) {
|
|
6587
6594
|
element.classList.add(visibleClass);
|
|
6588
|
-
onVisible?.();
|
|
6595
|
+
onVisible?.({ type, oldClass, oldType });
|
|
6596
|
+
oldClass = visibleClass;
|
|
6589
6597
|
} else if (hiddenEvents.includes(type)) {
|
|
6590
6598
|
element.classList.add(hiddenClass);
|
|
6591
|
-
onHidden?.();
|
|
6599
|
+
onHidden?.({ type, oldClass, oldType });
|
|
6600
|
+
oldClass = hiddenClass;
|
|
6592
6601
|
} else {
|
|
6593
6602
|
if (isHidden) {
|
|
6594
6603
|
element.classList.add(hiddenClass);
|
|
6595
|
-
onHidden?.();
|
|
6604
|
+
onHidden?.({ type, oldClass, oldType });
|
|
6605
|
+
oldClass = hiddenClass;
|
|
6596
6606
|
} else {
|
|
6597
6607
|
element.classList.add(visibleClass);
|
|
6598
|
-
onVisible?.();
|
|
6608
|
+
onVisible?.({ type, oldClass, oldType });
|
|
6609
|
+
oldClass = visibleClass;
|
|
6599
6610
|
}
|
|
6600
6611
|
}
|
|
6612
|
+
oldType = type;
|
|
6601
6613
|
};
|
|
6602
6614
|
|
|
6603
6615
|
/** @type {() => void} */
|
|
@@ -15694,10 +15706,374 @@ class UltraRandomMsgGen {
|
|
|
15694
15706
|
|
|
15695
15707
|
/* harmony default export */ const libs_UltraRandomMsgGen = (UltraRandomMsgGen);
|
|
15696
15708
|
|
|
15709
|
+
;// ./src/v1/libs/TinyEvents.mjs
|
|
15710
|
+
/**
|
|
15711
|
+
* A generic event listener callback function.
|
|
15712
|
+
*
|
|
15713
|
+
* @callback handler
|
|
15714
|
+
* @param {...any} payload - The data payload passed when the event is triggered.
|
|
15715
|
+
* @returns {void}
|
|
15716
|
+
*/
|
|
15717
|
+
|
|
15718
|
+
/**
|
|
15719
|
+
* TinyEvents provides a minimalistic event emitter system similar to Node.js's EventEmitter,
|
|
15720
|
+
* enabling components to subscribe to, emit, and manage events and their listeners.
|
|
15721
|
+
*
|
|
15722
|
+
* Features include:
|
|
15723
|
+
* - Adding/removing event listeners (`on`, `off`, `offAll`, `offAllTypes`)
|
|
15724
|
+
* - One-time listeners (`once`)
|
|
15725
|
+
* - Emitting events (`emit`)
|
|
15726
|
+
* - Listener inspection and limits (`listenerCount`, `listeners`, `eventNames`)
|
|
15727
|
+
* - Maximum listener control (`setMaxListeners`, `getMaxListeners`)
|
|
15728
|
+
*
|
|
15729
|
+
* This class is useful for lightweight, dependency-free publish/subscribe event handling
|
|
15730
|
+
* within modular JavaScript applications.
|
|
15731
|
+
*
|
|
15732
|
+
* @class
|
|
15733
|
+
*/
|
|
15734
|
+
class TinyEvents {
|
|
15735
|
+
/** @type {Map<string, { handler: handler; config: { once: boolean } }[]>} */
|
|
15736
|
+
#listeners = new Map();
|
|
15737
|
+
|
|
15738
|
+
/** @type {number} */
|
|
15739
|
+
#maxListeners = 10;
|
|
15740
|
+
|
|
15741
|
+
/** @type {boolean} */
|
|
15742
|
+
#throwMaxListeners = false;
|
|
15743
|
+
|
|
15744
|
+
/**
|
|
15745
|
+
* Enables or disables throwing an error when the maximum number of listeners is exceeded.
|
|
15746
|
+
*
|
|
15747
|
+
* @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
|
|
15748
|
+
*/
|
|
15749
|
+
setThrowOnMaxListeners(shouldThrow) {
|
|
15750
|
+
if (typeof shouldThrow !== 'boolean')
|
|
15751
|
+
throw new TypeError('setThrowOnMaxListeners(value): value must be a boolean');
|
|
15752
|
+
this.#throwMaxListeners = shouldThrow;
|
|
15753
|
+
}
|
|
15754
|
+
|
|
15755
|
+
/**
|
|
15756
|
+
* Checks whether an error will be thrown when the max listener limit is exceeded.
|
|
15757
|
+
*
|
|
15758
|
+
* @returns {boolean} True if an error will be thrown, false if only a warning is shown.
|
|
15759
|
+
*/
|
|
15760
|
+
getThrowOnMaxListeners() {
|
|
15761
|
+
return this.#throwMaxListeners;
|
|
15762
|
+
}
|
|
15763
|
+
|
|
15764
|
+
///////////////////////////////////////////////////
|
|
15765
|
+
|
|
15766
|
+
/**
|
|
15767
|
+
* Internal method to prepend a listener with options.
|
|
15768
|
+
*
|
|
15769
|
+
* @param {string} event - Event name.
|
|
15770
|
+
* @param {handler} handler - The callback function.
|
|
15771
|
+
* @param {Object} [settings={}] - Optional settings.
|
|
15772
|
+
* @param {boolean} [settings.once=false] - If the listener should be executed once.
|
|
15773
|
+
*/
|
|
15774
|
+
#prepend(event, handler, { once = false } = {}) {
|
|
15775
|
+
let eventData = this.#listeners.get(event);
|
|
15776
|
+
if (!Array.isArray(eventData)) {
|
|
15777
|
+
eventData = [];
|
|
15778
|
+
this.#listeners.set(event, eventData);
|
|
15779
|
+
}
|
|
15780
|
+
eventData.unshift({ handler, config: { once } });
|
|
15781
|
+
|
|
15782
|
+
const max = this.#maxListeners;
|
|
15783
|
+
if (max > 0 && eventData.length > max) {
|
|
15784
|
+
const warnMessage =
|
|
15785
|
+
`Possible memory leak detected. ${eventData.length} "${event}" listeners added. ` +
|
|
15786
|
+
`Use setMaxListeners() to increase limit.`;
|
|
15787
|
+
if (!this.#throwMaxListeners) console.warn(warnMessage);
|
|
15788
|
+
else throw new Error(warnMessage);
|
|
15789
|
+
}
|
|
15790
|
+
}
|
|
15791
|
+
|
|
15792
|
+
/**
|
|
15793
|
+
* Adds a listener to the beginning of the listeners array for the specified event.
|
|
15794
|
+
*
|
|
15795
|
+
* @param {string} event - Event name.
|
|
15796
|
+
* @param {handler} handler - The callback function.
|
|
15797
|
+
*/
|
|
15798
|
+
prependListener(event, handler) {
|
|
15799
|
+
if (typeof event !== 'string')
|
|
15800
|
+
throw new TypeError('prepend(event, handler): event name must be a string');
|
|
15801
|
+
if (typeof handler !== 'function')
|
|
15802
|
+
throw new TypeError('prepend(event, handler): handler must be a function');
|
|
15803
|
+
this.#prepend(event, handler);
|
|
15804
|
+
}
|
|
15805
|
+
|
|
15806
|
+
/**
|
|
15807
|
+
* Adds a one-time listener to the beginning of the listeners array for the specified event.
|
|
15808
|
+
*
|
|
15809
|
+
* @param {string} event - Event name.
|
|
15810
|
+
* @param {handler} handler - The callback function.
|
|
15811
|
+
* @returns {handler} - The wrapped handler used internally.
|
|
15812
|
+
*/
|
|
15813
|
+
prependListenerOnce(event, handler) {
|
|
15814
|
+
if (typeof event !== 'string')
|
|
15815
|
+
throw new TypeError('prependOnceListener(event, handler): event name must be a string');
|
|
15816
|
+
if (typeof handler !== 'function')
|
|
15817
|
+
throw new TypeError('prependOnceListener(event, handler): handler must be a function');
|
|
15818
|
+
|
|
15819
|
+
/** @type {handler} */
|
|
15820
|
+
const wrapped = (...args) => {
|
|
15821
|
+
this.off(event, wrapped);
|
|
15822
|
+
handler(...args);
|
|
15823
|
+
};
|
|
15824
|
+
|
|
15825
|
+
this.#prepend(event, wrapped, { once: true });
|
|
15826
|
+
return wrapped;
|
|
15827
|
+
}
|
|
15828
|
+
|
|
15829
|
+
////////////////////////////////////////////////////////////
|
|
15830
|
+
|
|
15831
|
+
/**
|
|
15832
|
+
* Adds a event listener.
|
|
15833
|
+
*
|
|
15834
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
15835
|
+
* @param {handler} handler - Callback function to be called when event fires.
|
|
15836
|
+
* @param {Object} [settings={}] - Optional settings.
|
|
15837
|
+
* @param {boolean} [settings.once=false] - This is a once event.
|
|
15838
|
+
*/
|
|
15839
|
+
#on(event, handler, { once = false } = {}) {
|
|
15840
|
+
let eventData = this.#listeners.get(event);
|
|
15841
|
+
if (!Array.isArray(eventData)) {
|
|
15842
|
+
eventData = [];
|
|
15843
|
+
this.#listeners.set(event, eventData);
|
|
15844
|
+
}
|
|
15845
|
+
eventData.push({ handler, config: { once } });
|
|
15846
|
+
// Warn if listener count exceeds the max allowed
|
|
15847
|
+
const max = this.#maxListeners;
|
|
15848
|
+
if (max > 0 && eventData.length > max) {
|
|
15849
|
+
const warnMessage =
|
|
15850
|
+
`Possible memory leak detected. ${eventData.length} "${event}" listeners added. ` +
|
|
15851
|
+
`Use setMaxListeners() to increase limit.`;
|
|
15852
|
+
if (!this.#throwMaxListeners) console.warn(warnMessage);
|
|
15853
|
+
else throw new Error(warnMessage);
|
|
15854
|
+
}
|
|
15855
|
+
}
|
|
15856
|
+
|
|
15857
|
+
/**
|
|
15858
|
+
* Adds a event listener.
|
|
15859
|
+
*
|
|
15860
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
15861
|
+
* @param {handler} handler - Callback function to be called when event fires.
|
|
15862
|
+
*/
|
|
15863
|
+
on(event, handler) {
|
|
15864
|
+
if (typeof event !== 'string')
|
|
15865
|
+
throw new TypeError('on(event, handler): event name must be a string');
|
|
15866
|
+
if (typeof handler !== 'function')
|
|
15867
|
+
throw new TypeError('on(event, handler): handler must be a function');
|
|
15868
|
+
return this.#on(event, handler);
|
|
15869
|
+
}
|
|
15870
|
+
|
|
15871
|
+
/**
|
|
15872
|
+
* Registers an event listener that runs only once, then is removed.
|
|
15873
|
+
*
|
|
15874
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
15875
|
+
* @param {handler} handler - The callback function to run on event.
|
|
15876
|
+
* @returns {handler} - The wrapped version of the handler.
|
|
15877
|
+
*/
|
|
15878
|
+
once(event, handler) {
|
|
15879
|
+
if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
|
|
15880
|
+
if (typeof handler !== 'function')
|
|
15881
|
+
throw new TypeError('once(event, handler): handler must be a function');
|
|
15882
|
+
|
|
15883
|
+
/** @type {handler} */
|
|
15884
|
+
const wrapped = (e) => {
|
|
15885
|
+
this.off(event, wrapped);
|
|
15886
|
+
if (typeof handler === 'function') handler(e);
|
|
15887
|
+
};
|
|
15888
|
+
this.#on(event, wrapped, { once: true });
|
|
15889
|
+
return wrapped;
|
|
15890
|
+
}
|
|
15891
|
+
|
|
15892
|
+
/**
|
|
15893
|
+
* Adds a event listener.
|
|
15894
|
+
*
|
|
15895
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
15896
|
+
* @param {handler} handler - Callback function to be called when event fires.
|
|
15897
|
+
*/
|
|
15898
|
+
appendListener(event, handler) {
|
|
15899
|
+
return this.on(event, handler);
|
|
15900
|
+
}
|
|
15901
|
+
|
|
15902
|
+
/**
|
|
15903
|
+
* Registers an event listener that runs only once, then is removed.
|
|
15904
|
+
*
|
|
15905
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
15906
|
+
* @param {handler} handler - The callback function to run on event.
|
|
15907
|
+
* @returns {handler} - The wrapped version of the handler.
|
|
15908
|
+
*/
|
|
15909
|
+
appendListenerOnce(event, handler) {
|
|
15910
|
+
return this.once(event, handler);
|
|
15911
|
+
}
|
|
15912
|
+
|
|
15913
|
+
///////////////////////////////////////////////
|
|
15914
|
+
|
|
15915
|
+
/**
|
|
15916
|
+
* Removes a previously registered event listener.
|
|
15917
|
+
*
|
|
15918
|
+
* @param {string} event - The name of the event to remove the handler from.
|
|
15919
|
+
* @param {handler} handler - The specific callback function to remove.
|
|
15920
|
+
*/
|
|
15921
|
+
off(event, handler) {
|
|
15922
|
+
if (typeof event !== 'string')
|
|
15923
|
+
throw new TypeError('off(event, handler): event name must be a string');
|
|
15924
|
+
if (typeof handler !== 'function')
|
|
15925
|
+
throw new TypeError('off(event, handler): handler must be a function');
|
|
15926
|
+
|
|
15927
|
+
const listeners = this.#listeners.get(event);
|
|
15928
|
+
if (!Array.isArray(listeners)) return;
|
|
15929
|
+
|
|
15930
|
+
const index = listeners.findIndex((listener) => listener.handler === handler);
|
|
15931
|
+
if (index !== -1) listeners.splice(index, 1);
|
|
15932
|
+
|
|
15933
|
+
// Optionally clean up empty arrays (optional)
|
|
15934
|
+
if (listeners.length === 0) this.#listeners.delete(event);
|
|
15935
|
+
}
|
|
15936
|
+
|
|
15937
|
+
/**
|
|
15938
|
+
* Removes all event listeners of a specific type from the element.
|
|
15939
|
+
*
|
|
15940
|
+
* @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
|
|
15941
|
+
*/
|
|
15942
|
+
offAll(event) {
|
|
15943
|
+
if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
|
|
15944
|
+
this.#listeners.delete(event);
|
|
15945
|
+
}
|
|
15946
|
+
|
|
15947
|
+
/**
|
|
15948
|
+
* Removes all event listeners of all types from the element.
|
|
15949
|
+
*/
|
|
15950
|
+
offAllTypes() {
|
|
15951
|
+
this.#listeners.clear();
|
|
15952
|
+
}
|
|
15953
|
+
|
|
15954
|
+
/////////////////////////////////////////////
|
|
15955
|
+
|
|
15956
|
+
/**
|
|
15957
|
+
* Returns the number of listeners for a given event.
|
|
15958
|
+
*
|
|
15959
|
+
* @param {string} event - The name of the event.
|
|
15960
|
+
* @returns {number} Number of listeners for the event.
|
|
15961
|
+
*/
|
|
15962
|
+
listenerCount(event) {
|
|
15963
|
+
if (typeof event !== 'string')
|
|
15964
|
+
throw new TypeError('listenerCount(event): event name must be a string');
|
|
15965
|
+
|
|
15966
|
+
const listeners = this.#listeners.get(event);
|
|
15967
|
+
return Array.isArray(listeners) ? listeners.length : 0;
|
|
15968
|
+
}
|
|
15969
|
+
|
|
15970
|
+
/**
|
|
15971
|
+
* Returns a copy of the array of listeners for the specified event.
|
|
15972
|
+
*
|
|
15973
|
+
* @param {string} event - The name of the event.
|
|
15974
|
+
* @returns {handler[]} Array of listener functions.
|
|
15975
|
+
*/
|
|
15976
|
+
listeners(event) {
|
|
15977
|
+
if (typeof event !== 'string')
|
|
15978
|
+
throw new TypeError('listeners(event): event name must be a string');
|
|
15979
|
+
|
|
15980
|
+
const listeners = this.#listeners.get(event);
|
|
15981
|
+
return Array.isArray(listeners)
|
|
15982
|
+
? [...listeners]
|
|
15983
|
+
.filter((listener) => !listener.config.once)
|
|
15984
|
+
.map((listener) => listener.handler)
|
|
15985
|
+
: [];
|
|
15986
|
+
}
|
|
15987
|
+
|
|
15988
|
+
/**
|
|
15989
|
+
* Returns a copy of the array of listeners for the specified event.
|
|
15990
|
+
*
|
|
15991
|
+
* @param {string} event - The name of the event.
|
|
15992
|
+
* @returns {handler[]} Array of listener functions.
|
|
15993
|
+
*/
|
|
15994
|
+
onceListeners(event) {
|
|
15995
|
+
if (typeof event !== 'string')
|
|
15996
|
+
throw new TypeError('onceListeners(event): event name must be a string');
|
|
15997
|
+
|
|
15998
|
+
const listeners = this.#listeners.get(event);
|
|
15999
|
+
return Array.isArray(listeners)
|
|
16000
|
+
? [...listeners]
|
|
16001
|
+
.filter((listener) => listener.config.once)
|
|
16002
|
+
.map((listener) => listener.handler)
|
|
16003
|
+
: [];
|
|
16004
|
+
}
|
|
16005
|
+
|
|
16006
|
+
/**
|
|
16007
|
+
* Returns a copy of the internal listeners array for the specified event,
|
|
16008
|
+
* including wrapper functions like those used by `.once()`.
|
|
16009
|
+
* @param {string | symbol} event - The event name.
|
|
16010
|
+
* @returns {handler[]} An array of raw listener functions.
|
|
16011
|
+
*/
|
|
16012
|
+
allListeners(event) {
|
|
16013
|
+
if (typeof event !== 'string')
|
|
16014
|
+
throw new TypeError('allListeners(event): event name must be a string');
|
|
16015
|
+
const listeners = this.#listeners.get(event);
|
|
16016
|
+
return Array.isArray(listeners) ? [...listeners].map((listener) => listener.handler) : [];
|
|
16017
|
+
}
|
|
16018
|
+
|
|
16019
|
+
/**
|
|
16020
|
+
* Returns an array of event names for which there are registered listeners.
|
|
16021
|
+
*
|
|
16022
|
+
* @returns {string[]} Array of registered event names.
|
|
16023
|
+
*/
|
|
16024
|
+
eventNames() {
|
|
16025
|
+
return [...this.#listeners.keys()];
|
|
16026
|
+
}
|
|
16027
|
+
|
|
16028
|
+
/**
|
|
16029
|
+
* Emits an event, triggering all registered handlers for that event.
|
|
16030
|
+
*
|
|
16031
|
+
* @param {string} event - The event name to emit.
|
|
16032
|
+
* @param {...any} payload - Optional data to pass to each handler.
|
|
16033
|
+
* @returns {boolean} True if any listeners were called, false otherwise.
|
|
16034
|
+
*/
|
|
16035
|
+
emit(event, ...payload) {
|
|
16036
|
+
if (typeof event !== 'string')
|
|
16037
|
+
throw new TypeError('emit(event, data): event name must be a string');
|
|
16038
|
+
|
|
16039
|
+
const listeners = this.#listeners.get(event);
|
|
16040
|
+
if (!Array.isArray(listeners) || listeners.length === 0) return false;
|
|
16041
|
+
|
|
16042
|
+
// Call all listeners with the provided data
|
|
16043
|
+
listeners.forEach((listener) => listener.handler(...payload));
|
|
16044
|
+
return true;
|
|
16045
|
+
}
|
|
16046
|
+
|
|
16047
|
+
///////////////////////////////////
|
|
16048
|
+
|
|
16049
|
+
/**
|
|
16050
|
+
* Sets the maximum number of listeners per event before a warning is shown.
|
|
16051
|
+
*
|
|
16052
|
+
* @param {number} n - The maximum number of listeners.
|
|
16053
|
+
*/
|
|
16054
|
+
setMaxListeners(n) {
|
|
16055
|
+
if (!Number.isInteger(n) || n < 0)
|
|
16056
|
+
throw new TypeError('setMaxListeners(n): n must be a non-negative integer');
|
|
16057
|
+
this.#maxListeners = n;
|
|
16058
|
+
}
|
|
16059
|
+
|
|
16060
|
+
/**
|
|
16061
|
+
* Gets the maximum number of listeners allowed per event.
|
|
16062
|
+
*
|
|
16063
|
+
* @returns {number} The maximum number of listeners.
|
|
16064
|
+
*/
|
|
16065
|
+
getMaxListeners() {
|
|
16066
|
+
return this.#maxListeners;
|
|
16067
|
+
}
|
|
16068
|
+
}
|
|
16069
|
+
|
|
16070
|
+
/* harmony default export */ const libs_TinyEvents = (TinyEvents);
|
|
16071
|
+
|
|
15697
16072
|
;// ./src/v1/libs/TinySmartScroller.mjs
|
|
15698
16073
|
|
|
15699
16074
|
|
|
15700
16075
|
|
|
16076
|
+
|
|
15701
16077
|
/**
|
|
15702
16078
|
* Represents the dimensions of a DOM element.
|
|
15703
16079
|
*
|
|
@@ -15744,71 +16120,267 @@ class UltraRandomMsgGen {
|
|
|
15744
16120
|
class TinySmartScroller {
|
|
15745
16121
|
static Utils = { ...collision_namespaceObject, TinyHtml: libs_TinyHtml };
|
|
15746
16122
|
|
|
15747
|
-
|
|
15748
|
-
#oldSizes = new WeakMap();
|
|
15749
|
-
/** @type {WeakMap<Element, NodeSizes>} */
|
|
15750
|
-
#newSizes = new WeakMap();
|
|
15751
|
-
|
|
15752
|
-
/** @type {WeakMap<Element, boolean>} */
|
|
15753
|
-
#newVisibles = new WeakMap();
|
|
15754
|
-
/** @type {WeakMap<Element, boolean>} */
|
|
15755
|
-
#oldVisibles = new WeakMap();
|
|
15756
|
-
|
|
15757
|
-
/** @type {WeakMap<Element, boolean>} */
|
|
15758
|
-
#newVisiblesByTime = new WeakMap();
|
|
15759
|
-
/** @type {WeakMap<Element, boolean>} */
|
|
15760
|
-
#oldVisiblesByTime = new WeakMap();
|
|
15761
|
-
|
|
15762
|
-
/** @type {Record<string, ScrollListenersFunc[]>} */
|
|
15763
|
-
#scrollListeners = {};
|
|
15764
|
-
|
|
15765
|
-
/** @type {ResizeObserver|null} */
|
|
15766
|
-
#resizeObserver = null;
|
|
15767
|
-
/** @type {MutationObserver|null} */
|
|
15768
|
-
#mutationObserver = null;
|
|
15769
|
-
|
|
15770
|
-
/** @type {Set<string>} */
|
|
15771
|
-
#loadTags = new Set(['IMG', 'IFRAME', 'VIDEO']);
|
|
16123
|
+
#events = new libs_TinyEvents();
|
|
15772
16124
|
|
|
15773
|
-
/**
|
|
15774
|
-
|
|
16125
|
+
/**
|
|
16126
|
+
* Enables or disables throwing an error when the maximum number of listeners is exceeded.
|
|
16127
|
+
*
|
|
16128
|
+
* @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
|
|
16129
|
+
*/
|
|
16130
|
+
setThrowOnMaxListeners(shouldThrow) {
|
|
16131
|
+
return this.#events.setThrowOnMaxListeners(shouldThrow);
|
|
16132
|
+
}
|
|
15775
16133
|
|
|
15776
|
-
|
|
15777
|
-
|
|
15778
|
-
|
|
15779
|
-
|
|
16134
|
+
/**
|
|
16135
|
+
* Checks whether an error will be thrown when the max listener limit is exceeded.
|
|
16136
|
+
*
|
|
16137
|
+
* @returns {boolean} True if an error will be thrown, false if only a warning is shown.
|
|
16138
|
+
*/
|
|
16139
|
+
getThrowOnMaxListeners() {
|
|
16140
|
+
return this.#events.getThrowOnMaxListeners();
|
|
16141
|
+
}
|
|
15780
16142
|
|
|
15781
|
-
|
|
15782
|
-
#useWindow = false;
|
|
15783
|
-
#destroyed = false;
|
|
15784
|
-
#scrollPaused = false;
|
|
15785
|
-
#autoScrollBottom = false;
|
|
15786
|
-
#observeMutations = false;
|
|
15787
|
-
#preserveScrollOnLayoutShift = false;
|
|
15788
|
-
#debounceTime = 0;
|
|
15789
|
-
#elemAmount = 0;
|
|
15790
|
-
#elemOldAmount = 0;
|
|
15791
|
-
#lastKnownScrollBottomOffset = 0;
|
|
15792
|
-
#extraScrollBoundary = 0;
|
|
16143
|
+
/////////////////////////////////////////////////////////////
|
|
15793
16144
|
|
|
15794
|
-
/**
|
|
15795
|
-
|
|
16145
|
+
/**
|
|
16146
|
+
* Adds a listener to the beginning of the listeners array for the specified event.
|
|
16147
|
+
*
|
|
16148
|
+
* @param {string} event - Event name.
|
|
16149
|
+
* @param {ScrollListenersFunc} handler - The callback function.
|
|
16150
|
+
*/
|
|
16151
|
+
prependListener(event, handler) {
|
|
16152
|
+
return this.#events.prependListener(event, handler);
|
|
16153
|
+
}
|
|
15796
16154
|
|
|
15797
|
-
/**
|
|
15798
|
-
|
|
16155
|
+
/**
|
|
16156
|
+
* Adds a one-time listener to the beginning of the listeners array for the specified event.
|
|
16157
|
+
*
|
|
16158
|
+
* @param {string} event - Event name.
|
|
16159
|
+
* @param {ScrollListenersFunc} handler - The callback function.
|
|
16160
|
+
* @returns {ScrollListenersFunc} - The wrapped handler used internally.
|
|
16161
|
+
*/
|
|
16162
|
+
prependListenerOnce(event, handler) {
|
|
16163
|
+
return this.#events.prependListenerOnce(event, handler);
|
|
16164
|
+
}
|
|
15799
16165
|
|
|
15800
|
-
|
|
15801
|
-
#sizeFilter = new Set();
|
|
16166
|
+
//////////////////////////////////////////////////////////////////////
|
|
15802
16167
|
|
|
15803
16168
|
/**
|
|
15804
|
-
*
|
|
15805
|
-
* automatic scroll behaviors, layout shift correction, and visibility tracking.
|
|
16169
|
+
* Adds a event listener.
|
|
15806
16170
|
*
|
|
15807
|
-
* @param {
|
|
15808
|
-
* @param {
|
|
15809
|
-
|
|
15810
|
-
|
|
15811
|
-
|
|
16171
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
16172
|
+
* @param {ScrollListenersFunc} handler - Callback function to be called when event fires.
|
|
16173
|
+
*/
|
|
16174
|
+
appendListener(event, handler) {
|
|
16175
|
+
return this.#events.appendListener(event, handler);
|
|
16176
|
+
}
|
|
16177
|
+
|
|
16178
|
+
/**
|
|
16179
|
+
* Registers an event listener that runs only once, then is removed.
|
|
16180
|
+
*
|
|
16181
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
16182
|
+
* @param {ScrollListenersFunc} handler - The callback function to run on event.
|
|
16183
|
+
* @returns {ScrollListenersFunc} - The wrapped version of the handler.
|
|
16184
|
+
*/
|
|
16185
|
+
appendListenerOnce(event, handler) {
|
|
16186
|
+
return this.#events.appendListenerOnce(event, handler);
|
|
16187
|
+
}
|
|
16188
|
+
|
|
16189
|
+
/**
|
|
16190
|
+
* Adds a event listener.
|
|
16191
|
+
*
|
|
16192
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
16193
|
+
* @param {ScrollListenersFunc} handler - Callback function to be called when event fires.
|
|
16194
|
+
*/
|
|
16195
|
+
on(event, handler) {
|
|
16196
|
+
return this.#events.on(event, handler);
|
|
16197
|
+
}
|
|
16198
|
+
|
|
16199
|
+
/**
|
|
16200
|
+
* Registers an event listener that runs only once, then is removed.
|
|
16201
|
+
*
|
|
16202
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
16203
|
+
* @param {ScrollListenersFunc} handler - The callback function to run on event.
|
|
16204
|
+
* @returns {ScrollListenersFunc} - The wrapped version of the handler.
|
|
16205
|
+
*/
|
|
16206
|
+
once(event, handler) {
|
|
16207
|
+
return this.#events.once(event, handler);
|
|
16208
|
+
}
|
|
16209
|
+
|
|
16210
|
+
////////////////////////////////////////////////////////////////////
|
|
16211
|
+
|
|
16212
|
+
/**
|
|
16213
|
+
* Removes a previously registered event listener.
|
|
16214
|
+
*
|
|
16215
|
+
* @param {string} event - The name of the event to remove the handler from.
|
|
16216
|
+
* @param {ScrollListenersFunc} handler - The specific callback function to remove.
|
|
16217
|
+
*/
|
|
16218
|
+
off(event, handler) {
|
|
16219
|
+
return this.#events.off(event, handler);
|
|
16220
|
+
}
|
|
16221
|
+
|
|
16222
|
+
/**
|
|
16223
|
+
* Removes all event listeners of a specific type from the element.
|
|
16224
|
+
*
|
|
16225
|
+
* @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
|
|
16226
|
+
*/
|
|
16227
|
+
offAll(event) {
|
|
16228
|
+
return this.#events.offAll(event);
|
|
16229
|
+
}
|
|
16230
|
+
|
|
16231
|
+
/**
|
|
16232
|
+
* Removes all event listeners of all types from the element.
|
|
16233
|
+
*/
|
|
16234
|
+
offAllTypes() {
|
|
16235
|
+
return this.#events.offAllTypes();
|
|
16236
|
+
}
|
|
16237
|
+
|
|
16238
|
+
////////////////////////////////////////////////////////////
|
|
16239
|
+
|
|
16240
|
+
/**
|
|
16241
|
+
* Returns the number of listeners for a given event.
|
|
16242
|
+
*
|
|
16243
|
+
* @param {string} event - The name of the event.
|
|
16244
|
+
* @returns {number} Number of listeners for the event.
|
|
16245
|
+
*/
|
|
16246
|
+
listenerCount(event) {
|
|
16247
|
+
return this.#events.listenerCount(event);
|
|
16248
|
+
}
|
|
16249
|
+
|
|
16250
|
+
/**
|
|
16251
|
+
* Returns a copy of the array of listeners for the specified event.
|
|
16252
|
+
*
|
|
16253
|
+
* @param {string} event - The name of the event.
|
|
16254
|
+
* @returns {ScrollListenersFunc[]} Array of listener functions.
|
|
16255
|
+
*/
|
|
16256
|
+
listeners(event) {
|
|
16257
|
+
return this.#events.listeners(event);
|
|
16258
|
+
}
|
|
16259
|
+
|
|
16260
|
+
/**
|
|
16261
|
+
* Returns a copy of the array of listeners for the specified event.
|
|
16262
|
+
*
|
|
16263
|
+
* @param {string} event - The name of the event.
|
|
16264
|
+
* @returns {ScrollListenersFunc[]} Array of listener functions.
|
|
16265
|
+
*/
|
|
16266
|
+
onceListeners(event) {
|
|
16267
|
+
return this.#events.onceListeners(event);
|
|
16268
|
+
}
|
|
16269
|
+
|
|
16270
|
+
/**
|
|
16271
|
+
* Returns a copy of the internal listeners array for the specified event,
|
|
16272
|
+
* including wrapper functions like those used by `.once()`.
|
|
16273
|
+
* @param {string | symbol} event - The event name.
|
|
16274
|
+
* @returns {ScrollListenersFunc[]} An array of raw listener functions.
|
|
16275
|
+
*/
|
|
16276
|
+
allListeners(event) {
|
|
16277
|
+
return this.#events.allListeners(event);
|
|
16278
|
+
}
|
|
16279
|
+
|
|
16280
|
+
/**
|
|
16281
|
+
* Returns an array of event names for which there are registered listeners.
|
|
16282
|
+
*
|
|
16283
|
+
* @returns {string[]} Array of registered event names.
|
|
16284
|
+
*/
|
|
16285
|
+
eventNames() {
|
|
16286
|
+
return this.#events.eventNames();
|
|
16287
|
+
}
|
|
16288
|
+
|
|
16289
|
+
//////////////////////////////////////////////////////
|
|
16290
|
+
|
|
16291
|
+
/**
|
|
16292
|
+
* Emits an event, triggering all registered handlers for that event.
|
|
16293
|
+
*
|
|
16294
|
+
* @param {string} event - The event name to emit.
|
|
16295
|
+
* @param {...any} payload - Optional data to pass to each handler.
|
|
16296
|
+
* @returns {boolean} True if any listeners were called, false otherwise.
|
|
16297
|
+
*/
|
|
16298
|
+
emit(event, ...payload) {
|
|
16299
|
+
return this.#events.emit(event, ...payload);
|
|
16300
|
+
}
|
|
16301
|
+
|
|
16302
|
+
/**
|
|
16303
|
+
* Sets the maximum number of listeners per event before a warning is shown.
|
|
16304
|
+
*
|
|
16305
|
+
* @param {number} n - The maximum number of listeners.
|
|
16306
|
+
*/
|
|
16307
|
+
setMaxListeners(n) {
|
|
16308
|
+
return this.#events.setMaxListeners(n);
|
|
16309
|
+
}
|
|
16310
|
+
|
|
16311
|
+
/**
|
|
16312
|
+
* Gets the maximum number of listeners allowed per event.
|
|
16313
|
+
*
|
|
16314
|
+
* @returns {number} The maximum number of listeners.
|
|
16315
|
+
*/
|
|
16316
|
+
getMaxListeners() {
|
|
16317
|
+
return this.#events.getMaxListeners();
|
|
16318
|
+
}
|
|
16319
|
+
|
|
16320
|
+
///////////////////////////////////////////////////
|
|
16321
|
+
|
|
16322
|
+
/** @type {WeakMap<Element, NodeSizes>} */
|
|
16323
|
+
#oldSizes = new WeakMap();
|
|
16324
|
+
/** @type {WeakMap<Element, NodeSizes>} */
|
|
16325
|
+
#newSizes = new WeakMap();
|
|
16326
|
+
|
|
16327
|
+
/** @type {WeakMap<Element, boolean>} */
|
|
16328
|
+
#newVisibles = new WeakMap();
|
|
16329
|
+
/** @type {WeakMap<Element, boolean>} */
|
|
16330
|
+
#oldVisibles = new WeakMap();
|
|
16331
|
+
|
|
16332
|
+
/** @type {WeakMap<Element, boolean>} */
|
|
16333
|
+
#newVisiblesByTime = new WeakMap();
|
|
16334
|
+
/** @type {WeakMap<Element, boolean>} */
|
|
16335
|
+
#oldVisiblesByTime = new WeakMap();
|
|
16336
|
+
|
|
16337
|
+
/** @type {ResizeObserver|null} */
|
|
16338
|
+
#resizeObserver = null;
|
|
16339
|
+
/** @type {MutationObserver|null} */
|
|
16340
|
+
#mutationObserver = null;
|
|
16341
|
+
|
|
16342
|
+
/** @type {Set<string>} */
|
|
16343
|
+
#loadTags = new Set(['IMG', 'IFRAME', 'VIDEO']);
|
|
16344
|
+
|
|
16345
|
+
/** @type {null|EventListenerOrEventListenerObject} */
|
|
16346
|
+
#handler = null;
|
|
16347
|
+
|
|
16348
|
+
#isAtBottom = false;
|
|
16349
|
+
#isAtTop = false;
|
|
16350
|
+
#isAtCustomTop = false;
|
|
16351
|
+
#isAtCustomBottom = false;
|
|
16352
|
+
|
|
16353
|
+
#querySelector = '';
|
|
16354
|
+
#useWindow = false;
|
|
16355
|
+
#destroyed = false;
|
|
16356
|
+
#scrollPaused = false;
|
|
16357
|
+
#autoScrollBottom = false;
|
|
16358
|
+
#observeMutations = false;
|
|
16359
|
+
#preserveScrollOnLayoutShift = false;
|
|
16360
|
+
#debounceTime = 0;
|
|
16361
|
+
#elemAmount = 0;
|
|
16362
|
+
#elemOldAmount = 0;
|
|
16363
|
+
#lastKnownScrollBottomOffset = 0;
|
|
16364
|
+
#extraScrollBoundary = 0;
|
|
16365
|
+
|
|
16366
|
+
/** @type {Set<string>} */
|
|
16367
|
+
#attributeFilter;
|
|
16368
|
+
|
|
16369
|
+
/** @type {Element} */
|
|
16370
|
+
#target;
|
|
16371
|
+
|
|
16372
|
+
/** @type {Set<NodeSizesEvent>} */
|
|
16373
|
+
#sizeFilter = new Set();
|
|
16374
|
+
|
|
16375
|
+
/**
|
|
16376
|
+
* Creates a new instance of TinySmartScroller, attaching scroll and resize observers to manage
|
|
16377
|
+
* automatic scroll behaviors, layout shift correction, and visibility tracking.
|
|
16378
|
+
*
|
|
16379
|
+
* @param {Element|Window} target - The scroll container to monitor. Can be an element or `window`.
|
|
16380
|
+
* @param {Object} [options={}] - Optional settings to configure scroll behavior.
|
|
16381
|
+
* @param {number} [options.extraScrollBoundary=0] - Extra margin in pixels to extend scroll boundary detection.
|
|
16382
|
+
* @param {boolean} [options.autoScrollBottom=true] - Whether to auto-scroll to bottom on layout updates.
|
|
16383
|
+
* @param {boolean} [options.observeMutations=true] - Enables MutationObserver to detect DOM changes.
|
|
15812
16384
|
* @param {boolean} [options.preserveScrollOnLayoutShift=true] - Prevents scroll jumps when layout changes.
|
|
15813
16385
|
* @param {number} [options.debounceTime=100] - Debounce time in milliseconds for scroll events.
|
|
15814
16386
|
* @param {string|null} [options.querySelector=null] - Optional CSS selector to filter observed child nodes.
|
|
@@ -16075,46 +16647,6 @@ class TinySmartScroller {
|
|
|
16075
16647
|
this.#sizeFilter.delete(handler);
|
|
16076
16648
|
}
|
|
16077
16649
|
|
|
16078
|
-
/**
|
|
16079
|
-
* Adds a scroll-related event listener.
|
|
16080
|
-
*
|
|
16081
|
-
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
16082
|
-
* @param {ScrollListenersFunc} handler - Callback function to be called when event fires.
|
|
16083
|
-
*/
|
|
16084
|
-
on(event, handler) {
|
|
16085
|
-
if (this.#destroyed) return;
|
|
16086
|
-
if (typeof event !== 'string')
|
|
16087
|
-
throw new TypeError('on(event, handler): event name must be a string');
|
|
16088
|
-
if (typeof handler !== 'function')
|
|
16089
|
-
throw new TypeError('on(event, handler): handler must be a function');
|
|
16090
|
-
|
|
16091
|
-
if (!this.#scrollListeners[event]) this.#scrollListeners[event] = [];
|
|
16092
|
-
this.#scrollListeners[event].push(handler);
|
|
16093
|
-
}
|
|
16094
|
-
|
|
16095
|
-
/**
|
|
16096
|
-
* Removes a previously registered scroll-related event listener.
|
|
16097
|
-
*
|
|
16098
|
-
* @param {string} event - The name of the event to remove the handler from.
|
|
16099
|
-
* @param {ScrollListenersFunc} handler - The specific callback function to remove.
|
|
16100
|
-
*/
|
|
16101
|
-
off(event, handler) {
|
|
16102
|
-
if (this.#destroyed) return;
|
|
16103
|
-
if (typeof event !== 'string')
|
|
16104
|
-
throw new TypeError('off(event, handler): event name must be a string');
|
|
16105
|
-
if (typeof handler !== 'function')
|
|
16106
|
-
throw new TypeError('off(event, handler): handler must be a function');
|
|
16107
|
-
|
|
16108
|
-
const listeners = this.#scrollListeners[event];
|
|
16109
|
-
if (!Array.isArray(listeners)) return;
|
|
16110
|
-
|
|
16111
|
-
const index = listeners.indexOf(handler);
|
|
16112
|
-
if (index !== -1) listeners.splice(index, 1);
|
|
16113
|
-
|
|
16114
|
-
// Optionally clean up empty arrays (optional)
|
|
16115
|
-
if (listeners.length === 0) delete this.#scrollListeners[event];
|
|
16116
|
-
}
|
|
16117
|
-
|
|
16118
16650
|
/**
|
|
16119
16651
|
* Checks which elements inside the target are currently visible and updates internal maps.
|
|
16120
16652
|
*
|
|
@@ -16139,12 +16671,10 @@ class TinySmartScroller {
|
|
|
16139
16671
|
*
|
|
16140
16672
|
* @param {string} event - Event name.
|
|
16141
16673
|
* @param {*} [payload] - Optional event data payload.
|
|
16674
|
+
* @deprecated - Use emit() instead.
|
|
16142
16675
|
*/
|
|
16143
16676
|
_emit(event, payload) {
|
|
16144
|
-
|
|
16145
|
-
if (typeof event !== 'string')
|
|
16146
|
-
throw new TypeError('_emit(event, payload): event name must be a string');
|
|
16147
|
-
(this.#scrollListeners[event] || []).forEach((fn) => fn(payload));
|
|
16677
|
+
this.emit(event, payload);
|
|
16148
16678
|
}
|
|
16149
16679
|
|
|
16150
16680
|
/**
|
|
@@ -16190,13 +16720,13 @@ class TinySmartScroller {
|
|
|
16190
16720
|
this.#lastKnownScrollBottomOffset = scrollHeight - scrollTop - clientHeight;
|
|
16191
16721
|
|
|
16192
16722
|
// Send results
|
|
16193
|
-
this.
|
|
16194
|
-
this.
|
|
16723
|
+
this.emit('onScrollBoundary', { status: atResult, ...scrollResult, scrollCache });
|
|
16724
|
+
this.emit('onExtraScrollBoundary', { status: atCustomResult, ...scrollResult, scrollCache });
|
|
16195
16725
|
|
|
16196
16726
|
if (!this.#scrollPaused) {
|
|
16197
|
-
this.
|
|
16727
|
+
this.emit('onAutoScroll', { ...scrollResult, scrollCache });
|
|
16198
16728
|
} else {
|
|
16199
|
-
this.
|
|
16729
|
+
this.emit('onScrollPause', { ...scrollResult, scrollCache });
|
|
16200
16730
|
}
|
|
16201
16731
|
}
|
|
16202
16732
|
|
|
@@ -16662,7 +17192,7 @@ class TinySmartScroller {
|
|
|
16662
17192
|
this.#oldVisiblesByTime = new WeakMap();
|
|
16663
17193
|
|
|
16664
17194
|
// Cleans listeners and filters
|
|
16665
|
-
this.#
|
|
17195
|
+
this.#events.offAllTypes();
|
|
16666
17196
|
this.#sizeFilter.clear();
|
|
16667
17197
|
this.#loadTags.clear();
|
|
16668
17198
|
}
|
|
@@ -17031,522 +17561,2141 @@ class TinyTextRangeEditor {
|
|
|
17031
17561
|
}
|
|
17032
17562
|
|
|
17033
17563
|
/**
|
|
17034
|
-
* Wraps the current selection with a tag, optionally including attributes.
|
|
17564
|
+
* Wraps the current selection with a tag, optionally including attributes.
|
|
17565
|
+
*
|
|
17566
|
+
* @param {string} tagName - The tag name (e.g., `b`, `color`, etc.).
|
|
17567
|
+
* @param {Record<string,string> | string[]} [attributes={}] - Optional attributes for the opening tag.
|
|
17568
|
+
* - If an object: key-value pairs (e.g., `{ color: "red" }` → `color="red"`).
|
|
17569
|
+
* - If an array: boolean attributes (e.g., `["disabled", "readonly"]`).
|
|
17570
|
+
*
|
|
17571
|
+
* @returns {TinyTextRangeEditor}
|
|
17572
|
+
*/
|
|
17573
|
+
wrapWithTag(tagName, attributes = {}) {
|
|
17574
|
+
if (typeof tagName !== 'string') throw new TypeError('tagName must be a string.');
|
|
17575
|
+
const attrStr = this._insertAttr(attributes);
|
|
17576
|
+
const openTag = attrStr
|
|
17577
|
+
? `${this.#openTag}${tagName} ${attrStr}${this.#closeTag}`
|
|
17578
|
+
: `${this.#openTag}${tagName}${this.#closeTag}`;
|
|
17579
|
+
const closeTag = `${this.#openTag}/${tagName}${this.#closeTag}`;
|
|
17580
|
+
this.surroundSelection(openTag, closeTag);
|
|
17581
|
+
return this;
|
|
17582
|
+
}
|
|
17583
|
+
|
|
17584
|
+
/**
|
|
17585
|
+
* Inserts a tag with optional inner content.
|
|
17586
|
+
* @param {string} tagName - The tag to insert.
|
|
17587
|
+
* @param {string} [content=''] - Optional content between tags.
|
|
17588
|
+
* @param {Record<string,string> | string[]} [attributes={}] - Optional attributes or list of empty attributes.
|
|
17589
|
+
* @returns {TinyTextRangeEditor}
|
|
17590
|
+
*/
|
|
17591
|
+
insertTag(tagName, content = '', attributes = {}) {
|
|
17592
|
+
if (typeof tagName !== 'string') throw new TypeError('tagName must be a string.');
|
|
17593
|
+
if (typeof content !== 'string') throw new TypeError('content must be a string.');
|
|
17594
|
+
|
|
17595
|
+
const attrStr = this._insertAttr(attributes);
|
|
17596
|
+
const open = attrStr
|
|
17597
|
+
? `${this.#openTag}${tagName} ${attrStr}${this.#closeTag}`
|
|
17598
|
+
: `${this.#openTag}${tagName}${this.#closeTag}`;
|
|
17599
|
+
const close = `${this.#openTag}/${tagName}${this.#closeTag}`;
|
|
17600
|
+
|
|
17601
|
+
this.insertText(`${open}${content}${close}`);
|
|
17602
|
+
return this;
|
|
17603
|
+
}
|
|
17604
|
+
|
|
17605
|
+
/**
|
|
17606
|
+
* Inserts a self-closing tag.
|
|
17607
|
+
* @param {string} tagName - The tag name.
|
|
17608
|
+
* @param {Record<string,string> | string[]} [attributes={}] - Optional attributes or list of empty attributes.
|
|
17609
|
+
* @returns {TinyTextRangeEditor}
|
|
17610
|
+
*/
|
|
17611
|
+
insertSelfClosingTag(tagName, attributes = {}) {
|
|
17612
|
+
if (typeof tagName !== 'string') throw new TypeError('tagName must be a string.');
|
|
17613
|
+
|
|
17614
|
+
const attrStr = this._insertAttr(attributes);
|
|
17615
|
+
const tag = attrStr
|
|
17616
|
+
? `${this.#openTag}${tagName} ${attrStr}${this.#closeTag}`
|
|
17617
|
+
: `${this.#openTag}${tagName}${this.#closeTag}`;
|
|
17618
|
+
|
|
17619
|
+
this.insertText(tag);
|
|
17620
|
+
return this;
|
|
17621
|
+
}
|
|
17622
|
+
|
|
17623
|
+
/**
|
|
17624
|
+
* Toggles a tag around the current selection.
|
|
17625
|
+
* Supports tags with attributes. If already wrapped, it unwraps.
|
|
17626
|
+
* @param {string} tagName - The tag to toggle.
|
|
17627
|
+
* @param {Record<string,string> | string[]} [attributes={}] - Optional attributes to apply when wrapping.
|
|
17628
|
+
* @returns {TinyTextRangeEditor}
|
|
17629
|
+
*/
|
|
17630
|
+
toggleTag(tagName, attributes = {}) {
|
|
17631
|
+
if (typeof tagName !== 'string') throw new TypeError('tagName must be a string.');
|
|
17632
|
+
const selected = this.getSelectedText();
|
|
17633
|
+
|
|
17634
|
+
// Regex: opening tag with optional attributes, and closing tag
|
|
17635
|
+
const openRegex = new RegExp(`^\\[${tagName}(\\s+[^\\]]*)?\\]`);
|
|
17636
|
+
const closeRegex = new RegExp(`\\[/${tagName}\\]$`);
|
|
17637
|
+
|
|
17638
|
+
const hasOpen = openRegex.test(selected);
|
|
17639
|
+
const hasClose = closeRegex.test(selected);
|
|
17640
|
+
|
|
17641
|
+
if (hasOpen && hasClose) {
|
|
17642
|
+
const unwrapped = selected
|
|
17643
|
+
.replace(openRegex, '') // remove opening tag
|
|
17644
|
+
.replace(closeRegex, ''); // remove closing tag
|
|
17645
|
+
this.insertText(unwrapped);
|
|
17646
|
+
} else {
|
|
17647
|
+
const attrStr = this._insertAttr(attributes);
|
|
17648
|
+
const open = attrStr
|
|
17649
|
+
? `${this.#openTag}${tagName} ${attrStr}${this.#closeTag}`
|
|
17650
|
+
: `${this.#openTag}${tagName}${this.#closeTag}`;
|
|
17651
|
+
const close = `${this.#openTag}/${tagName}${this.#closeTag}`;
|
|
17652
|
+
this.insertText(`${open}${selected}${close}`);
|
|
17653
|
+
}
|
|
17654
|
+
|
|
17655
|
+
return this;
|
|
17656
|
+
}
|
|
17657
|
+
}
|
|
17658
|
+
|
|
17659
|
+
/* harmony default export */ const libs_TinyTextRangeEditor = (TinyTextRangeEditor);
|
|
17660
|
+
|
|
17661
|
+
;// ./src/v1/libs/TinyClipboard.mjs
|
|
17662
|
+
/**
|
|
17663
|
+
* Utility class to handle clipboard operations for text and blob data.
|
|
17664
|
+
* Supports modern Clipboard API, custom platform, and legacy execCommand fallback.
|
|
17665
|
+
*/
|
|
17666
|
+
class TinyClipboard {
|
|
17667
|
+
/**
|
|
17668
|
+
* Indicates whether the legacy `document.execCommand()` API is available.
|
|
17669
|
+
* Used as a fallback for clipboard operations when modern APIs are not supported.
|
|
17670
|
+
*
|
|
17671
|
+
* @type {boolean}
|
|
17672
|
+
*/
|
|
17673
|
+
#existExecCommand = false;
|
|
17674
|
+
|
|
17675
|
+
/**
|
|
17676
|
+
* Indicates whether the modern Clipboard API (`navigator.clipboard`) is available.
|
|
17677
|
+
*
|
|
17678
|
+
* @type {boolean}
|
|
17679
|
+
*/
|
|
17680
|
+
#existNavigator = false;
|
|
17681
|
+
|
|
17682
|
+
/**
|
|
17683
|
+
* Function used to copy plain text to the clipboard.
|
|
17684
|
+
* Can be overridden using `setCopyText()`.
|
|
17685
|
+
*
|
|
17686
|
+
* @type {((text: string) => Promise<void>) | null}
|
|
17687
|
+
*/
|
|
17688
|
+
#copyText = null;
|
|
17689
|
+
|
|
17690
|
+
/**
|
|
17691
|
+
* Function used to copy a Blob (binary data) to the clipboard.
|
|
17692
|
+
* Can be overridden using `setCopyBlob()`.
|
|
17693
|
+
*
|
|
17694
|
+
* @type {((blob: Blob) => Promise<void>) | null}
|
|
17695
|
+
*/
|
|
17696
|
+
#copyBlob = null;
|
|
17697
|
+
|
|
17698
|
+
/**
|
|
17699
|
+
* Constructs a new TinyClipboard instance.
|
|
17700
|
+
* Automatically detects and configures available clipboard APIs.
|
|
17701
|
+
*/
|
|
17702
|
+
constructor() {
|
|
17703
|
+
// Whether the Clipboard API is available.
|
|
17704
|
+
if (typeof navigator.clipboard !== 'undefined' && navigator.clipboard !== null) {
|
|
17705
|
+
this.#existNavigator = true;
|
|
17706
|
+
this.#copyText = (text) => navigator.clipboard.writeText(text);
|
|
17707
|
+
this.#copyBlob = (blob) =>
|
|
17708
|
+
navigator.clipboard.write([
|
|
17709
|
+
new ClipboardItem({
|
|
17710
|
+
[blob.type]: blob,
|
|
17711
|
+
}),
|
|
17712
|
+
]);
|
|
17713
|
+
}
|
|
17714
|
+
|
|
17715
|
+
/**
|
|
17716
|
+
* @type {boolean}
|
|
17717
|
+
* Whether the legacy execCommand API is available.
|
|
17718
|
+
*/
|
|
17719
|
+
this.#existExecCommand =
|
|
17720
|
+
typeof document.execCommand !== 'undefined' && document.execCommand !== null;
|
|
17721
|
+
}
|
|
17722
|
+
|
|
17723
|
+
/**
|
|
17724
|
+
* Override the default text copy behavior.
|
|
17725
|
+
* This allows you to provide your own clipboard implementation or
|
|
17726
|
+
* integrate with external systems like Capacitor or Electron.
|
|
17727
|
+
*
|
|
17728
|
+
* @param {(text: string) => Promise<void>} callback - The function to use for copying text.
|
|
17729
|
+
* @throws {TypeError} If the callback is not a function.
|
|
17730
|
+
*/
|
|
17731
|
+
setCopyText(callback) {
|
|
17732
|
+
if (typeof callback !== 'function')
|
|
17733
|
+
throw new TypeError('setCopyText expected a function that returns Promise<void>.');
|
|
17734
|
+
this.#copyText = callback;
|
|
17735
|
+
}
|
|
17736
|
+
|
|
17737
|
+
/**
|
|
17738
|
+
* Override the default blob copy behavior.
|
|
17739
|
+
* This allows you to provide a custom clipboard handling method for blob data.
|
|
17740
|
+
*
|
|
17741
|
+
* @param {(blob: Blob) => Promise<void>} callback - The function to use for copying blob data.
|
|
17742
|
+
* @throws {TypeError} If the callback is not a function.
|
|
17743
|
+
*/
|
|
17744
|
+
setCopyBlob(callback) {
|
|
17745
|
+
if (typeof callback !== 'function')
|
|
17746
|
+
throw new TypeError('setCopyBlob expected a function that returns Promise<void>.');
|
|
17747
|
+
this.#copyBlob = callback;
|
|
17748
|
+
}
|
|
17749
|
+
|
|
17750
|
+
/**
|
|
17751
|
+
* Copy a plain text string to the clipboard.
|
|
17752
|
+
* Uses modern or legacy fallback.
|
|
17753
|
+
*
|
|
17754
|
+
* @param {string} text - The text string to be copied.
|
|
17755
|
+
* @returns {Promise<void>} A promise resolving when the text is copied or boolean for legacy.
|
|
17756
|
+
*/
|
|
17757
|
+
copyText(text) {
|
|
17758
|
+
if (typeof text !== 'string') throw new TypeError('copyText expected a string.');
|
|
17759
|
+
// Clipboard API
|
|
17760
|
+
if (this.#copyText) return this.#copyText(text);
|
|
17761
|
+
// Classic API
|
|
17762
|
+
else if (this.#existExecCommand) {
|
|
17763
|
+
const host = document.body;
|
|
17764
|
+
const copyInput = document.createElement('input');
|
|
17765
|
+
copyInput.style.position = 'fixed';
|
|
17766
|
+
copyInput.style.opacity = '0';
|
|
17767
|
+
copyInput.value = text;
|
|
17768
|
+
host.append(copyInput);
|
|
17769
|
+
|
|
17770
|
+
copyInput.select();
|
|
17771
|
+
copyInput.setSelectionRange(0, 99999);
|
|
17772
|
+
document.execCommand('Copy');
|
|
17773
|
+
copyInput.remove();
|
|
17774
|
+
return new Promise((resolve) => resolve(undefined));
|
|
17775
|
+
}
|
|
17776
|
+
throw new Error('Clipboard API not found!');
|
|
17777
|
+
}
|
|
17778
|
+
|
|
17779
|
+
/**
|
|
17780
|
+
* Copy a Blob (binary data) to the clipboard.
|
|
17781
|
+
*
|
|
17782
|
+
* @param {Blob} blob - The blob object to copy.
|
|
17783
|
+
* @returns {Promise<void>} A promise that resolves when the blob is copied or null on fallback.
|
|
17784
|
+
*/
|
|
17785
|
+
copyBlob(blob) {
|
|
17786
|
+
if (!(blob instanceof Blob)) throw new TypeError('copyBlob expected a Blob instance.');
|
|
17787
|
+
return new Promise((resolve, reject) => {
|
|
17788
|
+
if (this.#copyBlob) {
|
|
17789
|
+
return this.#copyBlob(blob).then(resolve).catch(reject);
|
|
17790
|
+
}
|
|
17791
|
+
throw new Error('Clipboard API not found!');
|
|
17792
|
+
});
|
|
17793
|
+
}
|
|
17794
|
+
|
|
17795
|
+
/**
|
|
17796
|
+
* Internal: Handle getting blob data from a clipboard item.
|
|
17797
|
+
*
|
|
17798
|
+
* @private
|
|
17799
|
+
* @param {string} type - The MIME type to fetch.
|
|
17800
|
+
* @param {ClipboardItem} clipboardItem - Clipboard item instance.
|
|
17801
|
+
* @returns {Promise<Blob>} A promise that resolves with the Blob.
|
|
17802
|
+
*/
|
|
17803
|
+
_handleBlob(type, clipboardItem) {
|
|
17804
|
+
return clipboardItem.getType(type);
|
|
17805
|
+
}
|
|
17806
|
+
|
|
17807
|
+
/**
|
|
17808
|
+
* Internal: Handle getting plain text from a clipboard item.
|
|
17809
|
+
*
|
|
17810
|
+
* @private
|
|
17811
|
+
* @param {string} type - The MIME type (should be 'text/plain').
|
|
17812
|
+
* @param {ClipboardItem} clipboardItem - Clipboard item instance.
|
|
17813
|
+
* @returns {Promise<string>} A promise that resolves with the text content.
|
|
17814
|
+
*/
|
|
17815
|
+
_handleText(type, clipboardItem) {
|
|
17816
|
+
return this._handleBlob(type, clipboardItem).then((blob) => blob.text());
|
|
17817
|
+
}
|
|
17818
|
+
|
|
17819
|
+
/**
|
|
17820
|
+
* Read clipboard data based on filters like type, mime, index.
|
|
17821
|
+
*
|
|
17822
|
+
* @param {number|null} [index=0] - Item index or null for all.
|
|
17823
|
+
* @param {'text'|'custom'|null} [type=null] - Data type to filter.
|
|
17824
|
+
* @param {string|null} [mimeFormat=null] - MIME type or prefix.
|
|
17825
|
+
* @param {boolean} [fixValue=false] - If true, exact match on MIME type.
|
|
17826
|
+
* @returns {Promise<Blob|string|Array<Blob|string>|null>} A promise resolving with matching data.
|
|
17827
|
+
*/
|
|
17828
|
+
_readData(index = 0, type = null, mimeFormat = null, fixValue = false) {
|
|
17829
|
+
return new Promise((resolve, reject) => {
|
|
17830
|
+
this._read(index)
|
|
17831
|
+
.then((items) => {
|
|
17832
|
+
if (!items) return resolve(null);
|
|
17833
|
+
/** @type {Array<Blob|string>} */
|
|
17834
|
+
const finalResult = [];
|
|
17835
|
+
|
|
17836
|
+
// Complete task
|
|
17837
|
+
let continueLoop = true;
|
|
17838
|
+
|
|
17839
|
+
/**
|
|
17840
|
+
* @param {string} mimeType
|
|
17841
|
+
* @param {ClipboardItem} item
|
|
17842
|
+
*/
|
|
17843
|
+
const completeTask = async (mimeType, item) => {
|
|
17844
|
+
if (!continueLoop) return;
|
|
17845
|
+
// Custom
|
|
17846
|
+
if (
|
|
17847
|
+
(type === null || type === 'custom') &&
|
|
17848
|
+
typeof mimeFormat === 'string' &&
|
|
17849
|
+
((!fixValue && mimeType.startsWith(mimeFormat)) ||
|
|
17850
|
+
(fixValue && mimeType === mimeFormat))
|
|
17851
|
+
) {
|
|
17852
|
+
continueLoop = false;
|
|
17853
|
+
const result = await this._handleBlob(mimeType, item);
|
|
17854
|
+
if (result) finalResult.push(result);
|
|
17855
|
+
}
|
|
17856
|
+
|
|
17857
|
+
// Text
|
|
17858
|
+
else if ((type === null || type === 'text') && mimeType === 'text/plain') {
|
|
17859
|
+
continueLoop = false;
|
|
17860
|
+
const result = await this._handleText(mimeType, item);
|
|
17861
|
+
if (result) finalResult.push(result);
|
|
17862
|
+
}
|
|
17863
|
+
|
|
17864
|
+
// Blob
|
|
17865
|
+
else if (type === null) {
|
|
17866
|
+
continueLoop = false;
|
|
17867
|
+
const result = await this._handleBlob(mimeType, item);
|
|
17868
|
+
if (result) finalResult.push(result);
|
|
17869
|
+
}
|
|
17870
|
+
};
|
|
17871
|
+
|
|
17872
|
+
/** @type {Promise<void>[]} */
|
|
17873
|
+
const promises = [];
|
|
17874
|
+
|
|
17875
|
+
/**
|
|
17876
|
+
* Read Item
|
|
17877
|
+
* @param {ClipboardItem | ClipboardItems} item
|
|
17878
|
+
*/
|
|
17879
|
+
const readItem = (item) => {
|
|
17880
|
+
if (!(item instanceof ClipboardItem))
|
|
17881
|
+
throw new Error('Expected ClipboardItem when reading data.');
|
|
17882
|
+
for (const tIndex in item.types) promises.push(completeTask(item.types[tIndex], item));
|
|
17883
|
+
};
|
|
17884
|
+
|
|
17885
|
+
// Specific Item
|
|
17886
|
+
if (
|
|
17887
|
+
typeof index === 'number' &&
|
|
17888
|
+
!Number.isNaN(index) &&
|
|
17889
|
+
Number.isFinite(index) &&
|
|
17890
|
+
index > -1
|
|
17891
|
+
) {
|
|
17892
|
+
readItem(items);
|
|
17893
|
+
Promise.all(promises)
|
|
17894
|
+
.then(() => {
|
|
17895
|
+
if (finalResult[0]) resolve(finalResult[0]);
|
|
17896
|
+
else resolve(null);
|
|
17897
|
+
})
|
|
17898
|
+
.catch(reject);
|
|
17899
|
+
}
|
|
17900
|
+
|
|
17901
|
+
// All
|
|
17902
|
+
else if (Array.isArray(items)) {
|
|
17903
|
+
for (const tIndex in items) readItem(items[tIndex]);
|
|
17904
|
+
Promise.all(promises)
|
|
17905
|
+
.then(() => resolve(finalResult))
|
|
17906
|
+
.catch(reject);
|
|
17907
|
+
}
|
|
17908
|
+
})
|
|
17909
|
+
// Fail
|
|
17910
|
+
.catch(reject);
|
|
17911
|
+
});
|
|
17912
|
+
}
|
|
17913
|
+
|
|
17914
|
+
/**
|
|
17915
|
+
* Read plain text from the clipboard (single item by index).
|
|
17916
|
+
*
|
|
17917
|
+
* @param {number} [index=0] - The index of the clipboard item to read.
|
|
17918
|
+
* @returns {Promise<string|null>} A promise that resolves to the clipboard text or null.
|
|
17919
|
+
*/
|
|
17920
|
+
async readText(index = 0) {
|
|
17921
|
+
const value = await this._readData(index, 'text');
|
|
17922
|
+
if (typeof value !== 'string') throw new Error('Failed to read text: expected string result.');
|
|
17923
|
+
return value;
|
|
17924
|
+
}
|
|
17925
|
+
|
|
17926
|
+
/**
|
|
17927
|
+
* Read custom clipboard data based on MIME type from a specific index.
|
|
17928
|
+
*
|
|
17929
|
+
* @param {string|null} [mimeFormat=null] - MIME prefix to match (e.g., "image/").
|
|
17930
|
+
* @param {boolean} [fixValue=false] - If true, matches exact MIME instead of prefix.
|
|
17931
|
+
* @param {number} [index=0] - Clipboard item index.
|
|
17932
|
+
* @returns {Promise<Blob|null>} A promise resolving with a blob or null.
|
|
17933
|
+
*/
|
|
17934
|
+
async readCustom(mimeFormat = null, fixValue = false, index = 0) {
|
|
17935
|
+
const value = await this._readData(index, 'custom', mimeFormat, fixValue);
|
|
17936
|
+
if (!(value instanceof Blob)) throw new Error('Failed to read custom data: expected Blob.');
|
|
17937
|
+
return value;
|
|
17938
|
+
}
|
|
17939
|
+
|
|
17940
|
+
/**
|
|
17941
|
+
* Read all available plain text entries from the clipboard.
|
|
17942
|
+
*
|
|
17943
|
+
* @returns {Promise<string[]>} A promise resolving to an array of strings or null.
|
|
17944
|
+
*/
|
|
17945
|
+
async readAllTexts() {
|
|
17946
|
+
const values = await this._readData(null, 'text');
|
|
17947
|
+
if (!Array.isArray(values))
|
|
17948
|
+
throw new Error('Expected array of strings when reading all texts.');
|
|
17949
|
+
if (!values.every((value) => typeof value === 'string'))
|
|
17950
|
+
throw new Error('Some values returned were not strings.');
|
|
17951
|
+
return values;
|
|
17952
|
+
}
|
|
17953
|
+
|
|
17954
|
+
/**
|
|
17955
|
+
* Read all clipboard data matching a specific custom MIME type.
|
|
17956
|
+
*
|
|
17957
|
+
* @param {string|null} [mimeFormat=null] - MIME prefix or exact type.
|
|
17958
|
+
* @param {boolean} [fixValue=false] - Match prefix or exact MIME.
|
|
17959
|
+
* @returns {Promise<Blob[]>} A promise resolving with array of Blobs or null.
|
|
17960
|
+
*/
|
|
17961
|
+
async readAllCustom(mimeFormat = null, fixValue = false) {
|
|
17962
|
+
const values = await this._readData(null, 'custom', mimeFormat, fixValue);
|
|
17963
|
+
if (!Array.isArray(values))
|
|
17964
|
+
throw new Error('Expected array of blobs when reading all custom items.');
|
|
17965
|
+
if (!values.every((value) => value instanceof Blob))
|
|
17966
|
+
throw new Error('Some values returned were not Blob instances.');
|
|
17967
|
+
return values;
|
|
17968
|
+
}
|
|
17969
|
+
|
|
17970
|
+
/**
|
|
17971
|
+
* Read all clipboard data as Blob or text depending on type.
|
|
17972
|
+
*
|
|
17973
|
+
* @param {'text'|'custom'|null} [type=null] - The type of data to retrieve.
|
|
17974
|
+
* @param {string|null} [mimeFormat=null] - The MIME type or prefix to match.
|
|
17975
|
+
* @returns {Promise<Array<Blob|string>>} A promise resolving with matching data array.
|
|
17976
|
+
*/
|
|
17977
|
+
async readAllData(type = null, mimeFormat = null) {
|
|
17978
|
+
const value = await this._readData(null, type, mimeFormat);
|
|
17979
|
+
if (!Array.isArray(value)) throw new Error('Expected array result when reading all data.');
|
|
17980
|
+
return value;
|
|
17981
|
+
}
|
|
17982
|
+
|
|
17983
|
+
/**
|
|
17984
|
+
* Read clipboard data at a specific index or all if null.
|
|
17985
|
+
*
|
|
17986
|
+
* @param {number|null} index - Index of the item to retrieve or null to get all.
|
|
17987
|
+
* @returns {Promise<ClipboardItem|ClipboardItems|null>} A promise resolving with a clipboard item or array of items.
|
|
17988
|
+
*/
|
|
17989
|
+
_read(index) {
|
|
17990
|
+
return new Promise((resolve, reject) => {
|
|
17991
|
+
if (!this.#existNavigator) reject(new Error('Clipboard API not found!'));
|
|
17992
|
+
navigator.clipboard
|
|
17993
|
+
.read()
|
|
17994
|
+
.then((items) => {
|
|
17995
|
+
// Index is number
|
|
17996
|
+
if (typeof index === 'number') {
|
|
17997
|
+
if (Number.isNaN(index) || !Number.isFinite(index) || index < 0)
|
|
17998
|
+
throw new Error(`Invalid index value: ${index}`);
|
|
17999
|
+
if (items[index]) resolve(items[index]);
|
|
18000
|
+
// Not found
|
|
18001
|
+
else resolve(null);
|
|
18002
|
+
}
|
|
18003
|
+
// Get All
|
|
18004
|
+
resolve(items);
|
|
18005
|
+
})
|
|
18006
|
+
.catch(reject);
|
|
18007
|
+
});
|
|
18008
|
+
}
|
|
18009
|
+
|
|
18010
|
+
/**
|
|
18011
|
+
* Read clipboard data at a specific index.
|
|
18012
|
+
*
|
|
18013
|
+
* @param {number} index - Index of the item to retrieve
|
|
18014
|
+
* @returns {Promise<ClipboardItem|null>} A promise resolving with a clipboard item.
|
|
18015
|
+
*/
|
|
18016
|
+
async readIndex(index) {
|
|
18017
|
+
const value = await this._read(index);
|
|
18018
|
+
if (value !== null && !(value instanceof ClipboardItem))
|
|
18019
|
+
throw new Error(`Value at index ${index} is not a ClipboardItem.`);
|
|
18020
|
+
return value;
|
|
18021
|
+
}
|
|
18022
|
+
|
|
18023
|
+
/**
|
|
18024
|
+
* Read all clipboard content without any filters.
|
|
18025
|
+
*
|
|
18026
|
+
* @returns {Promise<ClipboardItems>} A promise resolving with all clipboard items.
|
|
18027
|
+
*/
|
|
18028
|
+
async readAll() {
|
|
18029
|
+
const value = await this._read(null);
|
|
18030
|
+
if (!Array.isArray(value)) throw new Error('Expected array result from clipboard read.');
|
|
18031
|
+
for (const item of value) {
|
|
18032
|
+
if (!(item instanceof ClipboardItem))
|
|
18033
|
+
throw new Error('Invalid item type found in clipboard result.');
|
|
18034
|
+
}
|
|
18035
|
+
return value;
|
|
18036
|
+
}
|
|
18037
|
+
|
|
18038
|
+
/**
|
|
18039
|
+
* Returns whether the legacy `document.execCommand()` API is available.
|
|
18040
|
+
* This can be used to determine if a fallback clipboard method is usable.
|
|
18041
|
+
*
|
|
18042
|
+
* @returns {boolean} True if `document.execCommand` is available.
|
|
18043
|
+
*/
|
|
18044
|
+
isExecCommandAvailable() {
|
|
18045
|
+
return this.#existExecCommand;
|
|
18046
|
+
}
|
|
18047
|
+
|
|
18048
|
+
/**
|
|
18049
|
+
* Returns whether the modern Clipboard API (`navigator.clipboard`) is available.
|
|
18050
|
+
* Useful to know if full clipboard features can be accessed.
|
|
18051
|
+
*
|
|
18052
|
+
* @returns {boolean} True if `navigator.clipboard` is available.
|
|
18053
|
+
*/
|
|
18054
|
+
isNavigatorClipboardAvailable() {
|
|
18055
|
+
return this.#existNavigator;
|
|
18056
|
+
}
|
|
18057
|
+
|
|
18058
|
+
/**
|
|
18059
|
+
* Returns the function used to copy plain text to the clipboard.
|
|
18060
|
+
* This function may be built-in or set manually via `setCopyText`.
|
|
18061
|
+
*
|
|
18062
|
+
* @returns {((text: string) => Promise<void>) | null} The current text copy function or null if unavailable.
|
|
18063
|
+
*/
|
|
18064
|
+
getCopyTextFunc() {
|
|
18065
|
+
return this.#copyText;
|
|
18066
|
+
}
|
|
18067
|
+
|
|
18068
|
+
/**
|
|
18069
|
+
* Returns the function used to copy Blob (binary data) to the clipboard.
|
|
18070
|
+
* This function may be built-in or set manually via `setCopyBlob`.
|
|
18071
|
+
*
|
|
18072
|
+
* @returns {((blob: Blob) => Promise<void>) | null} The current blob copy function or null if unavailable.
|
|
18073
|
+
*/
|
|
18074
|
+
getCopyBlobFunc() {
|
|
18075
|
+
return this.#copyBlob;
|
|
18076
|
+
}
|
|
18077
|
+
}
|
|
18078
|
+
|
|
18079
|
+
/* harmony default export */ const libs_TinyClipboard = (TinyClipboard);
|
|
18080
|
+
|
|
18081
|
+
;// ./src/v1/libs/TinyColorConverter.mjs
|
|
18082
|
+
/**
|
|
18083
|
+
* Represents a color in RGBA format.
|
|
18084
|
+
* Each element must be a number between 0 and 255.
|
|
18085
|
+
* The fourth value represents the alpha (transparency) channel.
|
|
18086
|
+
*
|
|
18087
|
+
* @typedef {number[]} RgbaColor
|
|
18088
|
+
* @property {number} 0 - Red component (0–255)
|
|
18089
|
+
* @property {number} 1 - Green component (0–255)
|
|
18090
|
+
* @property {number} 2 - Blue component (0–255)
|
|
18091
|
+
* @property {number} 3 - Alpha component (0–255)
|
|
18092
|
+
*/
|
|
18093
|
+
|
|
18094
|
+
/**
|
|
18095
|
+
* Represents a color in RGB format.
|
|
18096
|
+
* Each element must be a number between 0 and 255.
|
|
18097
|
+
*
|
|
18098
|
+
* @typedef {number[]} RgbColor
|
|
18099
|
+
* @property {number} 0 - Red component (0–255)
|
|
18100
|
+
* @property {number} 1 - Green component (0–255)
|
|
18101
|
+
* @property {number} 2 - Blue component (0–255)
|
|
18102
|
+
*/
|
|
18103
|
+
|
|
18104
|
+
/**
|
|
18105
|
+
* Represents a color in HSLA format.
|
|
18106
|
+
* The fourth value represents the alpha (transparency) channel.
|
|
18107
|
+
*
|
|
18108
|
+
* @typedef {number[]} HslaColor
|
|
18109
|
+
* @property {number} 0 - Hue (0–360)
|
|
18110
|
+
* @property {number} 1 - Saturation (0–100)
|
|
18111
|
+
* @property {number} 2 - Lightness (0–100)
|
|
18112
|
+
* @property {number} 3 - Alpha component (0–255)
|
|
18113
|
+
*/
|
|
18114
|
+
|
|
18115
|
+
/**
|
|
18116
|
+
* Represents a color in HSL format.
|
|
18117
|
+
*
|
|
18118
|
+
* @typedef {number[]} HslColor
|
|
18119
|
+
* @property {number} 0 - Hue (0–360)
|
|
18120
|
+
* @property {number} 1 - Saturation (0–100)
|
|
18121
|
+
* @property {number} 2 - Lightness (0–100)
|
|
18122
|
+
*/
|
|
18123
|
+
|
|
18124
|
+
/**
|
|
18125
|
+
* Represents a hex color.
|
|
18126
|
+
*
|
|
18127
|
+
* @typedef {string} HexColor
|
|
18128
|
+
*/
|
|
18129
|
+
|
|
18130
|
+
/**
|
|
18131
|
+
* A union type representing various accepted color formats.
|
|
18132
|
+
* Can be a hex color string, a numeric value, or an array-based RGB/RGBA representation.
|
|
18133
|
+
*
|
|
18134
|
+
* @typedef {HexColor | number | RgbColor | RgbaColor} ColorTypes
|
|
18135
|
+
*/
|
|
18136
|
+
|
|
18137
|
+
/**
|
|
18138
|
+
* @typedef {Object} RgbaResult
|
|
18139
|
+
* @property {number} r - Red component (0–255)
|
|
18140
|
+
* @property {number} g - Green component (0–255)
|
|
18141
|
+
* @property {number} b - Blue component (0–255)
|
|
18142
|
+
* @property {number} a - Alpha component (0–255)
|
|
18143
|
+
*/
|
|
18144
|
+
|
|
18145
|
+
/**
|
|
18146
|
+
* @typedef {Object} RgbResult
|
|
18147
|
+
* @property {number} r - Red component (0–255)
|
|
18148
|
+
* @property {number} g - Green component (0–255)
|
|
18149
|
+
* @property {number} b - Blue component (0–255)
|
|
18150
|
+
*/
|
|
18151
|
+
|
|
18152
|
+
/**
|
|
18153
|
+
* @typedef {Object} HexResult
|
|
18154
|
+
* @property {string} hex - Hex color
|
|
18155
|
+
*/
|
|
18156
|
+
|
|
18157
|
+
/**
|
|
18158
|
+
* @typedef {Object} HslResult
|
|
18159
|
+
* @property {number} h - Hue (0–360)
|
|
18160
|
+
* @property {number} s - Saturation (0–100)
|
|
18161
|
+
* @property {number} l - Lightness (0–100)
|
|
18162
|
+
*/
|
|
18163
|
+
|
|
18164
|
+
/**
|
|
18165
|
+
* A class that allows converting colors between all common formats.
|
|
18166
|
+
*/
|
|
18167
|
+
class TinyColorConverter {
|
|
18168
|
+
/**
|
|
18169
|
+
* Generates a smooth gradient of colors based on sine wave patterns.
|
|
18170
|
+
*
|
|
18171
|
+
* @see {@link https://www.npmjs.com/package/rainbow-colors-array} Code Reference
|
|
18172
|
+
* @param {number} [len=24] - The number of colors to generate.
|
|
18173
|
+
* @param {'rgb'|'hex'|'hsl'} [type='rgb'] - The format of the colors returned: `'rgb'`, `'hex'`, or `'hsl'`.
|
|
18174
|
+
* @param {boolean} [pastel=false] - If true, generates pastel tones by adjusting the intensity and offset.
|
|
18175
|
+
* @returns {Array<RgbResult|HexResult|HslResult>} An array of color values in the selected format:
|
|
18176
|
+
*/
|
|
18177
|
+
static _rca(len, type, pastel) {
|
|
18178
|
+
let eq1 = 127;
|
|
18179
|
+
let eq2 = 128;
|
|
18180
|
+
if (len === undefined) {
|
|
18181
|
+
len = 24;
|
|
18182
|
+
}
|
|
18183
|
+
if (type === undefined) {
|
|
18184
|
+
type = 'rgb';
|
|
18185
|
+
}
|
|
18186
|
+
if (pastel === true) {
|
|
18187
|
+
eq1 = 55;
|
|
18188
|
+
eq2 = 200;
|
|
18189
|
+
}
|
|
18190
|
+
const frequency = (Math.PI * 2) / len;
|
|
18191
|
+
|
|
18192
|
+
const cvparr = [];
|
|
18193
|
+
for (let i = 0; i < len; ++i) {
|
|
18194
|
+
const red = Math.sin(frequency * i + 2) * eq1 + eq2;
|
|
18195
|
+
const green = Math.sin(frequency * i + 0) * eq1 + eq2;
|
|
18196
|
+
const blue = Math.sin(frequency * i + 4) * eq1 + eq2;
|
|
18197
|
+
|
|
18198
|
+
switch (type) {
|
|
18199
|
+
case 'hex':
|
|
18200
|
+
cvparr.push({ hex: this.rgbToHex(Math.round(red), Math.round(green), Math.round(blue)) });
|
|
18201
|
+
break;
|
|
18202
|
+
case 'rgb':
|
|
18203
|
+
cvparr.push({ r: red, g: green, b: blue });
|
|
18204
|
+
break;
|
|
18205
|
+
case 'hsl':
|
|
18206
|
+
const [h, s, l] = this.rgbaToHsl(Math.round(red), Math.round(green), Math.round(blue));
|
|
18207
|
+
cvparr.push({ h, s, l });
|
|
18208
|
+
break;
|
|
18209
|
+
}
|
|
18210
|
+
}
|
|
18211
|
+
|
|
18212
|
+
return cvparr;
|
|
18213
|
+
}
|
|
18214
|
+
|
|
18215
|
+
/**
|
|
18216
|
+
* Generates a smooth gradient of colors based on sine wave patterns.
|
|
18217
|
+
*
|
|
18218
|
+
* @param {number} [len=24] - The number of colors to generate.
|
|
18219
|
+
* @param {boolean} [pastel=false] - If true, generates pastel tones by adjusting the intensity and offset.
|
|
18220
|
+
* @returns {RgbResult[]} An array of rgb color values.
|
|
18221
|
+
*/
|
|
18222
|
+
static rcaRgb(len, pastel) {
|
|
18223
|
+
return /** @type {RgbResult[]} */ (TinyColorConverter._rca(len, 'rgb', pastel));
|
|
18224
|
+
}
|
|
18225
|
+
|
|
18226
|
+
/**
|
|
18227
|
+
* Generates a smooth gradient of colors based on sine wave patterns.
|
|
18228
|
+
*
|
|
18229
|
+
* @param {number} [len=24] - The number of colors to generate.
|
|
18230
|
+
* @param {boolean} [pastel=false] - If true, generates pastel tones by adjusting the intensity and offset.
|
|
18231
|
+
* @returns {HslResult[]} An array of hsl color values.
|
|
18232
|
+
*/
|
|
18233
|
+
static rcaHsl(len, pastel) {
|
|
18234
|
+
return /** @type {HslResult[]} */ (TinyColorConverter._rca(len, 'hsl', pastel));
|
|
18235
|
+
}
|
|
18236
|
+
|
|
18237
|
+
/**
|
|
18238
|
+
* Generates a smooth gradient of colors based on sine wave patterns.
|
|
18239
|
+
*
|
|
18240
|
+
* @param {number} [len=24] - The number of colors to generate.
|
|
18241
|
+
* @param {boolean} [pastel=false] - If true, generates pastel tones by adjusting the intensity and offset.
|
|
18242
|
+
* @returns {HexResult[]} An array of hex color values.
|
|
18243
|
+
*/
|
|
18244
|
+
static rcaHex(len, pastel) {
|
|
18245
|
+
return /** @type {HexResult[]} */ (TinyColorConverter._rca(len, 'hex', pastel));
|
|
18246
|
+
}
|
|
18247
|
+
|
|
18248
|
+
/**
|
|
18249
|
+
* Generates a random color in hexadecimal format.
|
|
18250
|
+
*
|
|
18251
|
+
* @returns {HexColor} A hex color string (e.g. `#a3e5f2`).
|
|
18252
|
+
*/
|
|
18253
|
+
static randomColor() {
|
|
18254
|
+
const hex = Math.floor(Math.random() * 0x1000000).toString(16);
|
|
18255
|
+
return `#${hex.padStart(6, '0')}`;
|
|
18256
|
+
}
|
|
18257
|
+
|
|
18258
|
+
/**
|
|
18259
|
+
* Parses input into RGBA array.
|
|
18260
|
+
* @param {ColorTypes} input
|
|
18261
|
+
* @param {boolean} isHsl
|
|
18262
|
+
* @returns {RgbaColor}
|
|
18263
|
+
*/
|
|
18264
|
+
static parseInput(input, isHsl) {
|
|
18265
|
+
if (typeof input === 'string') {
|
|
18266
|
+
input = input.trim().toLowerCase();
|
|
18267
|
+
if (input.startsWith('#')) return this.hexToRgba(input);
|
|
18268
|
+
if (input.startsWith('rgb')) return this.rgbStringToRgbaArray(input);
|
|
18269
|
+
if (input.startsWith('hsl')) return this.hslStringToRgbaArray(input);
|
|
18270
|
+
}
|
|
18271
|
+
if (typeof input === 'number') return this.intToRgba(input);
|
|
18272
|
+
if (
|
|
18273
|
+
Array.isArray(input) &&
|
|
18274
|
+
(input.length === 3 || input.length === 4) &&
|
|
18275
|
+
input.every((item) => typeof item === 'number')
|
|
18276
|
+
) {
|
|
18277
|
+
if (isHsl) {
|
|
18278
|
+
const [h, s, l, a2] = input;
|
|
18279
|
+
if (h <= 360 && s <= 100 && l <= 100) return this.hslToRgba(h, s, l, a2);
|
|
18280
|
+
}
|
|
18281
|
+
return [...input, 1].slice(0, 4);
|
|
18282
|
+
}
|
|
18283
|
+
|
|
18284
|
+
throw new Error('Unsupported color format.');
|
|
18285
|
+
}
|
|
18286
|
+
|
|
18287
|
+
// HSL Color
|
|
18288
|
+
|
|
18289
|
+
/**
|
|
18290
|
+
* Converts hsl to integer.
|
|
18291
|
+
* @param {number} h - Hue (0–360)
|
|
18292
|
+
* @param {number} s - Saturation (0–100)
|
|
18293
|
+
* @param {number} l - Lightness (0–100)
|
|
18294
|
+
* @returns {number}
|
|
18295
|
+
*/
|
|
18296
|
+
static hslToInt(h, s, l) {
|
|
18297
|
+
const [r, g, b] = TinyColorConverter.hslToRgba(h, s, l);
|
|
18298
|
+
return TinyColorConverter.rgbToInt(r, g, b);
|
|
18299
|
+
}
|
|
18300
|
+
|
|
18301
|
+
/**
|
|
18302
|
+
* Converts hsl to hex.
|
|
18303
|
+
* @param {number} h - Hue (0–360)
|
|
18304
|
+
* @param {number} s - Saturation (0–100)
|
|
18305
|
+
* @param {number} l - Lightness (0–100)
|
|
18306
|
+
* @returns {HexColor}
|
|
18307
|
+
*/
|
|
18308
|
+
static hslToHex(h, s, l) {
|
|
18309
|
+
const [r, g, b] = TinyColorConverter.hslToRgba(h, s, l);
|
|
18310
|
+
return TinyColorConverter.rgbToHex(r, g, b);
|
|
18311
|
+
}
|
|
18312
|
+
|
|
18313
|
+
/**
|
|
18314
|
+
* Converts hsl(a) string to RGBA array.
|
|
18315
|
+
* @param {string} hsl
|
|
18316
|
+
* @returns {RgbaColor}
|
|
18317
|
+
*/
|
|
18318
|
+
static hslStringToRgbaArray(hsl) {
|
|
18319
|
+
const match = hsl.match(/[\d.]+/g)?.map(Number);
|
|
18320
|
+
if (!match || match.length < 3) return [0, 0, 0, 1];
|
|
18321
|
+
const [h, s, l, a = 1] = match;
|
|
18322
|
+
return this.hslToRgba(h, s, l, a);
|
|
18323
|
+
}
|
|
18324
|
+
|
|
18325
|
+
/**
|
|
18326
|
+
* Converts HSL or HSLA to RGBA.
|
|
18327
|
+
* @param {number} h - Hue (0–360)
|
|
18328
|
+
* @param {number} s - Saturation (0–100)
|
|
18329
|
+
* @param {number} l - Lightness (0–100)
|
|
18330
|
+
* @param {number} [a=1] - Alpha (0–1)
|
|
18331
|
+
* @returns {RgbaColor}
|
|
18332
|
+
*/
|
|
18333
|
+
static hslToRgba(h, s, l, a = 1) {
|
|
18334
|
+
s /= 100;
|
|
18335
|
+
l /= 100;
|
|
18336
|
+
/** @type {(n: number) => number} */
|
|
18337
|
+
const k = (n) => (n + h / 30) % 12;
|
|
18338
|
+
const a_ = s * Math.min(l, 1 - l);
|
|
18339
|
+
/** @type {(n: number) => number} */
|
|
18340
|
+
const f = (n) => l - a_ * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
|
|
18341
|
+
return [Math.round(f(0) * 255), Math.round(f(8) * 255), Math.round(f(4) * 255), a];
|
|
18342
|
+
}
|
|
18343
|
+
|
|
18344
|
+
/**
|
|
18345
|
+
* Converts HSL or HSLA to RGB.
|
|
18346
|
+
* @param {number} h - Hue (0–360)
|
|
18347
|
+
* @param {number} s - Saturation (0–100)
|
|
18348
|
+
* @param {number} l - Lightness (0–100)
|
|
18349
|
+
* @param {number} [a=1] - Alpha (0–1)
|
|
18350
|
+
* @returns {RgbColor}
|
|
18351
|
+
*/
|
|
18352
|
+
static hslToRgb(h, s, l, a = 1) {
|
|
18353
|
+
return TinyColorConverter.hslToRgba(h, s, l, a).slice(0, 3);
|
|
18354
|
+
}
|
|
18355
|
+
|
|
18356
|
+
// Hex Color
|
|
18357
|
+
|
|
18358
|
+
/**
|
|
18359
|
+
* Converts hex to integer.
|
|
18360
|
+
* @param {HexColor} hex
|
|
18361
|
+
* @returns {number}
|
|
18362
|
+
*/
|
|
18363
|
+
static hexToInt(hex) {
|
|
18364
|
+
return parseInt(hex.replace(/^#/, ''), 16);
|
|
18365
|
+
}
|
|
18366
|
+
|
|
18367
|
+
/**
|
|
18368
|
+
* Converts hex string to HSL array.
|
|
18369
|
+
* @param {HexColor} hex
|
|
18370
|
+
* @returns {HslColor}
|
|
18371
|
+
*/
|
|
18372
|
+
static hexToHsl(hex) {
|
|
18373
|
+
const [r, g, b, a] = TinyColorConverter.hexToRgba(hex);
|
|
18374
|
+
return TinyColorConverter.rgbaToHsl(r, g, b, a);
|
|
18375
|
+
}
|
|
18376
|
+
|
|
18377
|
+
/**
|
|
18378
|
+
* Converts hex string to HSL array.
|
|
18379
|
+
* @param {HexColor} hex
|
|
18380
|
+
* @returns {HslaColor}
|
|
18381
|
+
*/
|
|
18382
|
+
static hexToHsla(hex) {
|
|
18383
|
+
const [r, g, b, a] = TinyColorConverter.hexToRgba(hex);
|
|
18384
|
+
return TinyColorConverter.rgbaToHsla(r, g, b, a);
|
|
18385
|
+
}
|
|
18386
|
+
|
|
18387
|
+
/**
|
|
18388
|
+
* Converts hex string to RGBA array.
|
|
18389
|
+
* @param {HexColor} hex
|
|
18390
|
+
* @returns {RgbaColor}
|
|
18391
|
+
*/
|
|
18392
|
+
static hexToRgba(hex) {
|
|
18393
|
+
hex = hex.replace(/^#/, '');
|
|
18394
|
+
if (hex.length === 3)
|
|
18395
|
+
hex = hex
|
|
18396
|
+
.split('')
|
|
18397
|
+
.map((c) => c + c)
|
|
18398
|
+
.join('');
|
|
18399
|
+
const intVal = parseInt(hex, 16);
|
|
18400
|
+
const r = (intVal >> 16) & 255;
|
|
18401
|
+
const g = (intVal >> 8) & 255;
|
|
18402
|
+
const b = intVal & 255;
|
|
18403
|
+
return [r, g, b, 1];
|
|
18404
|
+
}
|
|
18405
|
+
|
|
18406
|
+
/**
|
|
18407
|
+
* Converts HEX to RGB.
|
|
18408
|
+
* @param {HexColor} hex
|
|
18409
|
+
* @returns {RgbColor}
|
|
18410
|
+
*/
|
|
18411
|
+
static hexToRgb(hex) {
|
|
18412
|
+
return this.hexToRgba(hex).slice(0, 3);
|
|
18413
|
+
}
|
|
18414
|
+
|
|
18415
|
+
// RGBA Color
|
|
18416
|
+
|
|
18417
|
+
/**
|
|
18418
|
+
* Converts RGB to HEX.
|
|
18419
|
+
* @param {number} r
|
|
18420
|
+
* @param {number} g
|
|
18421
|
+
* @param {number} b
|
|
18422
|
+
* @returns {HexColor}
|
|
18423
|
+
*/
|
|
18424
|
+
static rgbToHex(r, g, b) {
|
|
18425
|
+
return '#' + [r, g, b].map((v) => v.toString(16).padStart(2, '0')).join('');
|
|
18426
|
+
}
|
|
18427
|
+
|
|
18428
|
+
/**
|
|
18429
|
+
* Converts RGB to integer.
|
|
18430
|
+
* @param {number} r
|
|
18431
|
+
* @param {number} g
|
|
18432
|
+
* @param {number} b
|
|
18433
|
+
* @returns {number}
|
|
18434
|
+
*/
|
|
18435
|
+
static rgbToInt(r, g, b) {
|
|
18436
|
+
return (r << 16) | (g << 8) | b;
|
|
18437
|
+
}
|
|
18438
|
+
|
|
18439
|
+
/**
|
|
18440
|
+
* Converts RGBA to HSLA.
|
|
18441
|
+
* @param {number} r
|
|
18442
|
+
* @param {number} g
|
|
18443
|
+
* @param {number} b
|
|
18444
|
+
* @param {number} [a=1]
|
|
18445
|
+
* @returns {HslaColor}
|
|
18446
|
+
*/
|
|
18447
|
+
static rgbaToHsla(r, g, b, a = 1) {
|
|
18448
|
+
r /= 255;
|
|
18449
|
+
g /= 255;
|
|
18450
|
+
b /= 255;
|
|
18451
|
+
const max = Math.max(r, g, b);
|
|
18452
|
+
const min = Math.min(r, g, b);
|
|
18453
|
+
let h = 0,
|
|
18454
|
+
s = 0,
|
|
18455
|
+
l = (max + min) / 2;
|
|
18456
|
+
const d = max - min;
|
|
18457
|
+
|
|
18458
|
+
if (d !== 0) {
|
|
18459
|
+
s = d / (1 - Math.abs(2 * l - 1));
|
|
18460
|
+
switch (max) {
|
|
18461
|
+
case r:
|
|
18462
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
18463
|
+
break;
|
|
18464
|
+
case g:
|
|
18465
|
+
h = (b - r) / d + 2;
|
|
18466
|
+
break;
|
|
18467
|
+
case b:
|
|
18468
|
+
h = (r - g) / d + 4;
|
|
18469
|
+
break;
|
|
18470
|
+
}
|
|
18471
|
+
h *= 60;
|
|
18472
|
+
}
|
|
18473
|
+
|
|
18474
|
+
return [Math.round(h), Math.round(s * 100), Math.round(l * 100), a];
|
|
18475
|
+
}
|
|
18476
|
+
|
|
18477
|
+
/**
|
|
18478
|
+
* Converts RGBA to HSL.
|
|
18479
|
+
* @param {number} r
|
|
18480
|
+
* @param {number} g
|
|
18481
|
+
* @param {number} b
|
|
18482
|
+
* @param {number} [a=1]
|
|
18483
|
+
* @returns {HslColor}
|
|
18484
|
+
*/
|
|
18485
|
+
static rgbaToHsl(r, g, b, a) {
|
|
18486
|
+
return this.rgbaToHsla(r, g, b, a).slice(0, 3);
|
|
18487
|
+
}
|
|
18488
|
+
|
|
18489
|
+
/**
|
|
18490
|
+
* Converts rgb(a) string to RGBA array.
|
|
18491
|
+
* @param {string} rgb
|
|
18492
|
+
* @returns {RgbaColor}
|
|
18493
|
+
*/
|
|
18494
|
+
static rgbStringToRgbaArray(rgb) {
|
|
18495
|
+
const match = rgb.match(/[\d.]+/g)?.map(Number);
|
|
18496
|
+
if (!match) return [];
|
|
18497
|
+
return [...match, 1].slice(0, 4);
|
|
18498
|
+
}
|
|
18499
|
+
|
|
18500
|
+
// Integer Color
|
|
18501
|
+
|
|
18502
|
+
/**
|
|
18503
|
+
* Converts integer color to HSL.
|
|
18504
|
+
* @param {number} int
|
|
18505
|
+
* @returns {HslColor}
|
|
18506
|
+
*/
|
|
18507
|
+
static intToHsl(int) {
|
|
18508
|
+
const [r, g, b, a] = TinyColorConverter.intToRgba(int);
|
|
18509
|
+
return TinyColorConverter.rgbaToHsl(r, g, b, a);
|
|
18510
|
+
}
|
|
18511
|
+
|
|
18512
|
+
/**
|
|
18513
|
+
* Converts integer color to HSL.
|
|
18514
|
+
* @param {number} int
|
|
18515
|
+
* @returns {HslaColor}
|
|
18516
|
+
*/
|
|
18517
|
+
static intToHsla(int) {
|
|
18518
|
+
const [r, g, b, a] = TinyColorConverter.intToRgba(int);
|
|
18519
|
+
return TinyColorConverter.rgbaToHsla(r, g, b, a);
|
|
18520
|
+
}
|
|
18521
|
+
|
|
18522
|
+
/**
|
|
18523
|
+
* Converts integer color to hex.
|
|
18524
|
+
* @param {number} int
|
|
18525
|
+
* @returns {HexColor}
|
|
18526
|
+
*/
|
|
18527
|
+
static intToHex(int) {
|
|
18528
|
+
return '#' + int.toString(16).padStart(6, '0');
|
|
18529
|
+
}
|
|
18530
|
+
|
|
18531
|
+
/**
|
|
18532
|
+
* Converts an integer (0xRRGGBB) to RGBA.
|
|
18533
|
+
* @param {number} value
|
|
18534
|
+
* @returns {RgbaColor}
|
|
18535
|
+
*/
|
|
18536
|
+
static intToRgba(value) {
|
|
18537
|
+
const r = (value >> 16) & 255;
|
|
18538
|
+
const g = (value >> 8) & 255;
|
|
18539
|
+
const b = value & 255;
|
|
18540
|
+
return [r, g, b, 1];
|
|
18541
|
+
}
|
|
18542
|
+
|
|
18543
|
+
// Class Script
|
|
18544
|
+
|
|
18545
|
+
/** @type {ColorTypes} */
|
|
18546
|
+
#original = '#000000';
|
|
18547
|
+
/** @type {RgbaColor} */
|
|
18548
|
+
#rgba = [0, 0, 0, 0];
|
|
18549
|
+
|
|
18550
|
+
#checkIsHsl;
|
|
18551
|
+
|
|
18552
|
+
/**
|
|
18553
|
+
* @param {ColorTypes|null} [input=null] - Any valid color (hex, rgb string, rgba string, hsl(a), css name, array or int).
|
|
18554
|
+
* @param {boolean} [checkIsHsl=false]
|
|
18555
|
+
*/
|
|
18556
|
+
constructor(input = null, checkIsHsl = false) {
|
|
18557
|
+
this.#checkIsHsl = checkIsHsl;
|
|
18558
|
+
if (typeof input !== 'undefined' && input !== null) this.setColor(input);
|
|
18559
|
+
}
|
|
18560
|
+
|
|
18561
|
+
/**
|
|
18562
|
+
* @param {ColorTypes} input - Any valid color (hex, rgb string, rgba string, hsl(a), css name, array or int).
|
|
18563
|
+
*/
|
|
18564
|
+
setColor(input) {
|
|
18565
|
+
this.#original = input;
|
|
18566
|
+
const isHsl =
|
|
18567
|
+
this.#checkIsHsl &&
|
|
18568
|
+
Array.isArray(input) &&
|
|
18569
|
+
input[0] <= 360 &&
|
|
18570
|
+
input[1] <= 100 &&
|
|
18571
|
+
input[2] <= 100;
|
|
18572
|
+
this.#rgba = TinyColorConverter.parseInput(input, isHsl);
|
|
18573
|
+
}
|
|
18574
|
+
|
|
18575
|
+
/**
|
|
18576
|
+
* Returns HSLA array.
|
|
18577
|
+
* @returns {HslaColor}
|
|
18578
|
+
*/
|
|
18579
|
+
toHslaArray() {
|
|
18580
|
+
const [r, g, b, a] = this.#rgba;
|
|
18581
|
+
return TinyColorConverter.rgbaToHsla(r, g, b, a);
|
|
18582
|
+
}
|
|
18583
|
+
|
|
18584
|
+
/**
|
|
18585
|
+
* Returns RGB string.
|
|
18586
|
+
* @returns {string}
|
|
18587
|
+
*/
|
|
18588
|
+
toHslString() {
|
|
18589
|
+
const [r, g, b] = this.#rgba;
|
|
18590
|
+
const [h, s, l] = TinyColorConverter.rgbaToHsl(r, g, b);
|
|
18591
|
+
return `hsl(${h}, ${s}%, ${l}%)`;
|
|
18592
|
+
}
|
|
18593
|
+
|
|
18594
|
+
/**
|
|
18595
|
+
* Returns RGBA string.
|
|
18596
|
+
* @returns {string}
|
|
18597
|
+
*/
|
|
18598
|
+
toHslaString() {
|
|
18599
|
+
const [r, g, b, a] = this.#rgba;
|
|
18600
|
+
const [h, s, l, a2] = TinyColorConverter.rgbaToHsla(r, g, b, a);
|
|
18601
|
+
return `hsla(${h}, ${s}%, ${l}%, ${a2 ?? 1})`;
|
|
18602
|
+
}
|
|
18603
|
+
|
|
18604
|
+
/**
|
|
18605
|
+
* Returns RGBA array.
|
|
18606
|
+
* @returns {RgbaColor}
|
|
18607
|
+
*/
|
|
18608
|
+
toRgbaArray() {
|
|
18609
|
+
return [...this.#rgba];
|
|
18610
|
+
}
|
|
18611
|
+
|
|
18612
|
+
/**
|
|
18613
|
+
* Returns RGB string.
|
|
18614
|
+
* @returns {string}
|
|
18615
|
+
*/
|
|
18616
|
+
toRgbString() {
|
|
18617
|
+
const [r, g, b] = this.#rgba;
|
|
18618
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
18619
|
+
}
|
|
18620
|
+
|
|
18621
|
+
/**
|
|
18622
|
+
* Returns RGBA string.
|
|
18623
|
+
* @returns {string}
|
|
18624
|
+
*/
|
|
18625
|
+
toRgbaString() {
|
|
18626
|
+
const [r, g, b, a] = this.#rgba;
|
|
18627
|
+
return `rgba(${r}, ${g}, ${b}, ${a ?? 1})`;
|
|
18628
|
+
}
|
|
18629
|
+
|
|
18630
|
+
/**
|
|
18631
|
+
* Returns hex color.
|
|
18632
|
+
* @returns {HexColor}
|
|
18633
|
+
*/
|
|
18634
|
+
toHex() {
|
|
18635
|
+
const [r, g, b] = this.#rgba;
|
|
18636
|
+
return TinyColorConverter.rgbToHex(r, g, b);
|
|
18637
|
+
}
|
|
18638
|
+
|
|
18639
|
+
/**
|
|
18640
|
+
* Returns color as integer.
|
|
18641
|
+
* @returns {number}
|
|
18642
|
+
*/
|
|
18643
|
+
toInt() {
|
|
18644
|
+
const [r, g, b] = this.#rgba;
|
|
18645
|
+
return TinyColorConverter.rgbToInt(r, g, b);
|
|
18646
|
+
}
|
|
18647
|
+
|
|
18648
|
+
/**
|
|
18649
|
+
* Returns the original input.
|
|
18650
|
+
* @returns {ColorTypes}
|
|
18651
|
+
*/
|
|
18652
|
+
getOriginal() {
|
|
18653
|
+
return this.#original;
|
|
18654
|
+
}
|
|
18655
|
+
}
|
|
18656
|
+
|
|
18657
|
+
/* harmony default export */ const libs_TinyColorConverter = (TinyColorConverter);
|
|
18658
|
+
|
|
18659
|
+
;// ./src/v1/libs/TinyTimeout.mjs
|
|
18660
|
+
/**
|
|
18661
|
+
* A utility class to manage dynamically adjusted timeouts based on how often
|
|
18662
|
+
* each unique ID is triggered. Also provides polling support for asynchronous conditions.
|
|
18663
|
+
*/
|
|
18664
|
+
class TinyTimeout {
|
|
18665
|
+
/** @type {boolean} Whether this instance has been destroyed. */
|
|
18666
|
+
#isDestroyed = false;
|
|
18667
|
+
|
|
18668
|
+
/** @type {boolean} Whether to allow auto-updating an ID's timeout config if `value` changes. */
|
|
18669
|
+
#allowAutoConfigChange;
|
|
18670
|
+
|
|
18671
|
+
/** @type {number} The interval time (ms) used to decrement cooldown counters. */
|
|
18672
|
+
#cooldownWatcherTime;
|
|
18673
|
+
|
|
18674
|
+
/** @type {NodeJS.Timeout|null} Reference to the internal cooldown interval. */
|
|
18675
|
+
#cooldownWatcher = null;
|
|
18676
|
+
|
|
18677
|
+
/**
|
|
18678
|
+
* Internal map that keeps track of how many times each ID has been triggered,
|
|
18679
|
+
* along with the base multiplier used to calculate delays.
|
|
18680
|
+
*
|
|
18681
|
+
* @type {Map<string, { value: number, now: number }>}
|
|
18682
|
+
*/
|
|
18683
|
+
#timeoutFixer = new Map();
|
|
18684
|
+
|
|
18685
|
+
/**
|
|
18686
|
+
* Creates a new instance of TinyTimeout.
|
|
18687
|
+
*
|
|
18688
|
+
* @param {Object} [options={}] Optional configuration object.
|
|
18689
|
+
* @param {number} [options.cooldownWatcherTime=5000] Interval in milliseconds for reducing `now` counters.
|
|
18690
|
+
* @param {boolean} [options.allowAutoConfigChange=false] Whether to allow auto value changes for existing IDs.
|
|
18691
|
+
*/
|
|
18692
|
+
constructor({ cooldownWatcherTime = 5000, allowAutoConfigChange = false } = {}) {
|
|
18693
|
+
if (!Number.isFinite(cooldownWatcherTime) || cooldownWatcherTime <= 0)
|
|
18694
|
+
throw new TypeError(`Expected 'cooldownWatcherTime' to be a positive number.`);
|
|
18695
|
+
if (typeof allowAutoConfigChange !== 'boolean')
|
|
18696
|
+
throw new TypeError(`Expected 'allowAutoConfigChange' to be a boolean.`);
|
|
18697
|
+
this.#cooldownWatcherTime = cooldownWatcherTime;
|
|
18698
|
+
this.#allowAutoConfigChange = allowAutoConfigChange;
|
|
18699
|
+
this.setCooldownWatcherTime(cooldownWatcherTime);
|
|
18700
|
+
}
|
|
18701
|
+
|
|
18702
|
+
/**
|
|
18703
|
+
* Whether this instance has been destroyed and is no longer usable.
|
|
18704
|
+
*
|
|
18705
|
+
* @returns {boolean}
|
|
18706
|
+
*/
|
|
18707
|
+
isDestroyed() {
|
|
18708
|
+
return this.#isDestroyed;
|
|
18709
|
+
}
|
|
18710
|
+
|
|
18711
|
+
/**
|
|
18712
|
+
* Whether auto config change is enabled.
|
|
18713
|
+
*
|
|
18714
|
+
* @returns {boolean}
|
|
18715
|
+
*/
|
|
18716
|
+
getAllowAutoConfigChange() {
|
|
18717
|
+
return this.#allowAutoConfigChange;
|
|
18718
|
+
}
|
|
18719
|
+
|
|
18720
|
+
/**
|
|
18721
|
+
* Gets the interval time used for cooldown decrementing.
|
|
18722
|
+
*
|
|
18723
|
+
* @returns {number}
|
|
18724
|
+
*/
|
|
18725
|
+
getCooldownWatcherTime() {
|
|
18726
|
+
return this.#cooldownWatcherTime;
|
|
18727
|
+
}
|
|
18728
|
+
|
|
18729
|
+
/**
|
|
18730
|
+
* Sets whether to allow auto-updating an ID's timeout config if `value` changes.
|
|
18731
|
+
*
|
|
18732
|
+
* @param {boolean} value
|
|
18733
|
+
*/
|
|
18734
|
+
setAllowAutoConfigChange(value) {
|
|
18735
|
+
if (typeof value !== 'boolean') throw new TypeError(`Expected 'value' to be a boolean.`);
|
|
18736
|
+
this.#allowAutoConfigChange = value;
|
|
18737
|
+
}
|
|
18738
|
+
|
|
18739
|
+
/**
|
|
18740
|
+
* Sets the cooldown watcher interval time.
|
|
18741
|
+
* Automatically resets the interval if it was already running.
|
|
18742
|
+
*
|
|
18743
|
+
* @param {number} value
|
|
18744
|
+
*/
|
|
18745
|
+
setCooldownWatcherTime(value) {
|
|
18746
|
+
if (this.#isDestroyed) throw new Error('TinyTimeout has been destroyed.');
|
|
18747
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
18748
|
+
throw new TypeError(`Expected 'value' to be a positive number.`);
|
|
18749
|
+
|
|
18750
|
+
this.#cooldownWatcherTime = value;
|
|
18751
|
+
|
|
18752
|
+
if (this.#cooldownWatcher) clearInterval(this.#cooldownWatcher);
|
|
18753
|
+
this.#cooldownWatcher = setInterval(() => {
|
|
18754
|
+
this.#timeoutFixer.forEach((data) => {
|
|
18755
|
+
if (data.now > 0) data.now--;
|
|
18756
|
+
});
|
|
18757
|
+
}, this.#cooldownWatcherTime);
|
|
18758
|
+
}
|
|
18759
|
+
|
|
18760
|
+
/**
|
|
18761
|
+
* Schedules a callback using a dynamically adjusted timeout based on usage frequency.
|
|
18762
|
+
* The more often an ID is triggered, the longer the timeout becomes,
|
|
18763
|
+
* scaled by the provided `value`. Optionally, the delay can be limited by `limit`.
|
|
18764
|
+
*
|
|
18765
|
+
* @param {string} id - A unique identifier to track timeout usage.
|
|
18766
|
+
* @param {Function} callback - The function to execute after the delay.
|
|
18767
|
+
* @param {number} value - Base delay multiplier in milliseconds.
|
|
18768
|
+
* @param {number|null} [limit=null] - Optional maximum delay cap.
|
|
18769
|
+
* @returns {number} Handle to the scheduled timeout.
|
|
18770
|
+
* @throws {Error} Throws if the instance has been destroyed or arguments are invalid.
|
|
18771
|
+
*/
|
|
18772
|
+
set(id, callback, value, limit = null) {
|
|
18773
|
+
if (this.#isDestroyed) throw new Error('TinyTimeout has been destroyed.');
|
|
18774
|
+
if (typeof id !== 'string' || id.trim() === '')
|
|
18775
|
+
throw new TypeError(`Expected 'id' to be a non-empty string.`);
|
|
18776
|
+
if (typeof callback !== 'function')
|
|
18777
|
+
throw new TypeError(`Expected 'callback' to be a function.`);
|
|
18778
|
+
if (!Number.isFinite(value) || value < 0)
|
|
18779
|
+
throw new TypeError(`Expected 'value' to be a non-negative number.`);
|
|
18780
|
+
if (limit !== null && (!Number.isFinite(limit) || limit < 0))
|
|
18781
|
+
throw new TypeError(`Expected 'limit' to be null or a non-negative number.`);
|
|
18782
|
+
|
|
18783
|
+
let entry = this.#timeoutFixer.get(id);
|
|
18784
|
+
if (!entry || (this.#allowAutoConfigChange && value !== entry.value)) {
|
|
18785
|
+
entry = { value, now: 0 };
|
|
18786
|
+
this.#timeoutFixer.set(id, entry);
|
|
18787
|
+
}
|
|
18788
|
+
|
|
18789
|
+
const delay = entry.value * entry.now;
|
|
18790
|
+
entry.now++;
|
|
18791
|
+
|
|
18792
|
+
return setTimeout(callback, typeof limit === 'number' ? Math.min(delay, limit) : delay);
|
|
18793
|
+
}
|
|
18794
|
+
|
|
18795
|
+
/**
|
|
18796
|
+
* Waits until a provided function returns `true`, checking repeatedly at the defined interval.
|
|
18797
|
+
* Useful for polling asynchronous conditions.
|
|
18798
|
+
*
|
|
18799
|
+
* @param {() => boolean} getValue - A function that returns `true` when the condition is met.
|
|
18800
|
+
* @param {number} [checkInterval=100] - How often (in ms) to check the condition.
|
|
18801
|
+
* @returns {Promise<void>} Resolves when the condition is met.
|
|
18802
|
+
* @throws {TypeError} If arguments are invalid.
|
|
18803
|
+
*/
|
|
18804
|
+
static waitForTrue(getValue, checkInterval = 100) {
|
|
18805
|
+
if (typeof getValue !== 'function')
|
|
18806
|
+
throw new TypeError(`Expected 'getValue' to be a function.`);
|
|
18807
|
+
if (!Number.isFinite(checkInterval) || checkInterval <= 0)
|
|
18808
|
+
throw new TypeError(`Expected 'checkInterval' to be a positive number.`);
|
|
18809
|
+
|
|
18810
|
+
return new Promise((resolve) => {
|
|
18811
|
+
const interval = setInterval(() => {
|
|
18812
|
+
if (getValue()) {
|
|
18813
|
+
clearInterval(interval);
|
|
18814
|
+
resolve();
|
|
18815
|
+
}
|
|
18816
|
+
}, checkInterval);
|
|
18817
|
+
});
|
|
18818
|
+
}
|
|
18819
|
+
|
|
18820
|
+
/**
|
|
18821
|
+
* Instance version of `waitForTrue`, which defaults to using the instance's
|
|
18822
|
+
* cooldownWatcherTime if not explicitly provided.
|
|
18823
|
+
*
|
|
18824
|
+
* @param {() => boolean} getValue - A function that returns `true` when the condition is met.
|
|
18825
|
+
* @param {number|null} [checkInterval=100] - How often (in ms) to check the condition.
|
|
18826
|
+
* @returns {Promise<void>} Resolves when the condition is met.
|
|
18827
|
+
* @throws {Error} If the instance is destroyed or arguments are invalid.
|
|
18828
|
+
*/
|
|
18829
|
+
waitForTrue(getValue, checkInterval = 100) {
|
|
18830
|
+
if (this.#isDestroyed) throw new Error('TinyTimeout has been destroyed.');
|
|
18831
|
+
if (typeof getValue !== 'function')
|
|
18832
|
+
throw new TypeError(`Expected 'getValue' to be a function.`);
|
|
18833
|
+
if (checkInterval !== null && (!Number.isFinite(checkInterval) || checkInterval <= 0))
|
|
18834
|
+
throw new TypeError(`Expected 'checkInterval' to be null or a positive number.`);
|
|
18835
|
+
return TinyTimeout.waitForTrue(getValue, checkInterval ?? this.#cooldownWatcherTime);
|
|
18836
|
+
}
|
|
18837
|
+
|
|
18838
|
+
/**
|
|
18839
|
+
* Cleans up all internal references and stops the cooldown watcher.
|
|
18840
|
+
* After calling this, the instance becomes unusable.
|
|
18841
|
+
*/
|
|
18842
|
+
destroy() {
|
|
18843
|
+
if (this.#isDestroyed) return;
|
|
18844
|
+
this.#isDestroyed = true;
|
|
18845
|
+
if (this.#cooldownWatcher) clearInterval(this.#cooldownWatcher);
|
|
18846
|
+
this.#cooldownWatcher = null;
|
|
18847
|
+
this.#timeoutFixer.clear();
|
|
18848
|
+
}
|
|
18849
|
+
}
|
|
18850
|
+
|
|
18851
|
+
/* harmony default export */ const libs_TinyTimeout = (TinyTimeout);
|
|
18852
|
+
|
|
18853
|
+
;// ./src/v1/libs/TinyLocalStorage.mjs
|
|
18854
|
+
|
|
18855
|
+
|
|
18856
|
+
|
|
18857
|
+
/** @type {Map<any, EncodeFn>} */
|
|
18858
|
+
const customEncoders = new Map();
|
|
18859
|
+
|
|
18860
|
+
/** @type {Map<any, DecodeFn>} */
|
|
18861
|
+
const customDecoders = new Map();
|
|
18862
|
+
|
|
18863
|
+
/**
|
|
18864
|
+
* A function that encodes a value into a serializable JSON-compatible format.
|
|
18865
|
+
*
|
|
18866
|
+
* @callback EncodeFn
|
|
18867
|
+
* @param {any} value - The value to encode.
|
|
18868
|
+
* @param {encodeSpecialJson} encodeSpecialJson - Recursive encoder helper.
|
|
18869
|
+
* @returns {any} The encoded value.
|
|
18870
|
+
*/
|
|
18871
|
+
|
|
18872
|
+
/**
|
|
18873
|
+
* An object that defines how to check and decode a specific serialized type.
|
|
18874
|
+
*
|
|
18875
|
+
* @typedef {Object} DecodeFn
|
|
18876
|
+
* @property {(value: any) => any} check - Checks if the value matches the custom encoded structure.
|
|
18877
|
+
* @property {(value: any, decodeSpecialJson: decodeSpecialJson) => any} decode - Decodes the structure back into its original form.
|
|
18878
|
+
*/
|
|
18879
|
+
|
|
18880
|
+
/**
|
|
18881
|
+
* Encodes extended JSON-compatible structures recursively.
|
|
18882
|
+
* @callback encodeSpecialJson
|
|
18883
|
+
* @param {any} value
|
|
18884
|
+
* @returns {any}
|
|
18885
|
+
*/
|
|
18886
|
+
|
|
18887
|
+
/**
|
|
18888
|
+
* Decodes extended JSON-compatible structures recursively.
|
|
18889
|
+
* @callback decodeSpecialJson
|
|
18890
|
+
* @param {any} value
|
|
18891
|
+
* @returns {any}
|
|
18892
|
+
*/
|
|
18893
|
+
|
|
18894
|
+
/**
|
|
18895
|
+
* Represents a value that can be safely stored and restored using JSON in `localStorage`,
|
|
18896
|
+
* including structures like arrays, plain objects, Map and Set.
|
|
18897
|
+
*
|
|
18898
|
+
* - `Record<string|number|symbol, any>` → plain object (e.g., `{ key: value }`)
|
|
18899
|
+
* - `any[]` → array of any JSON-serializable values
|
|
18900
|
+
* - `Map<string|number|symbol, any>` → converted to `{ __map__: true, data: [[k, v], ...] }`
|
|
18901
|
+
* - `Set<any>` → converted to `{ __set__: true, data: [v1, v2, ...] }`
|
|
18902
|
+
*
|
|
18903
|
+
* These conversions allow complex structures to be restored after JSON serialization.
|
|
18904
|
+
*
|
|
18905
|
+
* @typedef {(Record<string|number|symbol, any> | any[] | Map<string|number|symbol, any> | Set<any>)} LocalStorageJsonValue
|
|
18906
|
+
*/
|
|
18907
|
+
|
|
18908
|
+
/**
|
|
18909
|
+
* A powerful wrapper for Web Storage (`localStorage` or `sessionStorage`) that supports
|
|
18910
|
+
* type-safe methods and full JSON-like structure encoding and decoding.
|
|
18911
|
+
*
|
|
18912
|
+
* `TinyLocalStorage` allows storing and retrieving complex types such as:
|
|
18913
|
+
* - `Map`, `Set`
|
|
18914
|
+
* - `Date`, `RegExp`
|
|
18915
|
+
* - `BigInt`, `Symbol`
|
|
18916
|
+
* - `undefined`, `null`
|
|
18917
|
+
* - Plain objects and arrays
|
|
18918
|
+
*
|
|
18919
|
+
* Includes:
|
|
18920
|
+
* - Type-specific `set` and `get` methods (`setDate`, `getBool`, etc.)
|
|
18921
|
+
* - `getValue()` to retrieve any structure regardless of type
|
|
18922
|
+
* - Auto-encoding/decoding with support for custom types via `registerJsonType`
|
|
18923
|
+
* - Built-in event system (`TinyEvents`) to listen for changes
|
|
18924
|
+
* - Optional fallback values on decoding errors
|
|
18925
|
+
*
|
|
18926
|
+
* Supports registering and unregistering custom types via:
|
|
18927
|
+
* - `registerJsonType(...)`
|
|
18928
|
+
* - `deleteJsonType(...)`
|
|
18929
|
+
*
|
|
18930
|
+
* This class is suitable for applications that require structured persistence in the browser.
|
|
18931
|
+
*/
|
|
18932
|
+
class TinyLocalStorage {
|
|
18933
|
+
/** @typedef {import('./TinyEvents.mjs').handler} handler */
|
|
18934
|
+
|
|
18935
|
+
#events = new libs_TinyEvents();
|
|
18936
|
+
|
|
18937
|
+
/**
|
|
18938
|
+
* Enables or disables throwing an error when the maximum number of listeners is exceeded.
|
|
18939
|
+
*
|
|
18940
|
+
* @param {boolean} shouldThrow - If true, an error will be thrown when the max is exceeded.
|
|
18941
|
+
*/
|
|
18942
|
+
setThrowOnMaxListeners(shouldThrow) {
|
|
18943
|
+
return this.#events.setThrowOnMaxListeners(shouldThrow);
|
|
18944
|
+
}
|
|
18945
|
+
|
|
18946
|
+
/**
|
|
18947
|
+
* Checks whether an error will be thrown when the max listener limit is exceeded.
|
|
18948
|
+
*
|
|
18949
|
+
* @returns {boolean} True if an error will be thrown, false if only a warning is shown.
|
|
18950
|
+
*/
|
|
18951
|
+
getThrowOnMaxListeners() {
|
|
18952
|
+
return this.#events.getThrowOnMaxListeners();
|
|
18953
|
+
}
|
|
18954
|
+
|
|
18955
|
+
/////////////////////////////////////////////////////////////
|
|
18956
|
+
|
|
18957
|
+
/**
|
|
18958
|
+
* Adds a listener to the beginning of the listeners array for the specified event.
|
|
18959
|
+
*
|
|
18960
|
+
* @param {string} event - Event name.
|
|
18961
|
+
* @param {handler} handler - The callback function.
|
|
18962
|
+
*/
|
|
18963
|
+
prependListener(event, handler) {
|
|
18964
|
+
return this.#events.prependListener(event, handler);
|
|
18965
|
+
}
|
|
18966
|
+
|
|
18967
|
+
/**
|
|
18968
|
+
* Adds a one-time listener to the beginning of the listeners array for the specified event.
|
|
18969
|
+
*
|
|
18970
|
+
* @param {string} event - Event name.
|
|
18971
|
+
* @param {handler} handler - The callback function.
|
|
18972
|
+
* @returns {handler} - The wrapped handler used internally.
|
|
18973
|
+
*/
|
|
18974
|
+
prependListenerOnce(event, handler) {
|
|
18975
|
+
return this.#events.prependListenerOnce(event, handler);
|
|
18976
|
+
}
|
|
18977
|
+
|
|
18978
|
+
//////////////////////////////////////////////////////////////////////
|
|
18979
|
+
|
|
18980
|
+
/**
|
|
18981
|
+
* Adds a event listener.
|
|
18982
|
+
*
|
|
18983
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
18984
|
+
* @param {handler} handler - Callback function to be called when event fires.
|
|
18985
|
+
*/
|
|
18986
|
+
appendListener(event, handler) {
|
|
18987
|
+
return this.#events.appendListener(event, handler);
|
|
18988
|
+
}
|
|
18989
|
+
|
|
18990
|
+
/**
|
|
18991
|
+
* Registers an event listener that runs only once, then is removed.
|
|
18992
|
+
*
|
|
18993
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
18994
|
+
* @param {handler} handler - The callback function to run on event.
|
|
18995
|
+
* @returns {handler} - The wrapped version of the handler.
|
|
18996
|
+
*/
|
|
18997
|
+
appendListenerOnce(event, handler) {
|
|
18998
|
+
return this.#events.appendListenerOnce(event, handler);
|
|
18999
|
+
}
|
|
19000
|
+
|
|
19001
|
+
/**
|
|
19002
|
+
* Adds a event listener.
|
|
19003
|
+
*
|
|
19004
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
19005
|
+
* @param {handler} handler - Callback function to be called when event fires.
|
|
19006
|
+
*/
|
|
19007
|
+
on(event, handler) {
|
|
19008
|
+
return this.#events.on(event, handler);
|
|
19009
|
+
}
|
|
19010
|
+
|
|
19011
|
+
/**
|
|
19012
|
+
* Registers an event listener that runs only once, then is removed.
|
|
19013
|
+
*
|
|
19014
|
+
* @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
|
|
19015
|
+
* @param {handler} handler - The callback function to run on event.
|
|
19016
|
+
* @returns {handler} - The wrapped version of the handler.
|
|
19017
|
+
*/
|
|
19018
|
+
once(event, handler) {
|
|
19019
|
+
return this.#events.once(event, handler);
|
|
19020
|
+
}
|
|
19021
|
+
|
|
19022
|
+
////////////////////////////////////////////////////////////////////
|
|
19023
|
+
|
|
19024
|
+
/**
|
|
19025
|
+
* Removes a previously registered event listener.
|
|
17035
19026
|
*
|
|
17036
|
-
* @param {string}
|
|
17037
|
-
* @param {
|
|
17038
|
-
|
|
17039
|
-
|
|
19027
|
+
* @param {string} event - The name of the event to remove the handler from.
|
|
19028
|
+
* @param {handler} handler - The specific callback function to remove.
|
|
19029
|
+
*/
|
|
19030
|
+
off(event, handler) {
|
|
19031
|
+
return this.#events.off(event, handler);
|
|
19032
|
+
}
|
|
19033
|
+
|
|
19034
|
+
/**
|
|
19035
|
+
* Removes all event listeners of a specific type from the element.
|
|
17040
19036
|
*
|
|
17041
|
-
* @
|
|
19037
|
+
* @param {string} event - The event type to remove (e.g. 'onScrollBoundary').
|
|
17042
19038
|
*/
|
|
17043
|
-
|
|
17044
|
-
|
|
17045
|
-
const attrStr = this._insertAttr(attributes);
|
|
17046
|
-
const openTag = attrStr
|
|
17047
|
-
? `${this.#openTag}${tagName} ${attrStr}${this.#closeTag}`
|
|
17048
|
-
: `${this.#openTag}${tagName}${this.#closeTag}`;
|
|
17049
|
-
const closeTag = `${this.#openTag}/${tagName}${this.#closeTag}`;
|
|
17050
|
-
this.surroundSelection(openTag, closeTag);
|
|
17051
|
-
return this;
|
|
19039
|
+
offAll(event) {
|
|
19040
|
+
return this.#events.offAll(event);
|
|
17052
19041
|
}
|
|
17053
19042
|
|
|
17054
19043
|
/**
|
|
17055
|
-
*
|
|
17056
|
-
* @param {string} tagName - The tag to insert.
|
|
17057
|
-
* @param {string} [content=''] - Optional content between tags.
|
|
17058
|
-
* @param {Record<string,string> | string[]} [attributes={}] - Optional attributes or list of empty attributes.
|
|
17059
|
-
* @returns {TinyTextRangeEditor}
|
|
19044
|
+
* Removes all event listeners of all types from the element.
|
|
17060
19045
|
*/
|
|
17061
|
-
|
|
17062
|
-
|
|
17063
|
-
|
|
19046
|
+
offAllTypes() {
|
|
19047
|
+
return this.#events.offAllTypes();
|
|
19048
|
+
}
|
|
17064
19049
|
|
|
17065
|
-
|
|
17066
|
-
const open = attrStr
|
|
17067
|
-
? `${this.#openTag}${tagName} ${attrStr}${this.#closeTag}`
|
|
17068
|
-
: `${this.#openTag}${tagName}${this.#closeTag}`;
|
|
17069
|
-
const close = `${this.#openTag}/${tagName}${this.#closeTag}`;
|
|
19050
|
+
////////////////////////////////////////////////////////////
|
|
17070
19051
|
|
|
17071
|
-
|
|
17072
|
-
|
|
19052
|
+
/**
|
|
19053
|
+
* Returns the number of listeners for a given event.
|
|
19054
|
+
*
|
|
19055
|
+
* @param {string} event - The name of the event.
|
|
19056
|
+
* @returns {number} Number of listeners for the event.
|
|
19057
|
+
*/
|
|
19058
|
+
listenerCount(event) {
|
|
19059
|
+
return this.#events.listenerCount(event);
|
|
17073
19060
|
}
|
|
17074
19061
|
|
|
17075
19062
|
/**
|
|
17076
|
-
*
|
|
17077
|
-
*
|
|
17078
|
-
* @param {
|
|
17079
|
-
* @returns {
|
|
19063
|
+
* Returns a copy of the array of listeners for the specified event.
|
|
19064
|
+
*
|
|
19065
|
+
* @param {string} event - The name of the event.
|
|
19066
|
+
* @returns {handler[]} Array of listener functions.
|
|
17080
19067
|
*/
|
|
17081
|
-
|
|
17082
|
-
|
|
19068
|
+
listeners(event) {
|
|
19069
|
+
return this.#events.listeners(event);
|
|
19070
|
+
}
|
|
17083
19071
|
|
|
17084
|
-
|
|
17085
|
-
|
|
17086
|
-
|
|
17087
|
-
|
|
19072
|
+
/**
|
|
19073
|
+
* Returns a copy of the array of listeners for the specified event.
|
|
19074
|
+
*
|
|
19075
|
+
* @param {string} event - The name of the event.
|
|
19076
|
+
* @returns {handler[]} Array of listener functions.
|
|
19077
|
+
*/
|
|
19078
|
+
onceListeners(event) {
|
|
19079
|
+
return this.#events.onceListeners(event);
|
|
19080
|
+
}
|
|
17088
19081
|
|
|
17089
|
-
|
|
17090
|
-
|
|
19082
|
+
/**
|
|
19083
|
+
* Returns a copy of the internal listeners array for the specified event,
|
|
19084
|
+
* including wrapper functions like those used by `.once()`.
|
|
19085
|
+
* @param {string | symbol} event - The event name.
|
|
19086
|
+
* @returns {handler[]} An array of raw listener functions.
|
|
19087
|
+
*/
|
|
19088
|
+
allListeners(event) {
|
|
19089
|
+
return this.#events.allListeners(event);
|
|
17091
19090
|
}
|
|
17092
19091
|
|
|
17093
19092
|
/**
|
|
17094
|
-
*
|
|
17095
|
-
*
|
|
17096
|
-
* @
|
|
17097
|
-
* @param {Record<string,string> | string[]} [attributes={}] - Optional attributes to apply when wrapping.
|
|
17098
|
-
* @returns {TinyTextRangeEditor}
|
|
19093
|
+
* Returns an array of event names for which there are registered listeners.
|
|
19094
|
+
*
|
|
19095
|
+
* @returns {string[]} Array of registered event names.
|
|
17099
19096
|
*/
|
|
17100
|
-
|
|
17101
|
-
|
|
17102
|
-
|
|
19097
|
+
eventNames() {
|
|
19098
|
+
return this.#events.eventNames();
|
|
19099
|
+
}
|
|
17103
19100
|
|
|
17104
|
-
|
|
17105
|
-
const openRegex = new RegExp(`^\\[${tagName}(\\s+[^\\]]*)?\\]`);
|
|
17106
|
-
const closeRegex = new RegExp(`\\[/${tagName}\\]$`);
|
|
19101
|
+
//////////////////////////////////////////////////////
|
|
17107
19102
|
|
|
17108
|
-
|
|
17109
|
-
|
|
19103
|
+
/**
|
|
19104
|
+
* Emits an event, triggering all registered handlers for that event.
|
|
19105
|
+
*
|
|
19106
|
+
* @param {string} event - The event name to emit.
|
|
19107
|
+
* @param {...any} payload - Optional data to pass to each handler.
|
|
19108
|
+
* @returns {boolean} True if any listeners were called, false otherwise.
|
|
19109
|
+
*/
|
|
19110
|
+
emit(event, ...payload) {
|
|
19111
|
+
return this.#events.emit(event, ...payload);
|
|
19112
|
+
}
|
|
17110
19113
|
|
|
17111
|
-
|
|
17112
|
-
|
|
17113
|
-
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
? `${this.#openTag}${tagName} ${attrStr}${this.#closeTag}`
|
|
17120
|
-
: `${this.#openTag}${tagName}${this.#closeTag}`;
|
|
17121
|
-
const close = `${this.#openTag}/${tagName}${this.#closeTag}`;
|
|
17122
|
-
this.insertText(`${open}${selected}${close}`);
|
|
17123
|
-
}
|
|
19114
|
+
/**
|
|
19115
|
+
* Sets the maximum number of listeners per event before a warning is shown.
|
|
19116
|
+
*
|
|
19117
|
+
* @param {number} n - The maximum number of listeners.
|
|
19118
|
+
*/
|
|
19119
|
+
setMaxListeners(n) {
|
|
19120
|
+
return this.#events.setMaxListeners(n);
|
|
19121
|
+
}
|
|
17124
19122
|
|
|
17125
|
-
|
|
19123
|
+
/**
|
|
19124
|
+
* Gets the maximum number of listeners allowed per event.
|
|
19125
|
+
*
|
|
19126
|
+
* @returns {number} The maximum number of listeners.
|
|
19127
|
+
*/
|
|
19128
|
+
getMaxListeners() {
|
|
19129
|
+
return this.#events.getMaxListeners();
|
|
17126
19130
|
}
|
|
17127
|
-
}
|
|
17128
19131
|
|
|
17129
|
-
|
|
19132
|
+
///////////////////////////////////////////////////
|
|
17130
19133
|
|
|
17131
|
-
;// ./src/v1/libs/TinyClipboard.mjs
|
|
17132
|
-
/**
|
|
17133
|
-
* Utility class to handle clipboard operations for text and blob data.
|
|
17134
|
-
* Supports modern Clipboard API, custom platform, and legacy execCommand fallback.
|
|
17135
|
-
*/
|
|
17136
|
-
class TinyClipboard {
|
|
17137
19134
|
/**
|
|
17138
|
-
*
|
|
17139
|
-
* Used as a fallback for clipboard operations when modern APIs are not supported.
|
|
19135
|
+
* Registers a new JSON-serializable type with its encoder and decoder.
|
|
17140
19136
|
*
|
|
17141
|
-
* @
|
|
19137
|
+
* @param {any} type - The type or primitive type name (e.g. `"bigint"`, `"symbol"`, etc).
|
|
19138
|
+
* @param {EncodeFn} encodeFn - The function that encodes the value.
|
|
19139
|
+
* @param {DecodeFn} decodeFn - An object with `check` and `decode` methods for restoring the value.
|
|
17142
19140
|
*/
|
|
17143
|
-
|
|
19141
|
+
static registerJsonType(type, encodeFn, decodeFn) {
|
|
19142
|
+
customEncoders.set(type, encodeFn);
|
|
19143
|
+
customDecoders.set(type, decodeFn);
|
|
19144
|
+
}
|
|
17144
19145
|
|
|
17145
19146
|
/**
|
|
17146
|
-
*
|
|
19147
|
+
* Removes a previously registered custom type from the encoding/decoding system.
|
|
17147
19148
|
*
|
|
17148
|
-
* @
|
|
19149
|
+
* @param {string} type - The primitive name or constructor reference used in registration.
|
|
17149
19150
|
*/
|
|
17150
|
-
|
|
19151
|
+
static deleteJsonType(type) {
|
|
19152
|
+
customEncoders.delete(type);
|
|
19153
|
+
customDecoders.delete(type);
|
|
19154
|
+
}
|
|
19155
|
+
|
|
19156
|
+
//////////////////////////////////////////////////////
|
|
17151
19157
|
|
|
17152
19158
|
/**
|
|
17153
|
-
*
|
|
17154
|
-
* Can be overridden using `setCopyText()`.
|
|
19159
|
+
* Recursively serializes a value to a JSON-compatible format.
|
|
17155
19160
|
*
|
|
17156
|
-
*
|
|
19161
|
+
* This includes custom types (via `registerJsonType`), plus support for:
|
|
19162
|
+
* - `undefined` → `{ __undefined__: true }`
|
|
19163
|
+
* - `null` → `{ __null__: true }`
|
|
19164
|
+
*
|
|
19165
|
+
* @type {encodeSpecialJson}
|
|
17157
19166
|
*/
|
|
17158
|
-
|
|
19167
|
+
static encodeSpecialJson(value) {
|
|
19168
|
+
if (typeof value === 'undefined') return { __undefined__: true };
|
|
19169
|
+
if (value === null) return { __null__: true };
|
|
19170
|
+
for (const [type, encoder] of customEncoders.entries()) {
|
|
19171
|
+
if ((typeof type !== 'string' && value instanceof type) || typeof value === type) {
|
|
19172
|
+
return encoder(value, TinyLocalStorage.encodeSpecialJson);
|
|
19173
|
+
}
|
|
19174
|
+
}
|
|
19175
|
+
|
|
19176
|
+
if (Array.isArray(value)) {
|
|
19177
|
+
return value.map(TinyLocalStorage.encodeSpecialJson);
|
|
19178
|
+
}
|
|
19179
|
+
|
|
19180
|
+
if (isJsonObject(value)) {
|
|
19181
|
+
const encoded = {};
|
|
19182
|
+
for (const key in value) {
|
|
19183
|
+
// @ts-ignore
|
|
19184
|
+
encoded[key] = TinyLocalStorage.encodeSpecialJson(value[key]);
|
|
19185
|
+
}
|
|
19186
|
+
return encoded;
|
|
19187
|
+
}
|
|
19188
|
+
|
|
19189
|
+
return value;
|
|
19190
|
+
}
|
|
17159
19191
|
|
|
17160
19192
|
/**
|
|
17161
|
-
*
|
|
17162
|
-
* Can be overridden using `setCopyBlob()`.
|
|
19193
|
+
* Recursively deserializes a JSON-compatible value into its original structure.
|
|
17163
19194
|
*
|
|
17164
|
-
*
|
|
19195
|
+
* Automatically handles:
|
|
19196
|
+
* - `__undefined__` → `undefined`
|
|
19197
|
+
* - `__null__` → `null`
|
|
19198
|
+
* - Any type registered via `registerJsonType`
|
|
19199
|
+
*
|
|
19200
|
+
* @type {decodeSpecialJson}
|
|
17165
19201
|
*/
|
|
17166
|
-
|
|
19202
|
+
static decodeSpecialJson(value) {
|
|
19203
|
+
const isJson = isJsonObject(value);
|
|
19204
|
+
if (isJson) {
|
|
19205
|
+
if (value.__undefined__) return undefined;
|
|
19206
|
+
if (value.__null__) return null;
|
|
19207
|
+
}
|
|
19208
|
+
|
|
19209
|
+
if (Array.isArray(value)) {
|
|
19210
|
+
return value.map(TinyLocalStorage.decodeSpecialJson);
|
|
19211
|
+
}
|
|
19212
|
+
|
|
19213
|
+
if (isJson) {
|
|
19214
|
+
for (const [type, decoder] of customDecoders.entries()) {
|
|
19215
|
+
if (decoder.check && decoder.check(value)) {
|
|
19216
|
+
return decoder.decode(value, TinyLocalStorage.decodeSpecialJson);
|
|
19217
|
+
}
|
|
19218
|
+
}
|
|
19219
|
+
|
|
19220
|
+
const decoded = {};
|
|
19221
|
+
for (const key in value) {
|
|
19222
|
+
// @ts-ignore
|
|
19223
|
+
decoded[key] = TinyLocalStorage.decodeSpecialJson(value[key]);
|
|
19224
|
+
}
|
|
19225
|
+
return decoded;
|
|
19226
|
+
}
|
|
19227
|
+
|
|
19228
|
+
return value;
|
|
19229
|
+
}
|
|
19230
|
+
|
|
19231
|
+
//////////////////////////////////////////////////////
|
|
19232
|
+
|
|
19233
|
+
/** @type {Storage} */
|
|
19234
|
+
#localStorage = window.localStorage;
|
|
19235
|
+
|
|
19236
|
+
/** @type {(ev: StorageEvent) => any} */
|
|
19237
|
+
#storageEvent = (ev) => this.emit('storage', ev);
|
|
17167
19238
|
|
|
17168
19239
|
/**
|
|
17169
|
-
*
|
|
17170
|
-
*
|
|
19240
|
+
* Initializes the TinyLocalStorage instance and sets up cross-tab sync.
|
|
19241
|
+
*
|
|
19242
|
+
* Adds listener for the native `storage` event to support tab synchronization.
|
|
17171
19243
|
*/
|
|
17172
19244
|
constructor() {
|
|
17173
|
-
|
|
17174
|
-
|
|
17175
|
-
this.#existNavigator = true;
|
|
17176
|
-
this.#copyText = (text) => navigator.clipboard.writeText(text);
|
|
17177
|
-
this.#copyBlob = (blob) =>
|
|
17178
|
-
navigator.clipboard.write([
|
|
17179
|
-
new ClipboardItem({
|
|
17180
|
-
[blob.type]: blob,
|
|
17181
|
-
}),
|
|
17182
|
-
]);
|
|
17183
|
-
}
|
|
19245
|
+
window.addEventListener('storage', this.#storageEvent);
|
|
19246
|
+
}
|
|
17184
19247
|
|
|
17185
|
-
|
|
17186
|
-
|
|
17187
|
-
|
|
17188
|
-
|
|
17189
|
-
|
|
17190
|
-
|
|
19248
|
+
/**
|
|
19249
|
+
* Defines a custom storage interface (e.g. `sessionStorage`).
|
|
19250
|
+
*
|
|
19251
|
+
* @param {Storage} localstorage - A valid Storage object (localStorage or sessionStorage).
|
|
19252
|
+
*/
|
|
19253
|
+
setLocalStorage(localstorage) {
|
|
19254
|
+
if (!(localstorage instanceof Storage))
|
|
19255
|
+
throw new Error('Argument must be a valid instance of Storage.');
|
|
19256
|
+
this.#localStorage = localstorage;
|
|
17191
19257
|
}
|
|
17192
19258
|
|
|
17193
19259
|
/**
|
|
17194
|
-
*
|
|
17195
|
-
* This allows you to provide your own clipboard implementation or
|
|
17196
|
-
* integrate with external systems like Capacitor or Electron.
|
|
19260
|
+
* Checks if `localStorage` is supported by the current environment.
|
|
17197
19261
|
*
|
|
17198
|
-
* @
|
|
17199
|
-
* @throws {TypeError} If the callback is not a function.
|
|
19262
|
+
* @returns {boolean} True if `localStorage` exists, false otherwise.
|
|
17200
19263
|
*/
|
|
17201
|
-
|
|
17202
|
-
|
|
17203
|
-
throw new TypeError('setCopyText expected a function that returns Promise<void>.');
|
|
17204
|
-
this.#copyText = callback;
|
|
19264
|
+
localStorageExists() {
|
|
19265
|
+
return this.#localStorage instanceof Storage;
|
|
17205
19266
|
}
|
|
17206
19267
|
|
|
17207
19268
|
/**
|
|
17208
|
-
*
|
|
17209
|
-
* This allows you to provide a custom clipboard handling method for blob data.
|
|
19269
|
+
* Automatically serializes nested instances.
|
|
17210
19270
|
*
|
|
17211
|
-
* @param {
|
|
17212
|
-
* @
|
|
19271
|
+
* @param {string} name - The key under which to store the data.
|
|
19272
|
+
* @param {*} data - The data to be serialized.
|
|
19273
|
+
* @returns {*}
|
|
17213
19274
|
*/
|
|
17214
|
-
|
|
17215
|
-
if (typeof
|
|
17216
|
-
throw new
|
|
17217
|
-
|
|
19275
|
+
#setJson(name, data) {
|
|
19276
|
+
if (typeof name !== 'string' || !name.length)
|
|
19277
|
+
throw new Error('Key must be a non-empty string.');
|
|
19278
|
+
return TinyLocalStorage.encodeSpecialJson(data);
|
|
17218
19279
|
}
|
|
17219
19280
|
|
|
17220
19281
|
/**
|
|
17221
|
-
*
|
|
17222
|
-
* Uses modern or legacy fallback.
|
|
19282
|
+
* Stores a JSON-compatible value in `localStorage`.
|
|
17223
19283
|
*
|
|
17224
|
-
*
|
|
17225
|
-
*
|
|
19284
|
+
* Automatically serializes nested `Map` and `Set` instances.
|
|
19285
|
+
*
|
|
19286
|
+
* @param {string} name - The key under which to store the data.
|
|
19287
|
+
* @param {LocalStorageJsonValue} data - The data to be serialized and stored.
|
|
17226
19288
|
*/
|
|
17227
|
-
|
|
17228
|
-
if (
|
|
17229
|
-
|
|
17230
|
-
|
|
17231
|
-
|
|
17232
|
-
|
|
17233
|
-
|
|
17234
|
-
|
|
17235
|
-
|
|
17236
|
-
|
|
17237
|
-
|
|
17238
|
-
|
|
19289
|
+
setJson(name, data) {
|
|
19290
|
+
if (
|
|
19291
|
+
!isJsonObject(data) &&
|
|
19292
|
+
!Array.isArray(data) &&
|
|
19293
|
+
!(data instanceof Map) &&
|
|
19294
|
+
!(data instanceof Set)
|
|
19295
|
+
) {
|
|
19296
|
+
throw new Error('The storage value is not a valid JSON-compatible structure.');
|
|
19297
|
+
}
|
|
19298
|
+
const encoded = this.#setJson(name, data);
|
|
19299
|
+
this.emit('setJson', name, data);
|
|
19300
|
+
this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
19301
|
+
}
|
|
17239
19302
|
|
|
17240
|
-
|
|
17241
|
-
|
|
17242
|
-
|
|
17243
|
-
|
|
17244
|
-
|
|
19303
|
+
/**
|
|
19304
|
+
* Retrieves a value from `localStorage`.
|
|
19305
|
+
*
|
|
19306
|
+
* Automatically restores nested instances.
|
|
19307
|
+
*
|
|
19308
|
+
* @param {string} name - The key to retrieve.
|
|
19309
|
+
* @param {'array'|'obj'|'map'|'set'|'null'} [defaultData] - Default fallback format if value is invalid.
|
|
19310
|
+
* @returns {{ decoded: any, fallback: any }} The parsed object or fallback.
|
|
19311
|
+
*/
|
|
19312
|
+
#getJson(name, defaultData) {
|
|
19313
|
+
if (typeof name !== 'string' || !name.length)
|
|
19314
|
+
throw new Error('Key must be a non-empty string.');
|
|
19315
|
+
|
|
19316
|
+
const raw = this.#localStorage.getItem(name);
|
|
19317
|
+
const fallbackTypes = {
|
|
19318
|
+
obj: () => ({}),
|
|
19319
|
+
array: () => [],
|
|
19320
|
+
map: () => new Map(),
|
|
19321
|
+
set: () => new Set(),
|
|
19322
|
+
};
|
|
19323
|
+
|
|
19324
|
+
const fallback =
|
|
19325
|
+
// @ts-ignore
|
|
19326
|
+
typeof fallbackTypes[defaultData] === 'function' ? fallbackTypes[defaultData]() : null;
|
|
19327
|
+
|
|
19328
|
+
let parsed;
|
|
19329
|
+
|
|
19330
|
+
try {
|
|
19331
|
+
// @ts-ignore
|
|
19332
|
+
parsed = JSON.parse(raw);
|
|
19333
|
+
} catch {
|
|
19334
|
+
// @ts-ignore
|
|
19335
|
+
return fallback;
|
|
17245
19336
|
}
|
|
17246
|
-
|
|
19337
|
+
|
|
19338
|
+
return { decoded: TinyLocalStorage.decodeSpecialJson(parsed), fallback };
|
|
17247
19339
|
}
|
|
17248
19340
|
|
|
17249
19341
|
/**
|
|
17250
|
-
*
|
|
19342
|
+
* Retrieves and parses a JSON value from `localStorage`.
|
|
17251
19343
|
*
|
|
17252
|
-
*
|
|
17253
|
-
*
|
|
19344
|
+
* Automatically restores nested `Map` and `Set` instances.
|
|
19345
|
+
*
|
|
19346
|
+
* @param {string} name - The key to retrieve.
|
|
19347
|
+
* @param {'array'|'obj'|'map'|'set'|'null'} [defaultData] - Default fallback format if value is invalid.
|
|
19348
|
+
* @returns {LocalStorageJsonValue|null} The parsed object or fallback.
|
|
17254
19349
|
*/
|
|
17255
|
-
|
|
17256
|
-
|
|
17257
|
-
|
|
17258
|
-
|
|
17259
|
-
|
|
17260
|
-
|
|
17261
|
-
|
|
17262
|
-
|
|
19350
|
+
getJson(name, defaultData) {
|
|
19351
|
+
const { decoded, fallback } = this.#getJson(name, defaultData);
|
|
19352
|
+
if (
|
|
19353
|
+
decoded instanceof Map ||
|
|
19354
|
+
decoded instanceof Set ||
|
|
19355
|
+
Array.isArray(decoded) ||
|
|
19356
|
+
isJsonObject(decoded)
|
|
19357
|
+
)
|
|
19358
|
+
return decoded;
|
|
19359
|
+
return fallback;
|
|
17263
19360
|
}
|
|
17264
19361
|
|
|
17265
19362
|
/**
|
|
17266
|
-
*
|
|
17267
|
-
*
|
|
17268
|
-
* @
|
|
17269
|
-
* @param {string} type - The MIME type to fetch.
|
|
17270
|
-
* @param {ClipboardItem} clipboardItem - Clipboard item instance.
|
|
17271
|
-
* @returns {Promise<Blob>} A promise that resolves with the Blob.
|
|
19363
|
+
* Stores a Date in localStorage.
|
|
19364
|
+
* @param {string} name
|
|
19365
|
+
* @param {Date} data
|
|
17272
19366
|
*/
|
|
17273
|
-
|
|
17274
|
-
|
|
19367
|
+
setDate(name, data) {
|
|
19368
|
+
if (!(data instanceof Date)) throw new Error('Value must be a Date.');
|
|
19369
|
+
const encoded = this.#setJson(name, data);
|
|
19370
|
+
this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
19371
|
+
this.emit('setDate', name, data);
|
|
17275
19372
|
}
|
|
17276
19373
|
|
|
17277
19374
|
/**
|
|
17278
|
-
*
|
|
17279
|
-
*
|
|
17280
|
-
* @
|
|
17281
|
-
* @param {string} type - The MIME type (should be 'text/plain').
|
|
17282
|
-
* @param {ClipboardItem} clipboardItem - Clipboard item instance.
|
|
17283
|
-
* @returns {Promise<string>} A promise that resolves with the text content.
|
|
19375
|
+
* Retrieves a Date from localStorage.
|
|
19376
|
+
* @param {string} name
|
|
19377
|
+
* @returns {Date|null}
|
|
17284
19378
|
*/
|
|
17285
|
-
|
|
17286
|
-
|
|
19379
|
+
getDate(name) {
|
|
19380
|
+
const value = this.#getJson(name).decoded;
|
|
19381
|
+
return value instanceof Date ? value : null;
|
|
17287
19382
|
}
|
|
17288
19383
|
|
|
17289
19384
|
/**
|
|
17290
|
-
*
|
|
17291
|
-
*
|
|
17292
|
-
* @param {
|
|
17293
|
-
* @param {'text'|'custom'|null} [type=null] - Data type to filter.
|
|
17294
|
-
* @param {string|null} [mimeFormat=null] - MIME type or prefix.
|
|
17295
|
-
* @param {boolean} [fixValue=false] - If true, exact match on MIME type.
|
|
17296
|
-
* @returns {Promise<Blob|string|Array<Blob|string>|null>} A promise resolving with matching data.
|
|
19385
|
+
* Stores a RegExp in localStorage.
|
|
19386
|
+
* @param {string} name
|
|
19387
|
+
* @param {RegExp} data
|
|
17297
19388
|
*/
|
|
17298
|
-
|
|
17299
|
-
|
|
17300
|
-
|
|
17301
|
-
|
|
17302
|
-
|
|
17303
|
-
|
|
17304
|
-
const finalResult = [];
|
|
17305
|
-
|
|
17306
|
-
// Complete task
|
|
17307
|
-
let continueLoop = true;
|
|
17308
|
-
|
|
17309
|
-
/**
|
|
17310
|
-
* @param {string} mimeType
|
|
17311
|
-
* @param {ClipboardItem} item
|
|
17312
|
-
*/
|
|
17313
|
-
const completeTask = async (mimeType, item) => {
|
|
17314
|
-
if (!continueLoop) return;
|
|
17315
|
-
// Custom
|
|
17316
|
-
if (
|
|
17317
|
-
(type === null || type === 'custom') &&
|
|
17318
|
-
typeof mimeFormat === 'string' &&
|
|
17319
|
-
((!fixValue && mimeType.startsWith(mimeFormat)) ||
|
|
17320
|
-
(fixValue && mimeType === mimeFormat))
|
|
17321
|
-
) {
|
|
17322
|
-
continueLoop = false;
|
|
17323
|
-
const result = await this._handleBlob(mimeType, item);
|
|
17324
|
-
if (result) finalResult.push(result);
|
|
17325
|
-
}
|
|
17326
|
-
|
|
17327
|
-
// Text
|
|
17328
|
-
else if ((type === null || type === 'text') && mimeType === 'text/plain') {
|
|
17329
|
-
continueLoop = false;
|
|
17330
|
-
const result = await this._handleText(mimeType, item);
|
|
17331
|
-
if (result) finalResult.push(result);
|
|
17332
|
-
}
|
|
17333
|
-
|
|
17334
|
-
// Blob
|
|
17335
|
-
else if (type === null) {
|
|
17336
|
-
continueLoop = false;
|
|
17337
|
-
const result = await this._handleBlob(mimeType, item);
|
|
17338
|
-
if (result) finalResult.push(result);
|
|
17339
|
-
}
|
|
17340
|
-
};
|
|
19389
|
+
setRegExp(name, data) {
|
|
19390
|
+
if (!(data instanceof RegExp)) throw new Error('Value must be a RegExp.');
|
|
19391
|
+
const encoded = this.#setJson(name, data);
|
|
19392
|
+
this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
19393
|
+
this.emit('setRegExp', name, data);
|
|
19394
|
+
}
|
|
17341
19395
|
|
|
17342
|
-
|
|
17343
|
-
|
|
19396
|
+
/**
|
|
19397
|
+
* Retrieves a RegExp from localStorage.
|
|
19398
|
+
* @param {string} name
|
|
19399
|
+
* @returns {RegExp|null}
|
|
19400
|
+
*/
|
|
19401
|
+
getRegExp(name) {
|
|
19402
|
+
const value = this.#getJson(name).decoded;
|
|
19403
|
+
return value instanceof RegExp ? value : null;
|
|
19404
|
+
}
|
|
17344
19405
|
|
|
17345
|
-
|
|
17346
|
-
|
|
17347
|
-
|
|
17348
|
-
|
|
17349
|
-
|
|
17350
|
-
|
|
17351
|
-
|
|
17352
|
-
|
|
17353
|
-
|
|
19406
|
+
/**
|
|
19407
|
+
* Stores a BigInt in localStorage.
|
|
19408
|
+
* @param {string} name
|
|
19409
|
+
* @param {bigint} data
|
|
19410
|
+
*/
|
|
19411
|
+
setBigInt(name, data) {
|
|
19412
|
+
if (typeof data !== 'bigint') throw new Error('Value must be a BigInt.');
|
|
19413
|
+
const encoded = this.#setJson(name, data);
|
|
19414
|
+
this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
19415
|
+
this.emit('setBigInt', name, data);
|
|
19416
|
+
}
|
|
17354
19417
|
|
|
17355
|
-
|
|
17356
|
-
|
|
17357
|
-
|
|
17358
|
-
|
|
17359
|
-
|
|
17360
|
-
|
|
17361
|
-
|
|
17362
|
-
|
|
17363
|
-
|
|
17364
|
-
.then(() => {
|
|
17365
|
-
if (finalResult[0]) resolve(finalResult[0]);
|
|
17366
|
-
else resolve(null);
|
|
17367
|
-
})
|
|
17368
|
-
.catch(reject);
|
|
17369
|
-
}
|
|
19418
|
+
/**
|
|
19419
|
+
* Retrieves a BigInt from localStorage.
|
|
19420
|
+
* @param {string} name
|
|
19421
|
+
* @returns {bigint|null}
|
|
19422
|
+
*/
|
|
19423
|
+
getBigInt(name) {
|
|
19424
|
+
const value = this.#getJson(name).decoded;
|
|
19425
|
+
return typeof value === 'bigint' ? value : null;
|
|
19426
|
+
}
|
|
17370
19427
|
|
|
17371
|
-
|
|
17372
|
-
|
|
17373
|
-
|
|
17374
|
-
|
|
17375
|
-
|
|
17376
|
-
|
|
17377
|
-
|
|
17378
|
-
|
|
17379
|
-
|
|
17380
|
-
|
|
17381
|
-
|
|
19428
|
+
/**
|
|
19429
|
+
* Stores a Symbol in localStorage.
|
|
19430
|
+
* Only global symbols (`Symbol.for`) will preserve the key.
|
|
19431
|
+
* @param {string} name
|
|
19432
|
+
* @param {symbol} data
|
|
19433
|
+
*/
|
|
19434
|
+
setSymbol(name, data) {
|
|
19435
|
+
if (typeof data !== 'symbol') throw new Error('Value must be a Symbol.');
|
|
19436
|
+
const encoded = this.#setJson(name, data);
|
|
19437
|
+
this.#localStorage.setItem(name, JSON.stringify(encoded));
|
|
19438
|
+
this.emit('setSymbol', name, data);
|
|
17382
19439
|
}
|
|
17383
19440
|
|
|
17384
19441
|
/**
|
|
17385
|
-
*
|
|
17386
|
-
*
|
|
17387
|
-
* @
|
|
17388
|
-
* @returns {Promise<string|null>} A promise that resolves to the clipboard text or null.
|
|
19442
|
+
* Retrieves a Symbol from localStorage.
|
|
19443
|
+
* @param {string} name
|
|
19444
|
+
* @returns {symbol|null}
|
|
17389
19445
|
*/
|
|
17390
|
-
|
|
17391
|
-
const value =
|
|
17392
|
-
|
|
17393
|
-
return value;
|
|
19446
|
+
getSymbol(name) {
|
|
19447
|
+
const value = this.#getJson(name).decoded;
|
|
19448
|
+
return typeof value === 'symbol' ? value : null;
|
|
17394
19449
|
}
|
|
17395
19450
|
|
|
17396
19451
|
/**
|
|
17397
|
-
*
|
|
19452
|
+
* Retrieves a value from `localStorage`.
|
|
17398
19453
|
*
|
|
17399
|
-
* @param {string
|
|
17400
|
-
* @
|
|
17401
|
-
* @param {number} [index=0] - Clipboard item index.
|
|
17402
|
-
* @returns {Promise<Blob|null>} A promise resolving with a blob or null.
|
|
19454
|
+
* @param {string} name - The key to retrieve.
|
|
19455
|
+
* @returns {any} The stored value or null if not found.
|
|
17403
19456
|
*/
|
|
17404
|
-
|
|
17405
|
-
|
|
17406
|
-
if (!(value instanceof Blob)) throw new Error('Failed to read custom data: expected Blob.');
|
|
17407
|
-
return value;
|
|
19457
|
+
getValue(name) {
|
|
19458
|
+
return this.#getJson(name).decoded ?? null;
|
|
17408
19459
|
}
|
|
17409
19460
|
|
|
17410
19461
|
/**
|
|
17411
|
-
*
|
|
19462
|
+
* Stores a raw string value in `localStorage`.
|
|
17412
19463
|
*
|
|
17413
|
-
* @
|
|
19464
|
+
* @param {string} name - The key to use.
|
|
19465
|
+
* @param {any} data - The data to store.
|
|
17414
19466
|
*/
|
|
17415
|
-
|
|
17416
|
-
|
|
17417
|
-
|
|
17418
|
-
|
|
17419
|
-
|
|
17420
|
-
throw new Error('Some values returned were not strings.');
|
|
17421
|
-
return values;
|
|
19467
|
+
setItem(name, data) {
|
|
19468
|
+
if (typeof name !== 'string' || !name.length)
|
|
19469
|
+
throw new Error('Key must be a non-empty string.');
|
|
19470
|
+
this.emit('setItem', name, data);
|
|
19471
|
+
return this.#localStorage.setItem(name, data);
|
|
17422
19472
|
}
|
|
17423
19473
|
|
|
17424
19474
|
/**
|
|
17425
|
-
*
|
|
19475
|
+
* Retrieves a raw string value from `localStorage`.
|
|
17426
19476
|
*
|
|
17427
|
-
* @param {string
|
|
17428
|
-
* @
|
|
17429
|
-
* @returns {Promise<Blob[]>} A promise resolving with array of Blobs or null.
|
|
19477
|
+
* @param {string} name - The key to retrieve.
|
|
19478
|
+
* @returns {string|null} The stored value or null if not found.
|
|
17430
19479
|
*/
|
|
17431
|
-
|
|
17432
|
-
|
|
17433
|
-
|
|
17434
|
-
|
|
17435
|
-
if (!values.every((value) => value instanceof Blob))
|
|
17436
|
-
throw new Error('Some values returned were not Blob instances.');
|
|
17437
|
-
return values;
|
|
19480
|
+
getItem(name) {
|
|
19481
|
+
if (typeof name !== 'string' || !name.length)
|
|
19482
|
+
throw new Error('Key must be a non-empty string.');
|
|
19483
|
+
return this.#localStorage.getItem(name);
|
|
17438
19484
|
}
|
|
17439
19485
|
|
|
17440
19486
|
/**
|
|
17441
|
-
*
|
|
19487
|
+
* Stores a string in `localStorage`, ensuring the data is a valid string.
|
|
17442
19488
|
*
|
|
17443
|
-
* @param {
|
|
17444
|
-
* @param {string
|
|
17445
|
-
* @returns {Promise<Array<Blob|string>>} A promise resolving with matching data array.
|
|
19489
|
+
* @param {string} name - The key to store the string under.
|
|
19490
|
+
* @param {string} data - The string to store.
|
|
17446
19491
|
*/
|
|
17447
|
-
|
|
17448
|
-
|
|
17449
|
-
|
|
17450
|
-
|
|
19492
|
+
setString(name, data) {
|
|
19493
|
+
if (typeof name !== 'string' || !name.length)
|
|
19494
|
+
throw new Error('Key must be a non-empty string.');
|
|
19495
|
+
if (typeof data !== 'string') throw new Error('Value must be a string.');
|
|
19496
|
+
|
|
19497
|
+
this.emit('setString', name, data);
|
|
19498
|
+
return this.#localStorage.setItem(name, data);
|
|
17451
19499
|
}
|
|
17452
19500
|
|
|
17453
19501
|
/**
|
|
17454
|
-
*
|
|
19502
|
+
* Retrieves a string value from `localStorage`.
|
|
17455
19503
|
*
|
|
17456
|
-
* @param {
|
|
17457
|
-
* @returns {
|
|
19504
|
+
* @param {string} name - The key to retrieve.
|
|
19505
|
+
* @returns {string|null} The string if valid, or null.
|
|
17458
19506
|
*/
|
|
17459
|
-
|
|
17460
|
-
|
|
17461
|
-
|
|
17462
|
-
|
|
17463
|
-
|
|
17464
|
-
|
|
17465
|
-
// Index is number
|
|
17466
|
-
if (typeof index === 'number') {
|
|
17467
|
-
if (Number.isNaN(index) || !Number.isFinite(index) || index < 0)
|
|
17468
|
-
throw new Error(`Invalid index value: ${index}`);
|
|
17469
|
-
if (items[index]) resolve(items[index]);
|
|
17470
|
-
// Not found
|
|
17471
|
-
else resolve(null);
|
|
17472
|
-
}
|
|
17473
|
-
// Get All
|
|
17474
|
-
resolve(items);
|
|
17475
|
-
})
|
|
17476
|
-
.catch(reject);
|
|
17477
|
-
});
|
|
19507
|
+
getString(name) {
|
|
19508
|
+
if (typeof name !== 'string' || !name.length)
|
|
19509
|
+
throw new Error('Key must be a non-empty string.');
|
|
19510
|
+
let value = this.#localStorage.getItem(name);
|
|
19511
|
+
if (typeof value === 'string') return value;
|
|
19512
|
+
return null;
|
|
17478
19513
|
}
|
|
17479
19514
|
|
|
17480
19515
|
/**
|
|
17481
|
-
*
|
|
19516
|
+
* Stores a number value in `localStorage`.
|
|
17482
19517
|
*
|
|
17483
|
-
* @param {
|
|
17484
|
-
* @
|
|
19518
|
+
* @param {string} name - The key to use.
|
|
19519
|
+
* @param {number} data - The number to store.
|
|
17485
19520
|
*/
|
|
17486
|
-
|
|
17487
|
-
|
|
17488
|
-
|
|
17489
|
-
|
|
17490
|
-
|
|
19521
|
+
setNumber(name, data) {
|
|
19522
|
+
if (typeof name !== 'string' || !name.length)
|
|
19523
|
+
throw new Error('Key must be a non-empty string.');
|
|
19524
|
+
if (typeof data !== 'number') throw new Error('Value must be a number.');
|
|
19525
|
+
this.emit('setNumber', name, data);
|
|
19526
|
+
return this.#localStorage.setItem(name, String(data));
|
|
17491
19527
|
}
|
|
17492
19528
|
|
|
17493
19529
|
/**
|
|
17494
|
-
*
|
|
19530
|
+
* Retrieves a number from `localStorage`.
|
|
17495
19531
|
*
|
|
17496
|
-
* @
|
|
19532
|
+
* @param {string} name - The key to retrieve.
|
|
19533
|
+
* @returns {number|null} The number or null if invalid.
|
|
17497
19534
|
*/
|
|
17498
|
-
|
|
17499
|
-
|
|
17500
|
-
|
|
17501
|
-
|
|
17502
|
-
|
|
17503
|
-
|
|
19535
|
+
getNumber(name) {
|
|
19536
|
+
if (typeof name !== 'string' || !name.length)
|
|
19537
|
+
throw new Error('Key must be a non-empty string.');
|
|
19538
|
+
|
|
19539
|
+
/** @type {number|string|null} */
|
|
19540
|
+
let number = this.#localStorage.getItem(name);
|
|
19541
|
+
if (typeof number === 'number') return number;
|
|
19542
|
+
if (typeof number === 'string' && number.length > 0) {
|
|
19543
|
+
number = parseFloat(number);
|
|
19544
|
+
if (!Number.isNaN(number)) return number;
|
|
17504
19545
|
}
|
|
17505
|
-
return
|
|
19546
|
+
return null;
|
|
17506
19547
|
}
|
|
17507
19548
|
|
|
17508
19549
|
/**
|
|
17509
|
-
*
|
|
17510
|
-
* This can be used to determine if a fallback clipboard method is usable.
|
|
19550
|
+
* Stores a boolean value in `localStorage`.
|
|
17511
19551
|
*
|
|
17512
|
-
* @
|
|
19552
|
+
* @param {string} name - The key to use.
|
|
19553
|
+
* @param {boolean} data - The boolean value to store.
|
|
17513
19554
|
*/
|
|
17514
|
-
|
|
17515
|
-
|
|
19555
|
+
setBool(name, data) {
|
|
19556
|
+
if (typeof name !== 'string' || !name.length)
|
|
19557
|
+
throw new Error('Key must be a non-empty string.');
|
|
19558
|
+
if (typeof data !== 'boolean') throw new Error('Value must be a boolean.');
|
|
19559
|
+
this.emit('setBool', name, data);
|
|
19560
|
+
return this.#localStorage.setItem(name, String(data));
|
|
17516
19561
|
}
|
|
17517
19562
|
|
|
17518
19563
|
/**
|
|
17519
|
-
*
|
|
17520
|
-
* Useful to know if full clipboard features can be accessed.
|
|
19564
|
+
* Retrieves a boolean value from `localStorage`.
|
|
17521
19565
|
*
|
|
17522
|
-
* @
|
|
19566
|
+
* @param {string} name - The key to retrieve.
|
|
19567
|
+
* @returns {boolean|null} The boolean or null if invalid.
|
|
17523
19568
|
*/
|
|
17524
|
-
|
|
17525
|
-
|
|
19569
|
+
getBool(name) {
|
|
19570
|
+
if (typeof name !== 'string' || !name.length)
|
|
19571
|
+
throw new Error('Key must be a non-empty string.');
|
|
19572
|
+
|
|
19573
|
+
const value = this.#localStorage.getItem(name);
|
|
19574
|
+
if (typeof value === 'boolean') return value;
|
|
19575
|
+
if (typeof value === 'string') {
|
|
19576
|
+
if (value === 'true') return true;
|
|
19577
|
+
if (value === 'false') return false;
|
|
19578
|
+
}
|
|
19579
|
+
|
|
19580
|
+
return null;
|
|
17526
19581
|
}
|
|
17527
19582
|
|
|
17528
19583
|
/**
|
|
17529
|
-
*
|
|
17530
|
-
* This function may be built-in or set manually via `setCopyText`.
|
|
19584
|
+
* Removes a value from `localStorage`.
|
|
17531
19585
|
*
|
|
17532
|
-
* @
|
|
19586
|
+
* @param {string} name - The key to remove.
|
|
17533
19587
|
*/
|
|
17534
|
-
|
|
17535
|
-
|
|
19588
|
+
removeItem(name) {
|
|
19589
|
+
if (typeof name !== 'string' || !name.length)
|
|
19590
|
+
throw new Error('Key must be a non-empty string.');
|
|
19591
|
+
|
|
19592
|
+
this.emit('removeItem', name);
|
|
19593
|
+
return this.#localStorage.removeItem(name);
|
|
17536
19594
|
}
|
|
17537
19595
|
|
|
17538
19596
|
/**
|
|
17539
|
-
*
|
|
17540
|
-
* This function may be built-in or set manually via `setCopyBlob`.
|
|
17541
|
-
*
|
|
17542
|
-
* @returns {((blob: Blob) => Promise<void>) | null} The current blob copy function or null if unavailable.
|
|
19597
|
+
* Clears all data from `localStorage`.
|
|
17543
19598
|
*/
|
|
17544
|
-
|
|
17545
|
-
|
|
19599
|
+
clearLocalStorage() {
|
|
19600
|
+
this.#localStorage.clear();
|
|
19601
|
+
}
|
|
19602
|
+
|
|
19603
|
+
/**
|
|
19604
|
+
* Destroys the storage instance by removing the storage event listener.
|
|
19605
|
+
*/
|
|
19606
|
+
destroy() {
|
|
19607
|
+
window.removeEventListener('storage', this.#storageEvent);
|
|
19608
|
+
this.#events.offAllTypes();
|
|
17546
19609
|
}
|
|
17547
19610
|
}
|
|
17548
19611
|
|
|
17549
|
-
|
|
19612
|
+
// First registers
|
|
19613
|
+
|
|
19614
|
+
// Map
|
|
19615
|
+
TinyLocalStorage.registerJsonType(
|
|
19616
|
+
Map,
|
|
19617
|
+
(value, encodeSpecialJson) => ({
|
|
19618
|
+
__map__: true,
|
|
19619
|
+
data: Array.from(value.entries()).map(([k, v]) => [k, encodeSpecialJson(v)]),
|
|
19620
|
+
}),
|
|
19621
|
+
{
|
|
19622
|
+
check: (value) => value.__map__,
|
|
19623
|
+
/** @param {{ data: any[] }} value */
|
|
19624
|
+
decode: (value, decodeSpecialJson) =>
|
|
19625
|
+
new Map(value.data.map(([k, v]) => [k, decodeSpecialJson(v)])),
|
|
19626
|
+
},
|
|
19627
|
+
);
|
|
19628
|
+
|
|
19629
|
+
// Set
|
|
19630
|
+
TinyLocalStorage.registerJsonType(
|
|
19631
|
+
Set,
|
|
19632
|
+
(value, encodeSpecialJson) => ({
|
|
19633
|
+
__set__: true,
|
|
19634
|
+
data: Array.from(value).map(encodeSpecialJson),
|
|
19635
|
+
}),
|
|
19636
|
+
{
|
|
19637
|
+
check: (value) => value.__set__,
|
|
19638
|
+
decode: (value, decodeSpecialJson) => new Set(value.data.map(decodeSpecialJson)),
|
|
19639
|
+
},
|
|
19640
|
+
);
|
|
19641
|
+
|
|
19642
|
+
// Date
|
|
19643
|
+
TinyLocalStorage.registerJsonType(
|
|
19644
|
+
Date,
|
|
19645
|
+
(value) => ({
|
|
19646
|
+
__date__: true,
|
|
19647
|
+
value: value.toISOString(),
|
|
19648
|
+
}),
|
|
19649
|
+
{
|
|
19650
|
+
check: (value) => value.__date__,
|
|
19651
|
+
decode: (value) => new Date(value.value),
|
|
19652
|
+
},
|
|
19653
|
+
);
|
|
19654
|
+
|
|
19655
|
+
// Regex
|
|
19656
|
+
TinyLocalStorage.registerJsonType(
|
|
19657
|
+
RegExp,
|
|
19658
|
+
(value) => ({
|
|
19659
|
+
__regexp__: true,
|
|
19660
|
+
source: value.source,
|
|
19661
|
+
flags: value.flags,
|
|
19662
|
+
}),
|
|
19663
|
+
{
|
|
19664
|
+
check: (value) => value.__regexp__,
|
|
19665
|
+
decode: (value) => new RegExp(value.source, value.flags),
|
|
19666
|
+
},
|
|
19667
|
+
);
|
|
19668
|
+
|
|
19669
|
+
// Big Int
|
|
19670
|
+
TinyLocalStorage.registerJsonType(
|
|
19671
|
+
'bigint',
|
|
19672
|
+
(value) => ({
|
|
19673
|
+
__bigint__: true,
|
|
19674
|
+
value: value.toString(),
|
|
19675
|
+
}),
|
|
19676
|
+
{
|
|
19677
|
+
check: (value) => value.__bigint__,
|
|
19678
|
+
decode: (value) => BigInt(value.value),
|
|
19679
|
+
},
|
|
19680
|
+
);
|
|
19681
|
+
|
|
19682
|
+
// Symbol
|
|
19683
|
+
TinyLocalStorage.registerJsonType(
|
|
19684
|
+
'symbol',
|
|
19685
|
+
(value) => ({
|
|
19686
|
+
__symbol__: true,
|
|
19687
|
+
key: Symbol.keyFor(value) ?? value.description ?? null,
|
|
19688
|
+
}),
|
|
19689
|
+
{
|
|
19690
|
+
check: (value) => value.__symbol__,
|
|
19691
|
+
decode: (value) => {
|
|
19692
|
+
const key = value.key;
|
|
19693
|
+
return key != null ? Symbol.for(key) : Symbol();
|
|
19694
|
+
},
|
|
19695
|
+
},
|
|
19696
|
+
);
|
|
19697
|
+
|
|
19698
|
+
/* harmony default export */ const libs_TinyLocalStorage = (TinyLocalStorage);
|
|
17550
19699
|
|
|
17551
19700
|
;// ./src/v1/index.mjs
|
|
17552
19701
|
|
|
@@ -17580,6 +19729,10 @@ class TinyClipboard {
|
|
|
17580
19729
|
|
|
17581
19730
|
|
|
17582
19731
|
|
|
19732
|
+
|
|
19733
|
+
|
|
19734
|
+
|
|
19735
|
+
|
|
17583
19736
|
|
|
17584
19737
|
|
|
17585
19738
|
|