tiny-essentials 1.17.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/v1/TinyBasicsEs.js +336 -45
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyClipboard.js +459 -0
- package/dist/v1/TinyClipboard.min.js +1 -0
- package/dist/v1/TinyDragger.js +336 -45
- package/dist/v1/TinyDragger.min.js +1 -1
- package/dist/v1/TinyEssentials.js +1218 -45
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyHtml.js +336 -45
- package/dist/v1/TinyHtml.min.js +1 -1
- package/dist/v1/TinySmartScroller.js +336 -45
- package/dist/v1/TinySmartScroller.min.js +1 -1
- package/dist/v1/TinyTextRangeEditor.js +497 -0
- package/dist/v1/TinyTextRangeEditor.min.js +1 -0
- package/dist/v1/TinyUploadClicker.js +338 -45
- package/dist/v1/TinyUploadClicker.min.js +1 -1
- package/dist/v1/build/TinyClipboard.cjs +7 -0
- package/dist/v1/build/TinyClipboard.d.mts +3 -0
- package/dist/v1/build/TinyClipboard.mjs +2 -0
- package/dist/v1/build/TinyTextRangeEditor.cjs +7 -0
- package/dist/v1/build/TinyTextRangeEditor.d.mts +3 -0
- package/dist/v1/build/TinyTextRangeEditor.mjs +2 -0
- package/dist/v1/index.cjs +4 -0
- package/dist/v1/index.d.mts +3 -1
- package/dist/v1/index.mjs +3 -1
- package/dist/v1/libs/TinyClipboard.cjs +420 -0
- package/dist/v1/libs/TinyClipboard.d.mts +155 -0
- package/dist/v1/libs/TinyClipboard.mjs +398 -0
- package/dist/v1/libs/TinyHtml.cjs +336 -45
- package/dist/v1/libs/TinyHtml.d.mts +238 -27
- package/dist/v1/libs/TinyHtml.mjs +320 -47
- package/dist/v1/libs/TinyTextRangeEditor.cjs +458 -0
- package/dist/v1/libs/TinyTextRangeEditor.d.mts +200 -0
- package/dist/v1/libs/TinyTextRangeEditor.mjs +424 -0
- package/docs/v1/README.md +2 -0
- package/docs/v1/libs/TinyClipboard.md +213 -0
- package/docs/v1/libs/TinyHtml.md +211 -15
- package/docs/v1/libs/TinyTextRangeEditor.md +208 -0
- package/package.json +1 -1
|
@@ -2887,6 +2887,7 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
2887
2887
|
__webpack_require__.d(__webpack_exports__, {
|
|
2888
2888
|
ColorSafeStringify: () => (/* reexport */ libs_ColorSafeStringify),
|
|
2889
2889
|
TinyAfterScrollWatcher: () => (/* reexport */ libs_TinyAfterScrollWatcher),
|
|
2890
|
+
TinyClipboard: () => (/* reexport */ libs_TinyClipboard),
|
|
2890
2891
|
TinyDomReadyManager: () => (/* reexport */ libs_TinyDomReadyManager),
|
|
2891
2892
|
TinyDragDropDetector: () => (/* reexport */ libs_TinyDragDropDetector),
|
|
2892
2893
|
TinyDragger: () => (/* reexport */ libs_TinyDragger),
|
|
@@ -2897,6 +2898,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
2897
2898
|
TinyPromiseQueue: () => (/* reexport */ libs_TinyPromiseQueue),
|
|
2898
2899
|
TinyRateLimiter: () => (/* reexport */ libs_TinyRateLimiter),
|
|
2899
2900
|
TinySmartScroller: () => (/* reexport */ libs_TinySmartScroller),
|
|
2901
|
+
TinyTextRangeEditor: () => (/* reexport */ libs_TinyTextRangeEditor),
|
|
2900
2902
|
TinyToastNotify: () => (/* reexport */ libs_TinyToastNotify),
|
|
2901
2903
|
UltraRandomMsgGen: () => (/* reexport */ libs_UltraRandomMsgGen),
|
|
2902
2904
|
addAiMarkerShortcut: () => (/* reexport */ addAiMarkerShortcut),
|
|
@@ -7041,6 +7043,19 @@ const {
|
|
|
7041
7043
|
areElsCollRight: TinyHtml_areElsCollRight,
|
|
7042
7044
|
} = collision_namespaceObject;
|
|
7043
7045
|
|
|
7046
|
+
/**
|
|
7047
|
+
* Callback invoked on each animation frame with the current scroll position,
|
|
7048
|
+
* normalized animation time (`0` to `1`), and a completion flag.
|
|
7049
|
+
*
|
|
7050
|
+
* @typedef {(progress: { x: number, y: number, isComplete: boolean, time: number }) => void} OnScrollAnimation
|
|
7051
|
+
*/
|
|
7052
|
+
|
|
7053
|
+
/**
|
|
7054
|
+
* A list of supported easing function names for smooth animations.
|
|
7055
|
+
*
|
|
7056
|
+
* @typedef {'linear' | 'easeInQuad' | 'easeOutQuad' | 'easeInOutQuad' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic'} Easings
|
|
7057
|
+
*/
|
|
7058
|
+
|
|
7044
7059
|
/**
|
|
7045
7060
|
* Represents a raw Node element or an instance of TinyHtml.
|
|
7046
7061
|
* This type is used to abstract interactions with both plain elements
|
|
@@ -7111,6 +7126,21 @@ const {
|
|
|
7111
7126
|
* @typedef {Element|Window|Document} ElementAndWinAndDoc
|
|
7112
7127
|
*/
|
|
7113
7128
|
|
|
7129
|
+
/**
|
|
7130
|
+
* Represents a raw DOM element with document or an instance of TinyHtml.
|
|
7131
|
+
* This type is used to abstract interactions with both plain elements
|
|
7132
|
+
* and wrapped elements via the TinyHtml class.
|
|
7133
|
+
*
|
|
7134
|
+
* @typedef {ElementWithDoc|TinyHtml} TinyElementWithDoc
|
|
7135
|
+
*/
|
|
7136
|
+
|
|
7137
|
+
/**
|
|
7138
|
+
* Represents a value that can be either a DOM Element, or the document object.
|
|
7139
|
+
* Useful for functions that operate generically on measurable targets.
|
|
7140
|
+
*
|
|
7141
|
+
* @typedef {Element|Document} ElementWithDoc
|
|
7142
|
+
*/
|
|
7143
|
+
|
|
7114
7144
|
/**
|
|
7115
7145
|
* A parameter type used for filtering or matching elements.
|
|
7116
7146
|
* It can be:
|
|
@@ -7130,12 +7160,6 @@ const {
|
|
|
7130
7160
|
* @typedef {Window|Element|Document|Text} ConstructorElValues
|
|
7131
7161
|
*/
|
|
7132
7162
|
|
|
7133
|
-
/**
|
|
7134
|
-
* The handler function used in event listeners.
|
|
7135
|
-
*
|
|
7136
|
-
* @typedef {(e: Event) => any} EventRegistryHandle
|
|
7137
|
-
*/
|
|
7138
|
-
|
|
7139
7163
|
/**
|
|
7140
7164
|
* Options passed to `addEventListener` or `removeEventListener`.
|
|
7141
7165
|
* Can be a boolean or an object of type `AddEventListenerOptions`.
|
|
@@ -7147,7 +7171,7 @@ const {
|
|
|
7147
7171
|
* Structure describing a registered event callback and its options.
|
|
7148
7172
|
*
|
|
7149
7173
|
* @typedef {Object} EventRegistryItem
|
|
7150
|
-
* @property {
|
|
7174
|
+
* @property {EventListenerOrEventListenerObject|null} handler - The function to be executed when the event is triggered.
|
|
7151
7175
|
* @property {EventRegistryOptions} [options] - Optional configuration passed to the listener.
|
|
7152
7176
|
*/
|
|
7153
7177
|
|
|
@@ -7706,9 +7730,9 @@ class TinyHtml_TinyHtml {
|
|
|
7706
7730
|
* Ensures the input is returned as an array.
|
|
7707
7731
|
* Useful to normalize operations across multiple or single element/window/document elements.
|
|
7708
7732
|
*
|
|
7709
|
-
* @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/window element or array of html elements.
|
|
7733
|
+
* @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/document/window element or array of html elements.
|
|
7710
7734
|
* @param {string} where - The method or context name where validation is being called.
|
|
7711
|
-
* @returns {ElementAndWindow[]} - Always returns an array of element/window elements.
|
|
7735
|
+
* @returns {ElementAndWindow[]} - Always returns an array of element/document/window elements.
|
|
7712
7736
|
* @readonly
|
|
7713
7737
|
*/
|
|
7714
7738
|
static _preElemsAndWinAndDoc(elems, where) {
|
|
@@ -7725,9 +7749,9 @@ class TinyHtml_TinyHtml {
|
|
|
7725
7749
|
* Ensures the input is returned as an single element/window/document element.
|
|
7726
7750
|
* Useful to normalize operations across multiple or single element/window/document elements.
|
|
7727
7751
|
*
|
|
7728
|
-
* @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/window element or array of html elements.
|
|
7752
|
+
* @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/document/window element or array of html elements.
|
|
7729
7753
|
* @param {string} where - The method or context name where validation is being called.
|
|
7730
|
-
* @returns {ElementAndWindow} - Always returns an single element/window element.
|
|
7754
|
+
* @returns {ElementAndWindow} - Always returns an single element/document/window element.
|
|
7731
7755
|
* @readonly
|
|
7732
7756
|
*/
|
|
7733
7757
|
static _preElemAndWinAndDoc(elems, where) {
|
|
@@ -7741,6 +7765,32 @@ class TinyHtml_TinyHtml {
|
|
|
7741
7765
|
return result;
|
|
7742
7766
|
}
|
|
7743
7767
|
|
|
7768
|
+
/**
|
|
7769
|
+
* Ensures the input is returned as an array.
|
|
7770
|
+
* Useful to normalize operations across multiple or single element with document elements.
|
|
7771
|
+
*
|
|
7772
|
+
* @param {TinyElementWithDoc|TinyElementWithDoc[]} elems - A single element with document element or array of html elements.
|
|
7773
|
+
* @param {string} where - The method or context name where validation is being called.
|
|
7774
|
+
* @returns {ElementWithDoc[]} - Always returns an array of element with document elements.
|
|
7775
|
+
* @readonly
|
|
7776
|
+
*/
|
|
7777
|
+
static _preElemsWithDoc(elems, where) {
|
|
7778
|
+
return TinyHtml_TinyHtml._preElemsTemplate(elems, where, [Element, Document], ['Element', 'Document']);
|
|
7779
|
+
}
|
|
7780
|
+
|
|
7781
|
+
/**
|
|
7782
|
+
* Ensures the input is returned as an single element with document element.
|
|
7783
|
+
* Useful to normalize operations across multiple or single element with document elements.
|
|
7784
|
+
*
|
|
7785
|
+
* @param {TinyElementWithDoc|TinyElementWithDoc[]} elems - A single element/window element or array of html elements.
|
|
7786
|
+
* @param {string} where - The method or context name where validation is being called.
|
|
7787
|
+
* @returns {ElementWithDoc} - Always returns an single element/window element.
|
|
7788
|
+
* @readonly
|
|
7789
|
+
*/
|
|
7790
|
+
static _preElemWithDoc(elems, where) {
|
|
7791
|
+
return TinyHtml_TinyHtml._preElemTemplate(elems, where, [Element, Document], ['Element', 'Document']);
|
|
7792
|
+
}
|
|
7793
|
+
|
|
7744
7794
|
/**
|
|
7745
7795
|
* Normalizes and converts one or more DOM elements (or TinyHtml instances)
|
|
7746
7796
|
* into an array of `TinyHtml` instances.
|
|
@@ -9449,7 +9499,7 @@ class TinyHtml_TinyHtml {
|
|
|
9449
9499
|
*/
|
|
9450
9500
|
static setWinScrollTop(value) {
|
|
9451
9501
|
if (typeof value !== 'number') throw new TypeError('The value must be a number.');
|
|
9452
|
-
|
|
9502
|
+
TinyHtml_TinyHtml.setScrollTop(window, value);
|
|
9453
9503
|
}
|
|
9454
9504
|
|
|
9455
9505
|
/**
|
|
@@ -9458,7 +9508,7 @@ class TinyHtml_TinyHtml {
|
|
|
9458
9508
|
*/
|
|
9459
9509
|
static setWinScrollLeft(value) {
|
|
9460
9510
|
if (typeof value !== 'number') throw new TypeError('The value must be a number.');
|
|
9461
|
-
|
|
9511
|
+
TinyHtml_TinyHtml.setScrollLeft(window, value);
|
|
9462
9512
|
}
|
|
9463
9513
|
|
|
9464
9514
|
/**
|
|
@@ -9775,6 +9825,30 @@ class TinyHtml_TinyHtml {
|
|
|
9775
9825
|
|
|
9776
9826
|
//////////////////////////////////////////////////
|
|
9777
9827
|
|
|
9828
|
+
/**
|
|
9829
|
+
* Applies an animation to one or multiple TinyElement instances.
|
|
9830
|
+
*
|
|
9831
|
+
* @param {TinyElement|TinyElement[]} el - A single TinyElement or an array of TinyElements to animate.
|
|
9832
|
+
* @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes - The keyframes used to define the animation.
|
|
9833
|
+
* @param {number | KeyframeAnimationOptions} [ops] - Timing or configuration options for the animation.
|
|
9834
|
+
* @returns {TinyElement|TinyElement[]}
|
|
9835
|
+
*/
|
|
9836
|
+
static animate(el, keyframes, ops) {
|
|
9837
|
+
TinyHtml_TinyHtml._preElems(el, 'animate').forEach((elem) => elem.animate(keyframes, ops));
|
|
9838
|
+
return el;
|
|
9839
|
+
}
|
|
9840
|
+
|
|
9841
|
+
/**
|
|
9842
|
+
* Applies an animation to one or multiple TinyElement instances.
|
|
9843
|
+
*
|
|
9844
|
+
* @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes - The keyframes used to define the animation.
|
|
9845
|
+
* @param {number | KeyframeAnimationOptions} [ops] - Timing or configuration options for the animation.
|
|
9846
|
+
* @returns {TinyElement|TinyElement[]}
|
|
9847
|
+
*/
|
|
9848
|
+
animate(keyframes, ops) {
|
|
9849
|
+
return TinyHtml_TinyHtml.animate(this, keyframes, ops);
|
|
9850
|
+
}
|
|
9851
|
+
|
|
9778
9852
|
/**
|
|
9779
9853
|
* Gets the offset of the element relative to the document.
|
|
9780
9854
|
* @param {TinyElement} el - Target element.
|
|
@@ -9930,26 +10004,147 @@ class TinyHtml_TinyHtml {
|
|
|
9930
10004
|
}
|
|
9931
10005
|
|
|
9932
10006
|
/**
|
|
9933
|
-
*
|
|
9934
|
-
*
|
|
9935
|
-
*
|
|
9936
|
-
* @
|
|
10007
|
+
* Collection of easing functions used for scroll and animation calculations.
|
|
10008
|
+
* Each function receives a normalized time value (`t` from 0 to 1) and returns the eased progress.
|
|
10009
|
+
*
|
|
10010
|
+
* @type {Record<string, (t: number) => number>}
|
|
9937
10011
|
*/
|
|
9938
|
-
static
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
9942
|
-
|
|
10012
|
+
static easings = {
|
|
10013
|
+
linear: (t) => t,
|
|
10014
|
+
easeInQuad: (t) => t * t,
|
|
10015
|
+
easeOutQuad: (t) => t * (2 - t),
|
|
10016
|
+
easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
|
|
10017
|
+
easeInCubic: (t) => t * t * t,
|
|
10018
|
+
easeOutCubic: (t) => --t * t * t + 1,
|
|
10019
|
+
easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),
|
|
10020
|
+
};
|
|
10021
|
+
|
|
10022
|
+
/**
|
|
10023
|
+
* Smoothly scrolls one or more elements (or the window) to the specified X and Y coordinates
|
|
10024
|
+
* using a custom duration and easing function.
|
|
10025
|
+
*
|
|
10026
|
+
* If `duration` or a valid `easing` is not provided, the scroll will be performed immediately.
|
|
10027
|
+
*
|
|
10028
|
+
* @param {TinyElementAndWindow | TinyElementAndWindow[]} el - A single element, array of elements, or the window to scroll.
|
|
10029
|
+
* @param {Object} [settings={}] - Configuration object for the scroll animation.
|
|
10030
|
+
* @param {number} [settings.targetX] - The horizontal scroll target in pixels.
|
|
10031
|
+
* @param {number} [settings.targetY] - The vertical scroll target in pixels.
|
|
10032
|
+
* @param {number} [settings.duration] - The duration of the animation in milliseconds.
|
|
10033
|
+
* @param {Easings} [settings.easing] - The easing function name to use for the scroll animation.
|
|
10034
|
+
* @param {OnScrollAnimation} [settings.onAnimation] - Optional callback invoked on each animation
|
|
10035
|
+
* frame with the current scroll position, normalized animation time (`0` to `1`), and a completion flag.
|
|
10036
|
+
* @returns {TinyElementAndWindow|TinyElementAndWindow[]}
|
|
10037
|
+
* @throws {TypeError} If `el` is not a valid element, array, or window.
|
|
10038
|
+
* @throws {TypeError} If `targetX` or `targetY` is defined but not a number.
|
|
10039
|
+
* @throws {TypeError} If `duration` is defined but not a number.
|
|
10040
|
+
* @throws {TypeError} If `easing` is defined but not a valid easing function name.
|
|
10041
|
+
* @throws {TypeError} If `onAnimation` is defined but not a function.
|
|
10042
|
+
*/
|
|
10043
|
+
static scrollToXY(el, { targetX, targetY, duration, easing, onAnimation } = {}) {
|
|
10044
|
+
if (targetX !== undefined && typeof targetX !== 'number')
|
|
10045
|
+
throw new TypeError('`targetX` must be a number if provided.');
|
|
10046
|
+
if (targetY !== undefined && typeof targetY !== 'number')
|
|
10047
|
+
throw new TypeError('`targetY` must be a number if provided.');
|
|
10048
|
+
if (duration !== undefined && typeof duration !== 'number')
|
|
10049
|
+
throw new TypeError('`duration` must be a number if provided.');
|
|
10050
|
+
if (easing !== undefined && typeof easing !== 'string')
|
|
10051
|
+
throw new TypeError('`easing` must be a string if provided.');
|
|
10052
|
+
if (easing !== undefined && typeof TinyHtml_TinyHtml.easings[easing] !== 'function')
|
|
10053
|
+
throw new TypeError(`Unknown easing function: "${easing}".`);
|
|
10054
|
+
if (onAnimation !== undefined && typeof onAnimation !== 'function')
|
|
10055
|
+
throw new TypeError('`onAnimation` must be a function if provided.');
|
|
10056
|
+
|
|
10057
|
+
/**
|
|
10058
|
+
* Performs an instant scroll to the given coordinates.
|
|
10059
|
+
*
|
|
10060
|
+
* @param {ElementAndWindow} elem - The element or window to scroll.
|
|
10061
|
+
* @param {number} newX - The final horizontal scroll position.
|
|
10062
|
+
* @param {number} newY - The final vertical scroll position.
|
|
10063
|
+
* @param {number} time - Normalized progress value.
|
|
10064
|
+
*/
|
|
10065
|
+
const executeScroll = (elem, newX, newY, time) => {
|
|
10066
|
+
if (elem instanceof Window) {
|
|
10067
|
+
window.scrollTo(newX, newY);
|
|
9943
10068
|
} else if (elem.nodeType === 9) {
|
|
9944
10069
|
// @ts-ignore
|
|
9945
|
-
elem.defaultView.scrollTo(
|
|
10070
|
+
elem.defaultView.scrollTo(newX, newY);
|
|
9946
10071
|
} else {
|
|
9947
|
-
elem.
|
|
10072
|
+
const startX = elem instanceof Window ? window.scrollX : elem.scrollLeft;
|
|
10073
|
+
const startY = elem instanceof Window ? window.scrollY : elem.scrollTop;
|
|
10074
|
+
if (startX !== newX) elem.scrollLeft = newX;
|
|
10075
|
+
if (startY !== newY) elem.scrollTop = newY;
|
|
9948
10076
|
}
|
|
10077
|
+
if (typeof onAnimation === 'function')
|
|
10078
|
+
onAnimation({ x: newX, y: newY, isComplete: time >= 1, time });
|
|
10079
|
+
};
|
|
10080
|
+
|
|
10081
|
+
TinyHtml_TinyHtml._preElemsAndWindow(el, 'scrollToXY').forEach((elem) => {
|
|
10082
|
+
const startX = elem instanceof Window ? window.scrollX : elem.scrollLeft;
|
|
10083
|
+
const startY = elem instanceof Window ? window.scrollY : elem.scrollTop;
|
|
10084
|
+
const targX = targetX ?? startX;
|
|
10085
|
+
const targY = targetY ?? startY;
|
|
10086
|
+
|
|
10087
|
+
const changeX = targX - startX;
|
|
10088
|
+
const changeY = targY - startY;
|
|
10089
|
+
|
|
10090
|
+
const ease = (typeof easing === 'string' && TinyHtml_TinyHtml.easings[easing]) || null;
|
|
10091
|
+
if (typeof duration !== 'number' || typeof ease !== 'function')
|
|
10092
|
+
return executeScroll(elem, targX, targY, 1);
|
|
10093
|
+
const startTime = performance.now();
|
|
10094
|
+
const dur = duration ?? 0;
|
|
10095
|
+
|
|
10096
|
+
/**
|
|
10097
|
+
* Animates the scroll position based on easing and time.
|
|
10098
|
+
*
|
|
10099
|
+
* @param {number} currentTime - Timestamp provided by requestAnimationFrame.
|
|
10100
|
+
*/
|
|
10101
|
+
function animateScroll(currentTime) {
|
|
10102
|
+
if (typeof ease !== 'function') return;
|
|
10103
|
+
const time = Math.min(1, (currentTime - startTime) / dur);
|
|
10104
|
+
const easedTime = ease(time);
|
|
10105
|
+
|
|
10106
|
+
const newX = startX + changeX * easedTime;
|
|
10107
|
+
const newY = startY + changeY * easedTime;
|
|
10108
|
+
executeScroll(elem, newX, newY, time);
|
|
10109
|
+
|
|
10110
|
+
if (time < 1) requestAnimationFrame(animateScroll);
|
|
10111
|
+
}
|
|
10112
|
+
|
|
10113
|
+
requestAnimationFrame(animateScroll);
|
|
9949
10114
|
});
|
|
9950
10115
|
return el;
|
|
9951
10116
|
}
|
|
9952
10117
|
|
|
10118
|
+
/**
|
|
10119
|
+
* Smoothly scrolls one or more elements (or the window) to the specified X and Y coordinates
|
|
10120
|
+
* using a custom duration and easing function.
|
|
10121
|
+
*
|
|
10122
|
+
* If `duration` or a valid `easing` is not provided, the scroll will be performed immediately.
|
|
10123
|
+
*
|
|
10124
|
+
* @param {Object} [settings={}] - Configuration object for the scroll animation.
|
|
10125
|
+
* @param {number} [settings.targetX] - The horizontal scroll target in pixels.
|
|
10126
|
+
* @param {number} [settings.targetY] - The vertical scroll target in pixels.
|
|
10127
|
+
* @param {number} [settings.duration] - The duration of the animation in milliseconds.
|
|
10128
|
+
* @param {Easings} [settings.easing] - The easing function name to use for the scroll animation.
|
|
10129
|
+
* @param {OnScrollAnimation} [settings.onAnimation] - Optional callback invoked on each animation
|
|
10130
|
+
* frame with the current scroll position, normalized animation time (`0` to `1`), and a completion flag.
|
|
10131
|
+
* @returns {TinyElementAndWindow|TinyElementAndWindow[]}
|
|
10132
|
+
*/
|
|
10133
|
+
scrollToXY({ targetX, targetY, duration, easing, onAnimation } = {}) {
|
|
10134
|
+
return TinyHtml_TinyHtml.scrollToXY(this, { targetX, targetY, duration, easing, onAnimation });
|
|
10135
|
+
}
|
|
10136
|
+
|
|
10137
|
+
/**
|
|
10138
|
+
* Sets the vertical scroll position.
|
|
10139
|
+
* @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
|
|
10140
|
+
* @param {number} value - Scroll top value.
|
|
10141
|
+
* @returns {TinyElementAndWindow|TinyElementAndWindow[]}
|
|
10142
|
+
*/
|
|
10143
|
+
static setScrollTop(el, value) {
|
|
10144
|
+
if (typeof value !== 'number') throw new TypeError('ScrollTop value must be a number.');
|
|
10145
|
+
return TinyHtml_TinyHtml.scrollToXY(el, { targetY: value });
|
|
10146
|
+
}
|
|
10147
|
+
|
|
9953
10148
|
/**
|
|
9954
10149
|
* Sets the vertical scroll position.
|
|
9955
10150
|
* @param {number} value - Scroll top value.
|
|
@@ -9967,17 +10162,7 @@ class TinyHtml_TinyHtml {
|
|
|
9967
10162
|
*/
|
|
9968
10163
|
static setScrollLeft(el, value) {
|
|
9969
10164
|
if (typeof value !== 'number') throw new TypeError('ScrollLeft value must be a number.');
|
|
9970
|
-
TinyHtml_TinyHtml.
|
|
9971
|
-
if (TinyHtml_TinyHtml.isWindow(elem)) {
|
|
9972
|
-
elem.scrollTo(value, elem.pageYOffset);
|
|
9973
|
-
} else if (elem.nodeType === 9) {
|
|
9974
|
-
// @ts-ignore
|
|
9975
|
-
elem.defaultView.scrollTo(value, elem.defaultView.pageYOffset);
|
|
9976
|
-
} else {
|
|
9977
|
-
elem.scrollLeft = value;
|
|
9978
|
-
}
|
|
9979
|
-
});
|
|
9980
|
-
return el;
|
|
10165
|
+
return TinyHtml_TinyHtml.scrollToXY(el, { targetX: value });
|
|
9981
10166
|
}
|
|
9982
10167
|
|
|
9983
10168
|
/**
|
|
@@ -10865,12 +11050,120 @@ class TinyHtml_TinyHtml {
|
|
|
10865
11050
|
|
|
10866
11051
|
////////////////////////////////////////////
|
|
10867
11052
|
|
|
11053
|
+
/**
|
|
11054
|
+
* Registers a listener for the "paste" event to extract files and text from the clipboard (e.g., when the user presses Ctrl+V).
|
|
11055
|
+
*
|
|
11056
|
+
* This method allows reacting to pasted content by providing separate callbacks for files and plain text.
|
|
11057
|
+
* Useful for building file upload areas, rich-text editors, or input enhancements.
|
|
11058
|
+
*
|
|
11059
|
+
* @param {TinyElementWithDoc|TinyElementWithDoc[]} el - The target element(s) where the "paste" event will be listened.
|
|
11060
|
+
* @param {Object} [settings={}] - Optional callbacks to handle clipboard content.
|
|
11061
|
+
* @param {(data: DataTransferItem, file: File) => void} [settings.onFilePaste] - Called for each file pasted from the clipboard (e.g., images).
|
|
11062
|
+
* @param {(data: DataTransferItem, text: string) => void} [settings.onTextPaste] - Called when plain text is pasted from the clipboard.
|
|
11063
|
+
* @returns {EventListenerOrEventListenerObject} The internal "paste" event handler used.
|
|
11064
|
+
*/
|
|
11065
|
+
static listenForPaste(el, { onFilePaste, onTextPaste } = {}) {
|
|
11066
|
+
if (typeof onFilePaste !== 'undefined' && typeof onFilePaste !== 'function')
|
|
11067
|
+
throw new TypeError('onFilePaste must be a function.');
|
|
11068
|
+
if (typeof onTextPaste !== 'undefined' && typeof onTextPaste !== 'function')
|
|
11069
|
+
throw new TypeError('onTextPaste must be a function.');
|
|
11070
|
+
|
|
11071
|
+
/** @type {EventListenerOrEventListenerObject} */
|
|
11072
|
+
const pasteEvent = (event) => {
|
|
11073
|
+
if (!(event instanceof ClipboardEvent)) return;
|
|
11074
|
+
const items = event.clipboardData?.items || [];
|
|
11075
|
+
for (const item of items) {
|
|
11076
|
+
if (item.kind === 'file') {
|
|
11077
|
+
if (typeof onFilePaste === 'function') {
|
|
11078
|
+
const file = item.getAsFile();
|
|
11079
|
+
if (file) onFilePaste(item, file);
|
|
11080
|
+
}
|
|
11081
|
+
} else if (item.kind === 'string') {
|
|
11082
|
+
if (typeof onTextPaste === 'function')
|
|
11083
|
+
item.getAsString((text) => onTextPaste(item, text));
|
|
11084
|
+
}
|
|
11085
|
+
}
|
|
11086
|
+
};
|
|
11087
|
+
|
|
11088
|
+
TinyHtml_TinyHtml._preElemsWithDoc(el, 'listenForPaste').forEach((elem) =>
|
|
11089
|
+
TinyHtml_TinyHtml.on(elem, 'paste', pasteEvent),
|
|
11090
|
+
);
|
|
11091
|
+
return pasteEvent;
|
|
11092
|
+
}
|
|
11093
|
+
|
|
11094
|
+
/**
|
|
11095
|
+
* Registers a listener for the "paste" event to extract files and text from the clipboard (e.g., when the user presses Ctrl+V).
|
|
11096
|
+
*
|
|
11097
|
+
* This method allows reacting to pasted content by providing separate callbacks for files and plain text.
|
|
11098
|
+
* Useful for building file upload areas, rich-text editors, or input enhancements.
|
|
11099
|
+
*
|
|
11100
|
+
* @param {Object} [settings={}] - Optional callbacks to handle clipboard content.
|
|
11101
|
+
* @param {(data: DataTransferItem, file: File) => void} [settings.onFilePaste] - Called for each file pasted from the clipboard (e.g., images).
|
|
11102
|
+
* @param {(data: DataTransferItem, text: string) => void} [settings.onTextPaste] - Called when plain text is pasted from the clipboard.
|
|
11103
|
+
* @returns {EventListenerOrEventListenerObject} The internal "paste" event handler used.
|
|
11104
|
+
*/
|
|
11105
|
+
listenForPaste({ onFilePaste, onTextPaste } = {}) {
|
|
11106
|
+
return TinyHtml_TinyHtml.listenForPaste(this, { onFilePaste, onTextPaste });
|
|
11107
|
+
}
|
|
11108
|
+
|
|
11109
|
+
/**
|
|
11110
|
+
* Checks if the element has a listener for a specific event.
|
|
11111
|
+
*
|
|
11112
|
+
* @param {TinyEventTarget} el - The element to check.
|
|
11113
|
+
* @param {string} event - The event name to check.
|
|
11114
|
+
* @returns {boolean}
|
|
11115
|
+
*/
|
|
11116
|
+
static hasEventListener(el, event) {
|
|
11117
|
+
const elem = TinyHtml_TinyHtml._preEventTargetElem(el, 'hasEventListener');
|
|
11118
|
+
if (!__eventRegistry.has(elem)) return false;
|
|
11119
|
+
const events = __eventRegistry.get(elem);
|
|
11120
|
+
return !!(events && Array.isArray(events[event]) && events[event].length > 0);
|
|
11121
|
+
}
|
|
11122
|
+
|
|
11123
|
+
/**
|
|
11124
|
+
* Checks if the element has a listener for a specific event.
|
|
11125
|
+
*
|
|
11126
|
+
* @param {string} event - The event name to check.
|
|
11127
|
+
* @returns {boolean}
|
|
11128
|
+
*/
|
|
11129
|
+
hasEventListener(event) {
|
|
11130
|
+
return TinyHtml_TinyHtml.hasEventListener(this, event);
|
|
11131
|
+
}
|
|
11132
|
+
|
|
11133
|
+
/**
|
|
11134
|
+
* Checks if the element has the exact handler registered for a specific event.
|
|
11135
|
+
*
|
|
11136
|
+
* @param {TinyEventTarget} el - The element to check.
|
|
11137
|
+
* @param {string} event - The event name to check.
|
|
11138
|
+
* @param {EventListenerOrEventListenerObject} handler - The handler function to check.
|
|
11139
|
+
* @returns {boolean}
|
|
11140
|
+
*/
|
|
11141
|
+
static hasExactEventListener(el, event, handler) {
|
|
11142
|
+
const elem = TinyHtml_TinyHtml._preEventTargetElem(el, 'hasExactEventListener');
|
|
11143
|
+
if (typeof handler !== 'function') throw new TypeError('The "handler" must be a function.');
|
|
11144
|
+
if (!__eventRegistry.has(elem)) return false;
|
|
11145
|
+
const events = __eventRegistry.get(elem);
|
|
11146
|
+
if (!events || !Array.isArray(events[event])) return false;
|
|
11147
|
+
return events[event].some((item) => item.handler === handler);
|
|
11148
|
+
}
|
|
11149
|
+
|
|
11150
|
+
/**
|
|
11151
|
+
* Checks if the element has the exact handler registered for a specific event.
|
|
11152
|
+
*
|
|
11153
|
+
* @param {string} event - The event name to check.
|
|
11154
|
+
* @param {EventListenerOrEventListenerObject} handler - The handler function to check.
|
|
11155
|
+
* @returns {boolean}
|
|
11156
|
+
*/
|
|
11157
|
+
hasExactEventListener(event, handler) {
|
|
11158
|
+
return TinyHtml_TinyHtml.hasExactEventListener(this, event, handler);
|
|
11159
|
+
}
|
|
11160
|
+
|
|
10868
11161
|
/**
|
|
10869
11162
|
* Registers an event listener on the specified element.
|
|
10870
11163
|
*
|
|
10871
11164
|
* @param {TinyEventTarget|TinyEventTarget[]} el - The target to listen on.
|
|
10872
11165
|
* @param {string} event - The event type (e.g. 'click', 'keydown').
|
|
10873
|
-
* @param {
|
|
11166
|
+
* @param {EventListenerOrEventListenerObject|null} handler - The callback function to run on event.
|
|
10874
11167
|
* @param {EventRegistryOptions} [options] - Optional event listener options.
|
|
10875
11168
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
10876
11169
|
*/
|
|
@@ -10892,7 +11185,7 @@ class TinyHtml_TinyHtml {
|
|
|
10892
11185
|
* Registers an event listener on the specified element.
|
|
10893
11186
|
*
|
|
10894
11187
|
* @param {string} event - The event type (e.g. 'click', 'keydown').
|
|
10895
|
-
* @param {
|
|
11188
|
+
* @param {EventListenerOrEventListenerObject|null} handler - The callback function to run on event.
|
|
10896
11189
|
* @param {EventRegistryOptions} [options] - Optional event listener options.
|
|
10897
11190
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
10898
11191
|
*/
|
|
@@ -10905,17 +11198,17 @@ class TinyHtml_TinyHtml {
|
|
|
10905
11198
|
*
|
|
10906
11199
|
* @param {TinyEventTarget|TinyEventTarget[]} el - The target to listen on.
|
|
10907
11200
|
* @param {string} event - The event type (e.g. 'click', 'keydown').
|
|
10908
|
-
* @param {
|
|
11201
|
+
* @param {EventListenerOrEventListenerObject} handler - The callback function to run on event.
|
|
10909
11202
|
* @param {EventRegistryOptions} [options={}] - Optional event listener options.
|
|
10910
11203
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
10911
11204
|
*/
|
|
10912
11205
|
static once(el, event, handler, options = {}) {
|
|
10913
11206
|
if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
|
|
10914
11207
|
TinyHtml_TinyHtml._preEventTargetElems(el, 'once').forEach((elem) => {
|
|
10915
|
-
/** @type {
|
|
11208
|
+
/** @type {EventListenerOrEventListenerObject} */
|
|
10916
11209
|
const wrapped = (e) => {
|
|
10917
11210
|
TinyHtml_TinyHtml.off(elem, event, wrapped);
|
|
10918
|
-
handler(e);
|
|
11211
|
+
if (typeof handler === 'function') handler(e);
|
|
10919
11212
|
};
|
|
10920
11213
|
|
|
10921
11214
|
TinyHtml_TinyHtml.on(
|
|
@@ -10932,7 +11225,7 @@ class TinyHtml_TinyHtml {
|
|
|
10932
11225
|
* Registers an event listener that runs only once, then is removed.
|
|
10933
11226
|
*
|
|
10934
11227
|
* @param {string} event - The event type (e.g. 'click', 'keydown').
|
|
10935
|
-
* @param {
|
|
11228
|
+
* @param {EventListenerOrEventListenerObject} handler - The callback function to run on event.
|
|
10936
11229
|
* @param {EventRegistryOptions} [options={}] - Optional event listener options.
|
|
10937
11230
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
10938
11231
|
*/
|
|
@@ -10945,7 +11238,7 @@ class TinyHtml_TinyHtml {
|
|
|
10945
11238
|
*
|
|
10946
11239
|
* @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
|
|
10947
11240
|
* @param {string} event - The event type.
|
|
10948
|
-
* @param {
|
|
11241
|
+
* @param {EventListenerOrEventListenerObject|null} handler - The function originally bound to the event.
|
|
10949
11242
|
* @param {boolean|EventListenerOptions} [options] - Optional listener options.
|
|
10950
11243
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
10951
11244
|
*/
|
|
@@ -10967,7 +11260,7 @@ class TinyHtml_TinyHtml {
|
|
|
10967
11260
|
* Removes a specific event listener from an element.
|
|
10968
11261
|
*
|
|
10969
11262
|
* @param {string} event - The event type.
|
|
10970
|
-
* @param {
|
|
11263
|
+
* @param {EventListenerOrEventListenerObject|null} handler - The function originally bound to the event.
|
|
10971
11264
|
* @param {boolean|EventListenerOptions} [options] - Optional listener options.
|
|
10972
11265
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
10973
11266
|
*/
|
|
@@ -11010,7 +11303,7 @@ class TinyHtml_TinyHtml {
|
|
|
11010
11303
|
* Removes all event listeners of all types from the element.
|
|
11011
11304
|
*
|
|
11012
11305
|
* @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
|
|
11013
|
-
* @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
|
|
11306
|
+
* @param {((handler: EventListenerOrEventListenerObject|null, event: string) => boolean)|null} [filterFn=null] -
|
|
11014
11307
|
* Optional filter function to selectively remove specific handlers.
|
|
11015
11308
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
11016
11309
|
*/
|
|
@@ -11037,7 +11330,7 @@ class TinyHtml_TinyHtml {
|
|
|
11037
11330
|
/**
|
|
11038
11331
|
* Removes all event listeners of all types from the element.
|
|
11039
11332
|
*
|
|
11040
|
-
* @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
|
|
11333
|
+
* @param {((handler: EventListenerOrEventListenerObject|null, event: string) => boolean)|null} [filterFn=null] -
|
|
11041
11334
|
* Optional filter function to selectively remove specific handlers.
|
|
11042
11335
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
11043
11336
|
*/
|
|
@@ -16373,6 +16666,884 @@ class TinySmartScroller {
|
|
|
16373
16666
|
|
|
16374
16667
|
/* harmony default export */ const libs_TinySmartScroller = (TinySmartScroller);
|
|
16375
16668
|
|
|
16669
|
+
;// ./src/v1/libs/TinyTextRangeEditor.mjs
|
|
16670
|
+
/**
|
|
16671
|
+
* A full-featured text range editor for `<input>` and `<textarea>` elements,
|
|
16672
|
+
* including advanced utilities for BBCode or similar tag-based markup editing.
|
|
16673
|
+
*/
|
|
16674
|
+
class TinyTextRangeEditor {
|
|
16675
|
+
/** @type {HTMLInputElement | HTMLTextAreaElement} */
|
|
16676
|
+
#el;
|
|
16677
|
+
|
|
16678
|
+
/** @type {string} */
|
|
16679
|
+
#openTag;
|
|
16680
|
+
|
|
16681
|
+
/** @type {string} */
|
|
16682
|
+
#closeTag;
|
|
16683
|
+
|
|
16684
|
+
/**
|
|
16685
|
+
* @param {HTMLInputElement | HTMLTextAreaElement} elem - The target editable input or textarea element.
|
|
16686
|
+
* @param {Object} [settings={}] - Optional tag symbol customization.
|
|
16687
|
+
* @param {string} [settings.openTag='['] - The character or symbol used to start a tag (e.g., `'['`).
|
|
16688
|
+
* @param {string} [settings.closeTag=']'] - The character or symbol used to end a tag (e.g., `']'`).
|
|
16689
|
+
*/
|
|
16690
|
+
constructor(elem, { openTag = '[', closeTag = ']' } = {}) {
|
|
16691
|
+
if (!(elem instanceof HTMLInputElement || elem instanceof HTMLTextAreaElement))
|
|
16692
|
+
throw new TypeError('Element must be an input or textarea.');
|
|
16693
|
+
if (typeof openTag !== 'string') throw new TypeError('openTag must be a string.');
|
|
16694
|
+
if (typeof closeTag !== 'string') throw new TypeError('closeTag must be a string.');
|
|
16695
|
+
this.#el = elem;
|
|
16696
|
+
this.#openTag = openTag;
|
|
16697
|
+
this.#closeTag = closeTag;
|
|
16698
|
+
}
|
|
16699
|
+
|
|
16700
|
+
/** @returns {string} The current open tag symbol. */
|
|
16701
|
+
getOpenTag() {
|
|
16702
|
+
return this.#openTag;
|
|
16703
|
+
}
|
|
16704
|
+
|
|
16705
|
+
/** @returns {string} The current close tag symbol. */
|
|
16706
|
+
getCloseTag() {
|
|
16707
|
+
return this.#closeTag;
|
|
16708
|
+
}
|
|
16709
|
+
|
|
16710
|
+
/** @param {string} tag - New open tag symbol to use (e.g., `'['`). */
|
|
16711
|
+
setOpenTag(tag) {
|
|
16712
|
+
if (typeof tag !== 'string') throw new TypeError('Open tag must be a string.');
|
|
16713
|
+
this.#openTag = tag;
|
|
16714
|
+
}
|
|
16715
|
+
|
|
16716
|
+
/** @param {string} tag - New close tag symbol to use (e.g., `']'`). */
|
|
16717
|
+
setCloseTag(tag) {
|
|
16718
|
+
if (typeof tag !== 'string') throw new TypeError('Close tag must be a string.');
|
|
16719
|
+
this.#closeTag = tag;
|
|
16720
|
+
}
|
|
16721
|
+
|
|
16722
|
+
/**
|
|
16723
|
+
* Ensures the element has focus.
|
|
16724
|
+
* @returns {TinyTextRangeEditor}
|
|
16725
|
+
*/
|
|
16726
|
+
ensureFocus() {
|
|
16727
|
+
if (document.activeElement !== this.#el) this.#el.focus();
|
|
16728
|
+
return this;
|
|
16729
|
+
}
|
|
16730
|
+
|
|
16731
|
+
/**
|
|
16732
|
+
* Focus the element.
|
|
16733
|
+
* @returns {TinyTextRangeEditor}
|
|
16734
|
+
*/
|
|
16735
|
+
focus() {
|
|
16736
|
+
this.#el.focus();
|
|
16737
|
+
return this;
|
|
16738
|
+
}
|
|
16739
|
+
|
|
16740
|
+
/** @returns {{ start: number, end: number }} The current selection range. */
|
|
16741
|
+
getSelectionRange() {
|
|
16742
|
+
return {
|
|
16743
|
+
start: this.#el.selectionStart ?? NaN,
|
|
16744
|
+
end: this.#el.selectionEnd ?? NaN,
|
|
16745
|
+
};
|
|
16746
|
+
}
|
|
16747
|
+
|
|
16748
|
+
/**
|
|
16749
|
+
* Sets the current selection range.
|
|
16750
|
+
* @param {number} start - Start index.
|
|
16751
|
+
* @param {number} end - End index.
|
|
16752
|
+
* @param {boolean} [preserveScroll=true] - Whether to preserve scroll position.
|
|
16753
|
+
* @returns {TinyTextRangeEditor}
|
|
16754
|
+
*/
|
|
16755
|
+
setSelectionRange(start, end, preserveScroll = true) {
|
|
16756
|
+
if (typeof start !== 'number' || typeof end !== 'number')
|
|
16757
|
+
throw new TypeError('start and end must be numbers.');
|
|
16758
|
+
if (typeof preserveScroll !== 'boolean')
|
|
16759
|
+
throw new TypeError('preserveScroll must be a boolean.');
|
|
16760
|
+
const scrollTop = this.#el.scrollTop;
|
|
16761
|
+
const scrollLeft = this.#el.scrollLeft;
|
|
16762
|
+
this.#el.setSelectionRange(start, end);
|
|
16763
|
+
if (preserveScroll) {
|
|
16764
|
+
this.#el.scrollTop = scrollTop;
|
|
16765
|
+
this.#el.scrollLeft = scrollLeft;
|
|
16766
|
+
}
|
|
16767
|
+
return this;
|
|
16768
|
+
}
|
|
16769
|
+
|
|
16770
|
+
/** @returns {string} The full current text value. */
|
|
16771
|
+
getValue() {
|
|
16772
|
+
return this.#el.value;
|
|
16773
|
+
}
|
|
16774
|
+
|
|
16775
|
+
/**
|
|
16776
|
+
* Sets the full value of the element.
|
|
16777
|
+
* @param {string} value - The new value to assign.
|
|
16778
|
+
* @returns {TinyTextRangeEditor}
|
|
16779
|
+
*/
|
|
16780
|
+
setValue(value) {
|
|
16781
|
+
if (typeof value !== 'string') throw new TypeError('Value must be a string.');
|
|
16782
|
+
this.#el.value = value;
|
|
16783
|
+
return this;
|
|
16784
|
+
}
|
|
16785
|
+
|
|
16786
|
+
/** @returns {string} The currently selected text. */
|
|
16787
|
+
getSelectedText() {
|
|
16788
|
+
const { start, end } = this.getSelectionRange();
|
|
16789
|
+
return this.#el.value.slice(start, end);
|
|
16790
|
+
}
|
|
16791
|
+
|
|
16792
|
+
/**
|
|
16793
|
+
* Inserts text at the current selection, replacing any selected content.
|
|
16794
|
+
* @param {string} text - The text to insert.
|
|
16795
|
+
* @param {Object} [settings={}] - Optional auto-spacing behavior.
|
|
16796
|
+
* @param {'start' | 'end' | 'preserve'} [settings.newCursor='end'] - Controls caret position after insertion.
|
|
16797
|
+
* @param {boolean} [settings.autoSpacing=false]
|
|
16798
|
+
* @param {boolean} [settings.autoSpaceLeft=false]
|
|
16799
|
+
* @param {boolean} [settings.autoSpaceRight=false]
|
|
16800
|
+
* @returns {TinyTextRangeEditor}
|
|
16801
|
+
*/
|
|
16802
|
+
insertText(
|
|
16803
|
+
text,
|
|
16804
|
+
{
|
|
16805
|
+
newCursor = 'end',
|
|
16806
|
+
autoSpacing = false,
|
|
16807
|
+
autoSpaceLeft = autoSpacing,
|
|
16808
|
+
autoSpaceRight = autoSpacing,
|
|
16809
|
+
} = {},
|
|
16810
|
+
) {
|
|
16811
|
+
if (typeof text !== 'string') throw new TypeError('Text must be a string.');
|
|
16812
|
+
if (!['start', 'end', 'preserve'].includes(newCursor))
|
|
16813
|
+
throw new TypeError("newCursor must be one of 'start', 'end', or 'preserve'.");
|
|
16814
|
+
if (typeof autoSpacing !== 'boolean') throw new TypeError('autoSpacing must be a boolean.');
|
|
16815
|
+
if (typeof autoSpaceLeft !== 'boolean') throw new TypeError('autoSpaceLeft must be a boolean.');
|
|
16816
|
+
if (typeof autoSpaceRight !== 'boolean')
|
|
16817
|
+
throw new TypeError('autoSpaceRight must be a boolean.');
|
|
16818
|
+
|
|
16819
|
+
const { start, end } = this.getSelectionRange();
|
|
16820
|
+
const value = this.#el.value;
|
|
16821
|
+
|
|
16822
|
+
const leftChar = value[start - 1] || '';
|
|
16823
|
+
const rightChar = value[end] || '';
|
|
16824
|
+
|
|
16825
|
+
const addLeft = autoSpaceLeft && leftChar && !/\s/.test(leftChar);
|
|
16826
|
+
const addRight = autoSpaceRight && rightChar && !/\s/.test(rightChar);
|
|
16827
|
+
|
|
16828
|
+
const finalText = `${addLeft ? ' ' : ''}${text}${addRight ? ' ' : ''}`;
|
|
16829
|
+
|
|
16830
|
+
const newValue = value.slice(0, start) + finalText + value.slice(end);
|
|
16831
|
+
this.setValue(newValue);
|
|
16832
|
+
|
|
16833
|
+
let cursorPos = start;
|
|
16834
|
+
if (newCursor === 'end') cursorPos = start + finalText.length;
|
|
16835
|
+
else if (newCursor === 'preserve') cursorPos = start;
|
|
16836
|
+
|
|
16837
|
+
this.setSelectionRange(cursorPos, cursorPos);
|
|
16838
|
+
return this;
|
|
16839
|
+
}
|
|
16840
|
+
|
|
16841
|
+
/**
|
|
16842
|
+
* Deletes the currently selected text.
|
|
16843
|
+
* @returns {TinyTextRangeEditor}
|
|
16844
|
+
*/
|
|
16845
|
+
deleteSelection() {
|
|
16846
|
+
this.insertText('');
|
|
16847
|
+
return this;
|
|
16848
|
+
}
|
|
16849
|
+
|
|
16850
|
+
/**
|
|
16851
|
+
* Replaces the selection using a transformation function.
|
|
16852
|
+
* @param {(selected: string) => string} transformer - Function that modifies the selected text.
|
|
16853
|
+
* @returns {TinyTextRangeEditor}
|
|
16854
|
+
*/
|
|
16855
|
+
transformSelection(transformer) {
|
|
16856
|
+
if (typeof transformer !== 'function') throw new TypeError('transformer must be a function.');
|
|
16857
|
+
const { start } = this.getSelectionRange();
|
|
16858
|
+
const selected = this.getSelectedText();
|
|
16859
|
+
const transformed = transformer(selected);
|
|
16860
|
+
this.insertText(transformed);
|
|
16861
|
+
this.setSelectionRange(start, start + transformed.length);
|
|
16862
|
+
return this;
|
|
16863
|
+
}
|
|
16864
|
+
|
|
16865
|
+
/**
|
|
16866
|
+
* Surrounds current selection with prefix and suffix.
|
|
16867
|
+
* @param {string} prefix - Text to insert before.
|
|
16868
|
+
* @param {string} suffix - Text to insert after.
|
|
16869
|
+
* @returns {TinyTextRangeEditor}
|
|
16870
|
+
*/
|
|
16871
|
+
surroundSelection(prefix, suffix) {
|
|
16872
|
+
if (typeof prefix !== 'string' || typeof suffix !== 'string')
|
|
16873
|
+
throw new TypeError('prefix and suffix must be strings.');
|
|
16874
|
+
const selected = this.getSelectedText();
|
|
16875
|
+
this.insertText(`${prefix}${selected}${suffix}`);
|
|
16876
|
+
return this;
|
|
16877
|
+
}
|
|
16878
|
+
|
|
16879
|
+
/**
|
|
16880
|
+
* Moves the caret by a given offset.
|
|
16881
|
+
* @param {number} offset - Characters to move.
|
|
16882
|
+
* @returns {TinyTextRangeEditor}
|
|
16883
|
+
*/
|
|
16884
|
+
moveCaret(offset) {
|
|
16885
|
+
if (typeof offset !== 'number') throw new TypeError('offset must be a number.');
|
|
16886
|
+
const { start } = this.getSelectionRange();
|
|
16887
|
+
const pos = Math.max(0, start + offset);
|
|
16888
|
+
this.setSelectionRange(pos, pos);
|
|
16889
|
+
return this;
|
|
16890
|
+
}
|
|
16891
|
+
|
|
16892
|
+
/**
|
|
16893
|
+
* Selects all content in the field.
|
|
16894
|
+
* @returns {TinyTextRangeEditor}
|
|
16895
|
+
*/
|
|
16896
|
+
selectAll() {
|
|
16897
|
+
this.setSelectionRange(0, this.#el.value.length);
|
|
16898
|
+
return this;
|
|
16899
|
+
}
|
|
16900
|
+
|
|
16901
|
+
/**
|
|
16902
|
+
* Expands the current selection by character amounts.
|
|
16903
|
+
* @param {number} before - Characters to expand to the left.
|
|
16904
|
+
* @param {number} after - Characters to expand to the right.
|
|
16905
|
+
* @returns {TinyTextRangeEditor}
|
|
16906
|
+
*/
|
|
16907
|
+
expandSelection(before, after) {
|
|
16908
|
+
if (typeof before !== 'number' || typeof after !== 'number')
|
|
16909
|
+
throw new TypeError('before and after must be numbers.');
|
|
16910
|
+
const { start, end } = this.getSelectionRange();
|
|
16911
|
+
const newStart = Math.max(0, start - before);
|
|
16912
|
+
const newEnd = Math.min(this.#el.value.length, end + after);
|
|
16913
|
+
this.setSelectionRange(newStart, newEnd);
|
|
16914
|
+
return this;
|
|
16915
|
+
}
|
|
16916
|
+
|
|
16917
|
+
/**
|
|
16918
|
+
* Replaces all regex matches in the content.
|
|
16919
|
+
* @param {RegExp} regex - Regex to match.
|
|
16920
|
+
* @param {(match: string) => string} replacer - Replacement function.
|
|
16921
|
+
* @returns {TinyTextRangeEditor}
|
|
16922
|
+
*/
|
|
16923
|
+
replaceAll(regex, replacer) {
|
|
16924
|
+
if (!(regex instanceof RegExp)) throw new TypeError('regex must be a RegExp.');
|
|
16925
|
+
if (typeof replacer !== 'function') throw new TypeError('replacer must be a function.');
|
|
16926
|
+
const newValue = this.#el.value.replace(regex, replacer);
|
|
16927
|
+
this.setValue(newValue);
|
|
16928
|
+
return this;
|
|
16929
|
+
}
|
|
16930
|
+
|
|
16931
|
+
/**
|
|
16932
|
+
* Replaces all regex matches within the currently selected text.
|
|
16933
|
+
*
|
|
16934
|
+
* @param {RegExp} regex - Regular expression to match inside selection.
|
|
16935
|
+
* @param {(match: string) => string} replacer - Function to replace each match.
|
|
16936
|
+
* @returns {TinyTextRangeEditor}
|
|
16937
|
+
*/
|
|
16938
|
+
replaceInSelection(regex, replacer) {
|
|
16939
|
+
if (!(regex instanceof RegExp)) throw new TypeError('regex must be a RegExp.');
|
|
16940
|
+
if (typeof replacer !== 'function') throw new TypeError('replacer must be a function.');
|
|
16941
|
+
|
|
16942
|
+
const { start, end } = this.getSelectionRange();
|
|
16943
|
+
const original = this.#el.value;
|
|
16944
|
+
const selected = original.slice(start, end);
|
|
16945
|
+
|
|
16946
|
+
const replaced = selected.replace(regex, replacer);
|
|
16947
|
+
|
|
16948
|
+
const updated = original.slice(0, start) + replaced + original.slice(end);
|
|
16949
|
+
|
|
16950
|
+
this.setValue(updated);
|
|
16951
|
+
this.setSelectionRange(start, start + replaced.length);
|
|
16952
|
+
return this;
|
|
16953
|
+
}
|
|
16954
|
+
|
|
16955
|
+
/**
|
|
16956
|
+
* Toggles a code around the current selection.
|
|
16957
|
+
* If it's already wrapped, unwraps it.
|
|
16958
|
+
* @param {string} codeName - The code to toggle.
|
|
16959
|
+
* @returns {TinyTextRangeEditor}
|
|
16960
|
+
*/
|
|
16961
|
+
toggleCode(codeName) {
|
|
16962
|
+
if (typeof codeName !== 'string') throw new TypeError('codeName must be a string.');
|
|
16963
|
+
const selected = this.getSelectedText();
|
|
16964
|
+
if (selected.startsWith(codeName) && selected.endsWith(codeName)) {
|
|
16965
|
+
const unwrapped = selected.slice(codeName.length, selected.length - codeName.length);
|
|
16966
|
+
this.insertText(unwrapped);
|
|
16967
|
+
} else {
|
|
16968
|
+
this.insertText(`${codeName}${selected}${codeName}`);
|
|
16969
|
+
}
|
|
16970
|
+
return this;
|
|
16971
|
+
}
|
|
16972
|
+
|
|
16973
|
+
/**
|
|
16974
|
+
* Converts a list of attributes into a string suitable for tag insertion.
|
|
16975
|
+
*
|
|
16976
|
+
* This method supports both standard key-value attribute objects (e.g., `{ key: "value" }`)
|
|
16977
|
+
* and boolean-style attribute arrays (e.g., `[ "disabled", "autofocus" ]`).
|
|
16978
|
+
*
|
|
16979
|
+
* - Attributes passed as an array will render as boolean attributes (e.g., `disabled autofocus`)
|
|
16980
|
+
* - Attributes passed as an object will render as `key="value"` pairs (or just `key` if the value is an empty string)
|
|
16981
|
+
*
|
|
16982
|
+
* @param {Record<string, string> | string[]} attributes - The attributes to serialize into a tag string.
|
|
16983
|
+
* - If an array: treated as a list of boolean-style attributes.
|
|
16984
|
+
* - If an object: treated as key-value pairs.
|
|
16985
|
+
*
|
|
16986
|
+
* @throws {TypeError} If the array contains non-strings, or the object contains non-string values.
|
|
16987
|
+
* @returns {string} A string of serialized attributes for use inside a tag.
|
|
16988
|
+
*
|
|
16989
|
+
* @example
|
|
16990
|
+
* // Using object attributes
|
|
16991
|
+
* _insertAttr({ size: "12", color: "red" });
|
|
16992
|
+
* // Returns: 'size="12" color="red"'
|
|
16993
|
+
*
|
|
16994
|
+
* @example
|
|
16995
|
+
* // Using boolean attributes
|
|
16996
|
+
* _insertAttr(["disabled", "autofocus"]);
|
|
16997
|
+
* // Returns: 'disabled autofocus'
|
|
16998
|
+
*
|
|
16999
|
+
* @example
|
|
17000
|
+
* // Using mixed/empty object values
|
|
17001
|
+
* _insertAttr({ checked: "", class: "btn" });
|
|
17002
|
+
* // Returns: 'checked class="btn"'
|
|
17003
|
+
*/
|
|
17004
|
+
_insertAttr(attributes) {
|
|
17005
|
+
// Reuse attribute logic
|
|
17006
|
+
let attrStr = '';
|
|
17007
|
+
|
|
17008
|
+
if (Array.isArray(attributes)) {
|
|
17009
|
+
// string[]
|
|
17010
|
+
if (!attributes.every((attr) => typeof attr === 'string'))
|
|
17011
|
+
throw new TypeError('All entries in attributes array must be strings.');
|
|
17012
|
+
attrStr = attributes.map((attr) => `${attr}`).join(' ');
|
|
17013
|
+
} else if (typeof attributes === 'object' && attributes !== null) {
|
|
17014
|
+
// Record<string, string>
|
|
17015
|
+
attrStr = Object.entries(attributes)
|
|
17016
|
+
.map(([key, val]) => {
|
|
17017
|
+
if (typeof val !== 'string')
|
|
17018
|
+
throw new TypeError('All entries in attributes object must be strings.');
|
|
17019
|
+
return `${key}${val.length > 0 ? `="${val}"` : ''}`;
|
|
17020
|
+
})
|
|
17021
|
+
.join(' ');
|
|
17022
|
+
} else {
|
|
17023
|
+
throw new TypeError('attributes must be an object or an array of strings.');
|
|
17024
|
+
}
|
|
17025
|
+
|
|
17026
|
+
return attrStr;
|
|
17027
|
+
}
|
|
17028
|
+
|
|
17029
|
+
/**
|
|
17030
|
+
* Wraps the current selection with a tag, optionally including attributes.
|
|
17031
|
+
*
|
|
17032
|
+
* @param {string} tagName - The tag name (e.g., `b`, `color`, etc.).
|
|
17033
|
+
* @param {Record<string,string> | string[]} [attributes={}] - Optional attributes for the opening tag.
|
|
17034
|
+
* - If an object: key-value pairs (e.g., `{ color: "red" }` → `color="red"`).
|
|
17035
|
+
* - If an array: boolean attributes (e.g., `["disabled", "readonly"]`).
|
|
17036
|
+
*
|
|
17037
|
+
* @returns {TinyTextRangeEditor}
|
|
17038
|
+
*/
|
|
17039
|
+
wrapWithTag(tagName, attributes = {}) {
|
|
17040
|
+
if (typeof tagName !== 'string') throw new TypeError('tagName must be a string.');
|
|
17041
|
+
const attrStr = this._insertAttr(attributes);
|
|
17042
|
+
const openTag = attrStr
|
|
17043
|
+
? `${this.#openTag}${tagName} ${attrStr}${this.#closeTag}`
|
|
17044
|
+
: `${this.#openTag}${tagName}${this.#closeTag}`;
|
|
17045
|
+
const closeTag = `${this.#openTag}/${tagName}${this.#closeTag}`;
|
|
17046
|
+
this.surroundSelection(openTag, closeTag);
|
|
17047
|
+
return this;
|
|
17048
|
+
}
|
|
17049
|
+
|
|
17050
|
+
/**
|
|
17051
|
+
* Inserts a tag with optional inner content.
|
|
17052
|
+
* @param {string} tagName - The tag to insert.
|
|
17053
|
+
* @param {string} [content=''] - Optional content between tags.
|
|
17054
|
+
* @param {Record<string,string> | string[]} [attributes={}] - Optional attributes or list of empty attributes.
|
|
17055
|
+
* @returns {TinyTextRangeEditor}
|
|
17056
|
+
*/
|
|
17057
|
+
insertTag(tagName, content = '', attributes = {}) {
|
|
17058
|
+
if (typeof tagName !== 'string') throw new TypeError('tagName must be a string.');
|
|
17059
|
+
if (typeof content !== 'string') throw new TypeError('content must be a string.');
|
|
17060
|
+
|
|
17061
|
+
const attrStr = this._insertAttr(attributes);
|
|
17062
|
+
const open = attrStr
|
|
17063
|
+
? `${this.#openTag}${tagName} ${attrStr}${this.#closeTag}`
|
|
17064
|
+
: `${this.#openTag}${tagName}${this.#closeTag}`;
|
|
17065
|
+
const close = `${this.#openTag}/${tagName}${this.#closeTag}`;
|
|
17066
|
+
|
|
17067
|
+
this.insertText(`${open}${content}${close}`);
|
|
17068
|
+
return this;
|
|
17069
|
+
}
|
|
17070
|
+
|
|
17071
|
+
/**
|
|
17072
|
+
* Inserts a self-closing tag.
|
|
17073
|
+
* @param {string} tagName - The tag name.
|
|
17074
|
+
* @param {Record<string,string> | string[]} [attributes={}] - Optional attributes or list of empty attributes.
|
|
17075
|
+
* @returns {TinyTextRangeEditor}
|
|
17076
|
+
*/
|
|
17077
|
+
insertSelfClosingTag(tagName, attributes = {}) {
|
|
17078
|
+
if (typeof tagName !== 'string') throw new TypeError('tagName must be a string.');
|
|
17079
|
+
|
|
17080
|
+
const attrStr = this._insertAttr(attributes);
|
|
17081
|
+
const tag = attrStr
|
|
17082
|
+
? `${this.#openTag}${tagName} ${attrStr}${this.#closeTag}`
|
|
17083
|
+
: `${this.#openTag}${tagName}${this.#closeTag}`;
|
|
17084
|
+
|
|
17085
|
+
this.insertText(tag);
|
|
17086
|
+
return this;
|
|
17087
|
+
}
|
|
17088
|
+
|
|
17089
|
+
/**
|
|
17090
|
+
* Toggles a tag around the current selection.
|
|
17091
|
+
* Supports tags with attributes. If already wrapped, it unwraps.
|
|
17092
|
+
* @param {string} tagName - The tag to toggle.
|
|
17093
|
+
* @param {Record<string,string> | string[]} [attributes={}] - Optional attributes to apply when wrapping.
|
|
17094
|
+
* @returns {TinyTextRangeEditor}
|
|
17095
|
+
*/
|
|
17096
|
+
toggleTag(tagName, attributes = {}) {
|
|
17097
|
+
if (typeof tagName !== 'string') throw new TypeError('tagName must be a string.');
|
|
17098
|
+
const selected = this.getSelectedText();
|
|
17099
|
+
|
|
17100
|
+
// Regex: opening tag with optional attributes, and closing tag
|
|
17101
|
+
const openRegex = new RegExp(`^\\[${tagName}(\\s+[^\\]]*)?\\]`);
|
|
17102
|
+
const closeRegex = new RegExp(`\\[/${tagName}\\]$`);
|
|
17103
|
+
|
|
17104
|
+
const hasOpen = openRegex.test(selected);
|
|
17105
|
+
const hasClose = closeRegex.test(selected);
|
|
17106
|
+
|
|
17107
|
+
if (hasOpen && hasClose) {
|
|
17108
|
+
const unwrapped = selected
|
|
17109
|
+
.replace(openRegex, '') // remove opening tag
|
|
17110
|
+
.replace(closeRegex, ''); // remove closing tag
|
|
17111
|
+
this.insertText(unwrapped);
|
|
17112
|
+
} else {
|
|
17113
|
+
const attrStr = this._insertAttr(attributes);
|
|
17114
|
+
const open = attrStr
|
|
17115
|
+
? `${this.#openTag}${tagName} ${attrStr}${this.#closeTag}`
|
|
17116
|
+
: `${this.#openTag}${tagName}${this.#closeTag}`;
|
|
17117
|
+
const close = `${this.#openTag}/${tagName}${this.#closeTag}`;
|
|
17118
|
+
this.insertText(`${open}${selected}${close}`);
|
|
17119
|
+
}
|
|
17120
|
+
|
|
17121
|
+
return this;
|
|
17122
|
+
}
|
|
17123
|
+
}
|
|
17124
|
+
|
|
17125
|
+
/* harmony default export */ const libs_TinyTextRangeEditor = (TinyTextRangeEditor);
|
|
17126
|
+
|
|
17127
|
+
;// ./src/v1/libs/TinyClipboard.mjs
|
|
17128
|
+
/**
|
|
17129
|
+
* Utility class to handle clipboard operations for text and blob data.
|
|
17130
|
+
* Supports modern Clipboard API, custom platform, and legacy execCommand fallback.
|
|
17131
|
+
*/
|
|
17132
|
+
class TinyClipboard {
|
|
17133
|
+
/**
|
|
17134
|
+
* Indicates whether the legacy `document.execCommand()` API is available.
|
|
17135
|
+
* Used as a fallback for clipboard operations when modern APIs are not supported.
|
|
17136
|
+
*
|
|
17137
|
+
* @type {boolean}
|
|
17138
|
+
*/
|
|
17139
|
+
#existExecCommand = false;
|
|
17140
|
+
|
|
17141
|
+
/**
|
|
17142
|
+
* Indicates whether the modern Clipboard API (`navigator.clipboard`) is available.
|
|
17143
|
+
*
|
|
17144
|
+
* @type {boolean}
|
|
17145
|
+
*/
|
|
17146
|
+
#existNavigator = false;
|
|
17147
|
+
|
|
17148
|
+
/**
|
|
17149
|
+
* Function used to copy plain text to the clipboard.
|
|
17150
|
+
* Can be overridden using `setCopyText()`.
|
|
17151
|
+
*
|
|
17152
|
+
* @type {((text: string) => Promise<void>) | null}
|
|
17153
|
+
*/
|
|
17154
|
+
#copyText = null;
|
|
17155
|
+
|
|
17156
|
+
/**
|
|
17157
|
+
* Function used to copy a Blob (binary data) to the clipboard.
|
|
17158
|
+
* Can be overridden using `setCopyBlob()`.
|
|
17159
|
+
*
|
|
17160
|
+
* @type {((blob: Blob) => Promise<void>) | null}
|
|
17161
|
+
*/
|
|
17162
|
+
#copyBlob = null;
|
|
17163
|
+
|
|
17164
|
+
/**
|
|
17165
|
+
* Constructs a new TinyClipboard instance.
|
|
17166
|
+
* Automatically detects and configures available clipboard APIs.
|
|
17167
|
+
*/
|
|
17168
|
+
constructor() {
|
|
17169
|
+
// Whether the Clipboard API is available.
|
|
17170
|
+
if (typeof navigator.clipboard !== 'undefined' && navigator.clipboard !== null) {
|
|
17171
|
+
this.#existNavigator = true;
|
|
17172
|
+
this.#copyText = (text) => navigator.clipboard.writeText(text);
|
|
17173
|
+
this.#copyBlob = (blob) =>
|
|
17174
|
+
navigator.clipboard.write([
|
|
17175
|
+
new ClipboardItem({
|
|
17176
|
+
[blob.type]: blob,
|
|
17177
|
+
}),
|
|
17178
|
+
]);
|
|
17179
|
+
}
|
|
17180
|
+
|
|
17181
|
+
/**
|
|
17182
|
+
* @type {boolean}
|
|
17183
|
+
* Whether the legacy execCommand API is available.
|
|
17184
|
+
*/
|
|
17185
|
+
this.#existExecCommand =
|
|
17186
|
+
typeof document.execCommand !== 'undefined' && document.execCommand !== null;
|
|
17187
|
+
}
|
|
17188
|
+
|
|
17189
|
+
/**
|
|
17190
|
+
* Override the default text copy behavior.
|
|
17191
|
+
* This allows you to provide your own clipboard implementation or
|
|
17192
|
+
* integrate with external systems like Capacitor or Electron.
|
|
17193
|
+
*
|
|
17194
|
+
* @param {(text: string) => Promise<void>} callback - The function to use for copying text.
|
|
17195
|
+
* @throws {TypeError} If the callback is not a function.
|
|
17196
|
+
*/
|
|
17197
|
+
setCopyText(callback) {
|
|
17198
|
+
if (typeof callback !== 'function')
|
|
17199
|
+
throw new TypeError('setCopyText expected a function that returns Promise<void>.');
|
|
17200
|
+
this.#copyText = callback;
|
|
17201
|
+
}
|
|
17202
|
+
|
|
17203
|
+
/**
|
|
17204
|
+
* Override the default blob copy behavior.
|
|
17205
|
+
* This allows you to provide a custom clipboard handling method for blob data.
|
|
17206
|
+
*
|
|
17207
|
+
* @param {(blob: Blob) => Promise<void>} callback - The function to use for copying blob data.
|
|
17208
|
+
* @throws {TypeError} If the callback is not a function.
|
|
17209
|
+
*/
|
|
17210
|
+
setCopyBlob(callback) {
|
|
17211
|
+
if (typeof callback !== 'function')
|
|
17212
|
+
throw new TypeError('setCopyBlob expected a function that returns Promise<void>.');
|
|
17213
|
+
this.#copyBlob = callback;
|
|
17214
|
+
}
|
|
17215
|
+
|
|
17216
|
+
/**
|
|
17217
|
+
* Copy a plain text string to the clipboard.
|
|
17218
|
+
* Uses modern or legacy fallback.
|
|
17219
|
+
*
|
|
17220
|
+
* @param {string} text - The text string to be copied.
|
|
17221
|
+
* @returns {Promise<void>} A promise resolving when the text is copied or boolean for legacy.
|
|
17222
|
+
*/
|
|
17223
|
+
copyText(text) {
|
|
17224
|
+
if (typeof text !== 'string') throw new TypeError('copyText expected a string.');
|
|
17225
|
+
// Clipboard API
|
|
17226
|
+
if (this.#copyText) return this.#copyText(text);
|
|
17227
|
+
// Classic API
|
|
17228
|
+
else if (this.#existExecCommand) {
|
|
17229
|
+
const host = document.body;
|
|
17230
|
+
const copyInput = document.createElement('input');
|
|
17231
|
+
copyInput.style.position = 'fixed';
|
|
17232
|
+
copyInput.style.opacity = '0';
|
|
17233
|
+
copyInput.value = text;
|
|
17234
|
+
host.append(copyInput);
|
|
17235
|
+
|
|
17236
|
+
copyInput.select();
|
|
17237
|
+
copyInput.setSelectionRange(0, 99999);
|
|
17238
|
+
document.execCommand('Copy');
|
|
17239
|
+
copyInput.remove();
|
|
17240
|
+
return new Promise((resolve) => resolve(undefined));
|
|
17241
|
+
}
|
|
17242
|
+
throw new Error('Clipboard API not found!');
|
|
17243
|
+
}
|
|
17244
|
+
|
|
17245
|
+
/**
|
|
17246
|
+
* Copy a Blob (binary data) to the clipboard.
|
|
17247
|
+
*
|
|
17248
|
+
* @param {Blob} blob - The blob object to copy.
|
|
17249
|
+
* @returns {Promise<void>} A promise that resolves when the blob is copied or null on fallback.
|
|
17250
|
+
*/
|
|
17251
|
+
copyBlob(blob) {
|
|
17252
|
+
if (!(blob instanceof Blob)) throw new TypeError('copyBlob expected a Blob instance.');
|
|
17253
|
+
return new Promise((resolve, reject) => {
|
|
17254
|
+
if (this.#copyBlob) {
|
|
17255
|
+
return this.#copyBlob(blob).then(resolve).catch(reject);
|
|
17256
|
+
}
|
|
17257
|
+
throw new Error('Clipboard API not found!');
|
|
17258
|
+
});
|
|
17259
|
+
}
|
|
17260
|
+
|
|
17261
|
+
/**
|
|
17262
|
+
* Internal: Handle getting blob data from a clipboard item.
|
|
17263
|
+
*
|
|
17264
|
+
* @private
|
|
17265
|
+
* @param {string} type - The MIME type to fetch.
|
|
17266
|
+
* @param {ClipboardItem} clipboardItem - Clipboard item instance.
|
|
17267
|
+
* @returns {Promise<Blob>} A promise that resolves with the Blob.
|
|
17268
|
+
*/
|
|
17269
|
+
_handleBlob(type, clipboardItem) {
|
|
17270
|
+
return clipboardItem.getType(type);
|
|
17271
|
+
}
|
|
17272
|
+
|
|
17273
|
+
/**
|
|
17274
|
+
* Internal: Handle getting plain text from a clipboard item.
|
|
17275
|
+
*
|
|
17276
|
+
* @private
|
|
17277
|
+
* @param {string} type - The MIME type (should be 'text/plain').
|
|
17278
|
+
* @param {ClipboardItem} clipboardItem - Clipboard item instance.
|
|
17279
|
+
* @returns {Promise<string>} A promise that resolves with the text content.
|
|
17280
|
+
*/
|
|
17281
|
+
_handleText(type, clipboardItem) {
|
|
17282
|
+
return this._handleBlob(type, clipboardItem).then((blob) => blob.text());
|
|
17283
|
+
}
|
|
17284
|
+
|
|
17285
|
+
/**
|
|
17286
|
+
* Read clipboard data based on filters like type, mime, index.
|
|
17287
|
+
*
|
|
17288
|
+
* @param {number|null} [index=0] - Item index or null for all.
|
|
17289
|
+
* @param {'text'|'custom'|null} [type=null] - Data type to filter.
|
|
17290
|
+
* @param {string|null} [mimeFormat=null] - MIME type or prefix.
|
|
17291
|
+
* @param {boolean} [fixValue=false] - If true, exact match on MIME type.
|
|
17292
|
+
* @returns {Promise<Blob|string|Array<Blob|string>|null>} A promise resolving with matching data.
|
|
17293
|
+
*/
|
|
17294
|
+
_readData(index = 0, type = null, mimeFormat = null, fixValue = false) {
|
|
17295
|
+
return new Promise((resolve, reject) => {
|
|
17296
|
+
this._read(index)
|
|
17297
|
+
.then((items) => {
|
|
17298
|
+
if (!items) return resolve(null);
|
|
17299
|
+
/** @type {Array<Blob|string>} */
|
|
17300
|
+
const finalResult = [];
|
|
17301
|
+
|
|
17302
|
+
// Complete task
|
|
17303
|
+
let continueLoop = true;
|
|
17304
|
+
|
|
17305
|
+
/**
|
|
17306
|
+
* @param {string} mimeType
|
|
17307
|
+
* @param {ClipboardItem} item
|
|
17308
|
+
*/
|
|
17309
|
+
const completeTask = async (mimeType, item) => {
|
|
17310
|
+
if (!continueLoop) return;
|
|
17311
|
+
// Custom
|
|
17312
|
+
if (
|
|
17313
|
+
(type === null || type === 'custom') &&
|
|
17314
|
+
typeof mimeFormat === 'string' &&
|
|
17315
|
+
((!fixValue && mimeType.startsWith(mimeFormat)) ||
|
|
17316
|
+
(fixValue && mimeType === mimeFormat))
|
|
17317
|
+
) {
|
|
17318
|
+
continueLoop = false;
|
|
17319
|
+
const result = await this._handleBlob(mimeType, item);
|
|
17320
|
+
if (result) finalResult.push(result);
|
|
17321
|
+
}
|
|
17322
|
+
|
|
17323
|
+
// Text
|
|
17324
|
+
else if ((type === null || type === 'text') && mimeType === 'text/plain') {
|
|
17325
|
+
continueLoop = false;
|
|
17326
|
+
const result = await this._handleText(mimeType, item);
|
|
17327
|
+
if (result) finalResult.push(result);
|
|
17328
|
+
}
|
|
17329
|
+
|
|
17330
|
+
// Blob
|
|
17331
|
+
else if (type === null) {
|
|
17332
|
+
continueLoop = false;
|
|
17333
|
+
const result = await this._handleBlob(mimeType, item);
|
|
17334
|
+
if (result) finalResult.push(result);
|
|
17335
|
+
}
|
|
17336
|
+
};
|
|
17337
|
+
|
|
17338
|
+
/** @type {Promise<void>[]} */
|
|
17339
|
+
const promises = [];
|
|
17340
|
+
|
|
17341
|
+
/**
|
|
17342
|
+
* Read Item
|
|
17343
|
+
* @param {ClipboardItem | ClipboardItems} item
|
|
17344
|
+
*/
|
|
17345
|
+
const readItem = (item) => {
|
|
17346
|
+
if (!(item instanceof ClipboardItem))
|
|
17347
|
+
throw new Error('Expected ClipboardItem when reading data.');
|
|
17348
|
+
for (const tIndex in item.types) promises.push(completeTask(item.types[tIndex], item));
|
|
17349
|
+
};
|
|
17350
|
+
|
|
17351
|
+
// Specific Item
|
|
17352
|
+
if (
|
|
17353
|
+
typeof index === 'number' &&
|
|
17354
|
+
!Number.isNaN(index) &&
|
|
17355
|
+
Number.isFinite(index) &&
|
|
17356
|
+
index > -1
|
|
17357
|
+
) {
|
|
17358
|
+
readItem(items);
|
|
17359
|
+
Promise.all(promises)
|
|
17360
|
+
.then(() => {
|
|
17361
|
+
if (finalResult[0]) resolve(finalResult[0]);
|
|
17362
|
+
else resolve(null);
|
|
17363
|
+
})
|
|
17364
|
+
.catch(reject);
|
|
17365
|
+
}
|
|
17366
|
+
|
|
17367
|
+
// All
|
|
17368
|
+
else if (Array.isArray(items)) {
|
|
17369
|
+
for (const tIndex in items) readItem(items[tIndex]);
|
|
17370
|
+
Promise.all(promises)
|
|
17371
|
+
.then(() => resolve(finalResult))
|
|
17372
|
+
.catch(reject);
|
|
17373
|
+
}
|
|
17374
|
+
})
|
|
17375
|
+
// Fail
|
|
17376
|
+
.catch(reject);
|
|
17377
|
+
});
|
|
17378
|
+
}
|
|
17379
|
+
|
|
17380
|
+
/**
|
|
17381
|
+
* Read plain text from the clipboard (single item by index).
|
|
17382
|
+
*
|
|
17383
|
+
* @param {number} [index=0] - The index of the clipboard item to read.
|
|
17384
|
+
* @returns {Promise<string|null>} A promise that resolves to the clipboard text or null.
|
|
17385
|
+
*/
|
|
17386
|
+
async readText(index = 0) {
|
|
17387
|
+
const value = await this._readData(index, 'text');
|
|
17388
|
+
if (typeof value !== 'string') throw new Error('Failed to read text: expected string result.');
|
|
17389
|
+
return value;
|
|
17390
|
+
}
|
|
17391
|
+
|
|
17392
|
+
/**
|
|
17393
|
+
* Read custom clipboard data based on MIME type from a specific index.
|
|
17394
|
+
*
|
|
17395
|
+
* @param {string|null} [mimeFormat=null] - MIME prefix to match (e.g., "image/").
|
|
17396
|
+
* @param {boolean} [fixValue=false] - If true, matches exact MIME instead of prefix.
|
|
17397
|
+
* @param {number} [index=0] - Clipboard item index.
|
|
17398
|
+
* @returns {Promise<Blob|null>} A promise resolving with a blob or null.
|
|
17399
|
+
*/
|
|
17400
|
+
async readCustom(mimeFormat = null, fixValue = false, index = 0) {
|
|
17401
|
+
const value = await this._readData(index, 'custom', mimeFormat, fixValue);
|
|
17402
|
+
if (!(value instanceof Blob)) throw new Error('Failed to read custom data: expected Blob.');
|
|
17403
|
+
return value;
|
|
17404
|
+
}
|
|
17405
|
+
|
|
17406
|
+
/**
|
|
17407
|
+
* Read all available plain text entries from the clipboard.
|
|
17408
|
+
*
|
|
17409
|
+
* @returns {Promise<string[]>} A promise resolving to an array of strings or null.
|
|
17410
|
+
*/
|
|
17411
|
+
async readAllTexts() {
|
|
17412
|
+
const values = await this._readData(null, 'text');
|
|
17413
|
+
if (!Array.isArray(values))
|
|
17414
|
+
throw new Error('Expected array of strings when reading all texts.');
|
|
17415
|
+
if (!values.every((value) => typeof value === 'string'))
|
|
17416
|
+
throw new Error('Some values returned were not strings.');
|
|
17417
|
+
return values;
|
|
17418
|
+
}
|
|
17419
|
+
|
|
17420
|
+
/**
|
|
17421
|
+
* Read all clipboard data matching a specific custom MIME type.
|
|
17422
|
+
*
|
|
17423
|
+
* @param {string|null} [mimeFormat=null] - MIME prefix or exact type.
|
|
17424
|
+
* @param {boolean} [fixValue=false] - Match prefix or exact MIME.
|
|
17425
|
+
* @returns {Promise<Blob[]>} A promise resolving with array of Blobs or null.
|
|
17426
|
+
*/
|
|
17427
|
+
async readAllCustom(mimeFormat = null, fixValue = false) {
|
|
17428
|
+
const values = await this._readData(null, 'custom', mimeFormat, fixValue);
|
|
17429
|
+
if (!Array.isArray(values))
|
|
17430
|
+
throw new Error('Expected array of blobs when reading all custom items.');
|
|
17431
|
+
if (!values.every((value) => value instanceof Blob))
|
|
17432
|
+
throw new Error('Some values returned were not Blob instances.');
|
|
17433
|
+
return values;
|
|
17434
|
+
}
|
|
17435
|
+
|
|
17436
|
+
/**
|
|
17437
|
+
* Read all clipboard data as Blob or text depending on type.
|
|
17438
|
+
*
|
|
17439
|
+
* @param {'text'|'custom'|null} [type=null] - The type of data to retrieve.
|
|
17440
|
+
* @param {string|null} [mimeFormat=null] - The MIME type or prefix to match.
|
|
17441
|
+
* @returns {Promise<Array<Blob|string>>} A promise resolving with matching data array.
|
|
17442
|
+
*/
|
|
17443
|
+
async readAllData(type = null, mimeFormat = null) {
|
|
17444
|
+
const value = await this._readData(null, type, mimeFormat);
|
|
17445
|
+
if (!Array.isArray(value)) throw new Error('Expected array result when reading all data.');
|
|
17446
|
+
return value;
|
|
17447
|
+
}
|
|
17448
|
+
|
|
17449
|
+
/**
|
|
17450
|
+
* Read clipboard data at a specific index or all if null.
|
|
17451
|
+
*
|
|
17452
|
+
* @param {number|null} index - Index of the item to retrieve or null to get all.
|
|
17453
|
+
* @returns {Promise<ClipboardItem|ClipboardItems|null>} A promise resolving with a clipboard item or array of items.
|
|
17454
|
+
*/
|
|
17455
|
+
_read(index) {
|
|
17456
|
+
return new Promise((resolve, reject) => {
|
|
17457
|
+
if (!this.#existNavigator) reject(new Error('Clipboard API not found!'));
|
|
17458
|
+
navigator.clipboard
|
|
17459
|
+
.read()
|
|
17460
|
+
.then((items) => {
|
|
17461
|
+
// Index is number
|
|
17462
|
+
if (typeof index === 'number') {
|
|
17463
|
+
if (Number.isNaN(index) || !Number.isFinite(index) || index < 0)
|
|
17464
|
+
throw new Error(`Invalid index value: ${index}`);
|
|
17465
|
+
if (items[index]) resolve(items[index]);
|
|
17466
|
+
// Not found
|
|
17467
|
+
else resolve(null);
|
|
17468
|
+
}
|
|
17469
|
+
// Get All
|
|
17470
|
+
resolve(items);
|
|
17471
|
+
})
|
|
17472
|
+
.catch(reject);
|
|
17473
|
+
});
|
|
17474
|
+
}
|
|
17475
|
+
|
|
17476
|
+
/**
|
|
17477
|
+
* Read clipboard data at a specific index.
|
|
17478
|
+
*
|
|
17479
|
+
* @param {number} index - Index of the item to retrieve
|
|
17480
|
+
* @returns {Promise<ClipboardItem|null>} A promise resolving with a clipboard item.
|
|
17481
|
+
*/
|
|
17482
|
+
async readIndex(index) {
|
|
17483
|
+
const value = await this._read(index);
|
|
17484
|
+
if (value !== null && !(value instanceof ClipboardItem))
|
|
17485
|
+
throw new Error(`Value at index ${index} is not a ClipboardItem.`);
|
|
17486
|
+
return value;
|
|
17487
|
+
}
|
|
17488
|
+
|
|
17489
|
+
/**
|
|
17490
|
+
* Read all clipboard content without any filters.
|
|
17491
|
+
*
|
|
17492
|
+
* @returns {Promise<ClipboardItems>} A promise resolving with all clipboard items.
|
|
17493
|
+
*/
|
|
17494
|
+
async readAll() {
|
|
17495
|
+
const value = await this._read(null);
|
|
17496
|
+
if (!Array.isArray(value)) throw new Error('Expected array result from clipboard read.');
|
|
17497
|
+
for (const item of value) {
|
|
17498
|
+
if (!(item instanceof ClipboardItem))
|
|
17499
|
+
throw new Error('Invalid item type found in clipboard result.');
|
|
17500
|
+
}
|
|
17501
|
+
return value;
|
|
17502
|
+
}
|
|
17503
|
+
|
|
17504
|
+
/**
|
|
17505
|
+
* Returns whether the legacy `document.execCommand()` API is available.
|
|
17506
|
+
* This can be used to determine if a fallback clipboard method is usable.
|
|
17507
|
+
*
|
|
17508
|
+
* @returns {boolean} True if `document.execCommand` is available.
|
|
17509
|
+
*/
|
|
17510
|
+
isExecCommandAvailable() {
|
|
17511
|
+
return this.#existExecCommand;
|
|
17512
|
+
}
|
|
17513
|
+
|
|
17514
|
+
/**
|
|
17515
|
+
* Returns whether the modern Clipboard API (`navigator.clipboard`) is available.
|
|
17516
|
+
* Useful to know if full clipboard features can be accessed.
|
|
17517
|
+
*
|
|
17518
|
+
* @returns {boolean} True if `navigator.clipboard` is available.
|
|
17519
|
+
*/
|
|
17520
|
+
isNavigatorClipboardAvailable() {
|
|
17521
|
+
return this.#existNavigator;
|
|
17522
|
+
}
|
|
17523
|
+
|
|
17524
|
+
/**
|
|
17525
|
+
* Returns the function used to copy plain text to the clipboard.
|
|
17526
|
+
* This function may be built-in or set manually via `setCopyText`.
|
|
17527
|
+
*
|
|
17528
|
+
* @returns {((text: string) => Promise<void>) | null} The current text copy function or null if unavailable.
|
|
17529
|
+
*/
|
|
17530
|
+
getCopyTextFunc() {
|
|
17531
|
+
return this.#copyText;
|
|
17532
|
+
}
|
|
17533
|
+
|
|
17534
|
+
/**
|
|
17535
|
+
* Returns the function used to copy Blob (binary data) to the clipboard.
|
|
17536
|
+
* This function may be built-in or set manually via `setCopyBlob`.
|
|
17537
|
+
*
|
|
17538
|
+
* @returns {((blob: Blob) => Promise<void>) | null} The current blob copy function or null if unavailable.
|
|
17539
|
+
*/
|
|
17540
|
+
getCopyBlobFunc() {
|
|
17541
|
+
return this.#copyBlob;
|
|
17542
|
+
}
|
|
17543
|
+
}
|
|
17544
|
+
|
|
17545
|
+
/* harmony default export */ const libs_TinyClipboard = (TinyClipboard);
|
|
17546
|
+
|
|
16376
17547
|
;// ./src/v1/index.mjs
|
|
16377
17548
|
|
|
16378
17549
|
|
|
@@ -16404,6 +17575,8 @@ class TinySmartScroller {
|
|
|
16404
17575
|
|
|
16405
17576
|
|
|
16406
17577
|
|
|
17578
|
+
|
|
17579
|
+
|
|
16407
17580
|
|
|
16408
17581
|
|
|
16409
17582
|
|