tiny-essentials 1.17.1 → 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 +164 -21
- 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 +164 -21
- package/dist/v1/TinyDragger.min.js +1 -1
- package/dist/v1/TinyEssentials.js +1046 -21
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyHtml.js +164 -21
- package/dist/v1/TinyHtml.min.js +1 -1
- package/dist/v1/TinySmartScroller.js +164 -21
- 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 +166 -21
- 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 +164 -21
- package/dist/v1/libs/TinyHtml.d.mts +150 -27
- package/dist/v1/libs/TinyHtml.mjs +158 -20
- 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 +112 -15
- package/docs/v1/libs/TinyTextRangeEditor.md +208 -0
- package/package.json +1 -1
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility class to handle clipboard operations for text and blob data.
|
|
3
|
+
* Supports modern Clipboard API, custom platform, and legacy execCommand fallback.
|
|
4
|
+
*/
|
|
5
|
+
class TinyClipboard {
|
|
6
|
+
/**
|
|
7
|
+
* Indicates whether the legacy `document.execCommand()` API is available.
|
|
8
|
+
* Used as a fallback for clipboard operations when modern APIs are not supported.
|
|
9
|
+
*
|
|
10
|
+
* @type {boolean}
|
|
11
|
+
*/
|
|
12
|
+
#existExecCommand = false;
|
|
13
|
+
/**
|
|
14
|
+
* Indicates whether the modern Clipboard API (`navigator.clipboard`) is available.
|
|
15
|
+
*
|
|
16
|
+
* @type {boolean}
|
|
17
|
+
*/
|
|
18
|
+
#existNavigator = false;
|
|
19
|
+
/**
|
|
20
|
+
* Function used to copy plain text to the clipboard.
|
|
21
|
+
* Can be overridden using `setCopyText()`.
|
|
22
|
+
*
|
|
23
|
+
* @type {((text: string) => Promise<void>) | null}
|
|
24
|
+
*/
|
|
25
|
+
#copyText = null;
|
|
26
|
+
/**
|
|
27
|
+
* Function used to copy a Blob (binary data) to the clipboard.
|
|
28
|
+
* Can be overridden using `setCopyBlob()`.
|
|
29
|
+
*
|
|
30
|
+
* @type {((blob: Blob) => Promise<void>) | null}
|
|
31
|
+
*/
|
|
32
|
+
#copyBlob = null;
|
|
33
|
+
/**
|
|
34
|
+
* Constructs a new TinyClipboard instance.
|
|
35
|
+
* Automatically detects and configures available clipboard APIs.
|
|
36
|
+
*/
|
|
37
|
+
constructor() {
|
|
38
|
+
// Whether the Clipboard API is available.
|
|
39
|
+
if (typeof navigator.clipboard !== 'undefined' && navigator.clipboard !== null) {
|
|
40
|
+
this.#existNavigator = true;
|
|
41
|
+
this.#copyText = (text) => navigator.clipboard.writeText(text);
|
|
42
|
+
this.#copyBlob = (blob) => navigator.clipboard.write([
|
|
43
|
+
new ClipboardItem({
|
|
44
|
+
[blob.type]: blob,
|
|
45
|
+
}),
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @type {boolean}
|
|
50
|
+
* Whether the legacy execCommand API is available.
|
|
51
|
+
*/
|
|
52
|
+
this.#existExecCommand =
|
|
53
|
+
typeof document.execCommand !== 'undefined' && document.execCommand !== null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Override the default text copy behavior.
|
|
57
|
+
* This allows you to provide your own clipboard implementation or
|
|
58
|
+
* integrate with external systems like Capacitor or Electron.
|
|
59
|
+
*
|
|
60
|
+
* @param {(text: string) => Promise<void>} callback - The function to use for copying text.
|
|
61
|
+
* @throws {TypeError} If the callback is not a function.
|
|
62
|
+
*/
|
|
63
|
+
setCopyText(callback) {
|
|
64
|
+
if (typeof callback !== 'function')
|
|
65
|
+
throw new TypeError('setCopyText expected a function that returns Promise<void>.');
|
|
66
|
+
this.#copyText = callback;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Override the default blob copy behavior.
|
|
70
|
+
* This allows you to provide a custom clipboard handling method for blob data.
|
|
71
|
+
*
|
|
72
|
+
* @param {(blob: Blob) => Promise<void>} callback - The function to use for copying blob data.
|
|
73
|
+
* @throws {TypeError} If the callback is not a function.
|
|
74
|
+
*/
|
|
75
|
+
setCopyBlob(callback) {
|
|
76
|
+
if (typeof callback !== 'function')
|
|
77
|
+
throw new TypeError('setCopyBlob expected a function that returns Promise<void>.');
|
|
78
|
+
this.#copyBlob = callback;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Copy a plain text string to the clipboard.
|
|
82
|
+
* Uses modern or legacy fallback.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} text - The text string to be copied.
|
|
85
|
+
* @returns {Promise<void>} A promise resolving when the text is copied or boolean for legacy.
|
|
86
|
+
*/
|
|
87
|
+
copyText(text) {
|
|
88
|
+
if (typeof text !== 'string')
|
|
89
|
+
throw new TypeError('copyText expected a string.');
|
|
90
|
+
// Clipboard API
|
|
91
|
+
if (this.#copyText)
|
|
92
|
+
return this.#copyText(text);
|
|
93
|
+
// Classic API
|
|
94
|
+
else if (this.#existExecCommand) {
|
|
95
|
+
const host = document.body;
|
|
96
|
+
const copyInput = document.createElement('input');
|
|
97
|
+
copyInput.style.position = 'fixed';
|
|
98
|
+
copyInput.style.opacity = '0';
|
|
99
|
+
copyInput.value = text;
|
|
100
|
+
host.append(copyInput);
|
|
101
|
+
copyInput.select();
|
|
102
|
+
copyInput.setSelectionRange(0, 99999);
|
|
103
|
+
document.execCommand('Copy');
|
|
104
|
+
copyInput.remove();
|
|
105
|
+
return new Promise((resolve) => resolve(undefined));
|
|
106
|
+
}
|
|
107
|
+
throw new Error('Clipboard API not found!');
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Copy a Blob (binary data) to the clipboard.
|
|
111
|
+
*
|
|
112
|
+
* @param {Blob} blob - The blob object to copy.
|
|
113
|
+
* @returns {Promise<void>} A promise that resolves when the blob is copied or null on fallback.
|
|
114
|
+
*/
|
|
115
|
+
copyBlob(blob) {
|
|
116
|
+
if (!(blob instanceof Blob))
|
|
117
|
+
throw new TypeError('copyBlob expected a Blob instance.');
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
if (this.#copyBlob) {
|
|
120
|
+
return this.#copyBlob(blob).then(resolve).catch(reject);
|
|
121
|
+
}
|
|
122
|
+
throw new Error('Clipboard API not found!');
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Internal: Handle getting blob data from a clipboard item.
|
|
127
|
+
*
|
|
128
|
+
* @private
|
|
129
|
+
* @param {string} type - The MIME type to fetch.
|
|
130
|
+
* @param {ClipboardItem} clipboardItem - Clipboard item instance.
|
|
131
|
+
* @returns {Promise<Blob>} A promise that resolves with the Blob.
|
|
132
|
+
*/
|
|
133
|
+
_handleBlob(type, clipboardItem) {
|
|
134
|
+
return clipboardItem.getType(type);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Internal: Handle getting plain text from a clipboard item.
|
|
138
|
+
*
|
|
139
|
+
* @private
|
|
140
|
+
* @param {string} type - The MIME type (should be 'text/plain').
|
|
141
|
+
* @param {ClipboardItem} clipboardItem - Clipboard item instance.
|
|
142
|
+
* @returns {Promise<string>} A promise that resolves with the text content.
|
|
143
|
+
*/
|
|
144
|
+
_handleText(type, clipboardItem) {
|
|
145
|
+
return this._handleBlob(type, clipboardItem).then((blob) => blob.text());
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Read clipboard data based on filters like type, mime, index.
|
|
149
|
+
*
|
|
150
|
+
* @param {number|null} [index=0] - Item index or null for all.
|
|
151
|
+
* @param {'text'|'custom'|null} [type=null] - Data type to filter.
|
|
152
|
+
* @param {string|null} [mimeFormat=null] - MIME type or prefix.
|
|
153
|
+
* @param {boolean} [fixValue=false] - If true, exact match on MIME type.
|
|
154
|
+
* @returns {Promise<Blob|string|Array<Blob|string>|null>} A promise resolving with matching data.
|
|
155
|
+
*/
|
|
156
|
+
_readData(index = 0, type = null, mimeFormat = null, fixValue = false) {
|
|
157
|
+
return new Promise((resolve, reject) => {
|
|
158
|
+
this._read(index)
|
|
159
|
+
.then((items) => {
|
|
160
|
+
if (!items)
|
|
161
|
+
return resolve(null);
|
|
162
|
+
/** @type {Array<Blob|string>} */
|
|
163
|
+
const finalResult = [];
|
|
164
|
+
// Complete task
|
|
165
|
+
let continueLoop = true;
|
|
166
|
+
/**
|
|
167
|
+
* @param {string} mimeType
|
|
168
|
+
* @param {ClipboardItem} item
|
|
169
|
+
*/
|
|
170
|
+
const completeTask = async (mimeType, item) => {
|
|
171
|
+
if (!continueLoop)
|
|
172
|
+
return;
|
|
173
|
+
// Custom
|
|
174
|
+
if ((type === null || type === 'custom') &&
|
|
175
|
+
typeof mimeFormat === 'string' &&
|
|
176
|
+
((!fixValue && mimeType.startsWith(mimeFormat)) ||
|
|
177
|
+
(fixValue && mimeType === mimeFormat))) {
|
|
178
|
+
continueLoop = false;
|
|
179
|
+
const result = await this._handleBlob(mimeType, item);
|
|
180
|
+
if (result)
|
|
181
|
+
finalResult.push(result);
|
|
182
|
+
}
|
|
183
|
+
// Text
|
|
184
|
+
else if ((type === null || type === 'text') && mimeType === 'text/plain') {
|
|
185
|
+
continueLoop = false;
|
|
186
|
+
const result = await this._handleText(mimeType, item);
|
|
187
|
+
if (result)
|
|
188
|
+
finalResult.push(result);
|
|
189
|
+
}
|
|
190
|
+
// Blob
|
|
191
|
+
else if (type === null) {
|
|
192
|
+
continueLoop = false;
|
|
193
|
+
const result = await this._handleBlob(mimeType, item);
|
|
194
|
+
if (result)
|
|
195
|
+
finalResult.push(result);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
/** @type {Promise<void>[]} */
|
|
199
|
+
const promises = [];
|
|
200
|
+
/**
|
|
201
|
+
* Read Item
|
|
202
|
+
* @param {ClipboardItem | ClipboardItems} item
|
|
203
|
+
*/
|
|
204
|
+
const readItem = (item) => {
|
|
205
|
+
if (!(item instanceof ClipboardItem))
|
|
206
|
+
throw new Error('Expected ClipboardItem when reading data.');
|
|
207
|
+
for (const tIndex in item.types)
|
|
208
|
+
promises.push(completeTask(item.types[tIndex], item));
|
|
209
|
+
};
|
|
210
|
+
// Specific Item
|
|
211
|
+
if (typeof index === 'number' &&
|
|
212
|
+
!Number.isNaN(index) &&
|
|
213
|
+
Number.isFinite(index) &&
|
|
214
|
+
index > -1) {
|
|
215
|
+
readItem(items);
|
|
216
|
+
Promise.all(promises)
|
|
217
|
+
.then(() => {
|
|
218
|
+
if (finalResult[0])
|
|
219
|
+
resolve(finalResult[0]);
|
|
220
|
+
else
|
|
221
|
+
resolve(null);
|
|
222
|
+
})
|
|
223
|
+
.catch(reject);
|
|
224
|
+
}
|
|
225
|
+
// All
|
|
226
|
+
else if (Array.isArray(items)) {
|
|
227
|
+
for (const tIndex in items)
|
|
228
|
+
readItem(items[tIndex]);
|
|
229
|
+
Promise.all(promises)
|
|
230
|
+
.then(() => resolve(finalResult))
|
|
231
|
+
.catch(reject);
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
// Fail
|
|
235
|
+
.catch(reject);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Read plain text from the clipboard (single item by index).
|
|
240
|
+
*
|
|
241
|
+
* @param {number} [index=0] - The index of the clipboard item to read.
|
|
242
|
+
* @returns {Promise<string|null>} A promise that resolves to the clipboard text or null.
|
|
243
|
+
*/
|
|
244
|
+
async readText(index = 0) {
|
|
245
|
+
const value = await this._readData(index, 'text');
|
|
246
|
+
if (typeof value !== 'string')
|
|
247
|
+
throw new Error('Failed to read text: expected string result.');
|
|
248
|
+
return value;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Read custom clipboard data based on MIME type from a specific index.
|
|
252
|
+
*
|
|
253
|
+
* @param {string|null} [mimeFormat=null] - MIME prefix to match (e.g., "image/").
|
|
254
|
+
* @param {boolean} [fixValue=false] - If true, matches exact MIME instead of prefix.
|
|
255
|
+
* @param {number} [index=0] - Clipboard item index.
|
|
256
|
+
* @returns {Promise<Blob|null>} A promise resolving with a blob or null.
|
|
257
|
+
*/
|
|
258
|
+
async readCustom(mimeFormat = null, fixValue = false, index = 0) {
|
|
259
|
+
const value = await this._readData(index, 'custom', mimeFormat, fixValue);
|
|
260
|
+
if (!(value instanceof Blob))
|
|
261
|
+
throw new Error('Failed to read custom data: expected Blob.');
|
|
262
|
+
return value;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Read all available plain text entries from the clipboard.
|
|
266
|
+
*
|
|
267
|
+
* @returns {Promise<string[]>} A promise resolving to an array of strings or null.
|
|
268
|
+
*/
|
|
269
|
+
async readAllTexts() {
|
|
270
|
+
const values = await this._readData(null, 'text');
|
|
271
|
+
if (!Array.isArray(values))
|
|
272
|
+
throw new Error('Expected array of strings when reading all texts.');
|
|
273
|
+
if (!values.every((value) => typeof value === 'string'))
|
|
274
|
+
throw new Error('Some values returned were not strings.');
|
|
275
|
+
return values;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Read all clipboard data matching a specific custom MIME type.
|
|
279
|
+
*
|
|
280
|
+
* @param {string|null} [mimeFormat=null] - MIME prefix or exact type.
|
|
281
|
+
* @param {boolean} [fixValue=false] - Match prefix or exact MIME.
|
|
282
|
+
* @returns {Promise<Blob[]>} A promise resolving with array of Blobs or null.
|
|
283
|
+
*/
|
|
284
|
+
async readAllCustom(mimeFormat = null, fixValue = false) {
|
|
285
|
+
const values = await this._readData(null, 'custom', mimeFormat, fixValue);
|
|
286
|
+
if (!Array.isArray(values))
|
|
287
|
+
throw new Error('Expected array of blobs when reading all custom items.');
|
|
288
|
+
if (!values.every((value) => value instanceof Blob))
|
|
289
|
+
throw new Error('Some values returned were not Blob instances.');
|
|
290
|
+
return values;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Read all clipboard data as Blob or text depending on type.
|
|
294
|
+
*
|
|
295
|
+
* @param {'text'|'custom'|null} [type=null] - The type of data to retrieve.
|
|
296
|
+
* @param {string|null} [mimeFormat=null] - The MIME type or prefix to match.
|
|
297
|
+
* @returns {Promise<Array<Blob|string>>} A promise resolving with matching data array.
|
|
298
|
+
*/
|
|
299
|
+
async readAllData(type = null, mimeFormat = null) {
|
|
300
|
+
const value = await this._readData(null, type, mimeFormat);
|
|
301
|
+
if (!Array.isArray(value))
|
|
302
|
+
throw new Error('Expected array result when reading all data.');
|
|
303
|
+
return value;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Read clipboard data at a specific index or all if null.
|
|
307
|
+
*
|
|
308
|
+
* @param {number|null} index - Index of the item to retrieve or null to get all.
|
|
309
|
+
* @returns {Promise<ClipboardItem|ClipboardItems|null>} A promise resolving with a clipboard item or array of items.
|
|
310
|
+
*/
|
|
311
|
+
_read(index) {
|
|
312
|
+
return new Promise((resolve, reject) => {
|
|
313
|
+
if (!this.#existNavigator)
|
|
314
|
+
reject(new Error('Clipboard API not found!'));
|
|
315
|
+
navigator.clipboard
|
|
316
|
+
.read()
|
|
317
|
+
.then((items) => {
|
|
318
|
+
// Index is number
|
|
319
|
+
if (typeof index === 'number') {
|
|
320
|
+
if (Number.isNaN(index) || !Number.isFinite(index) || index < 0)
|
|
321
|
+
throw new Error(`Invalid index value: ${index}`);
|
|
322
|
+
if (items[index])
|
|
323
|
+
resolve(items[index]);
|
|
324
|
+
// Not found
|
|
325
|
+
else
|
|
326
|
+
resolve(null);
|
|
327
|
+
}
|
|
328
|
+
// Get All
|
|
329
|
+
resolve(items);
|
|
330
|
+
})
|
|
331
|
+
.catch(reject);
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Read clipboard data at a specific index.
|
|
336
|
+
*
|
|
337
|
+
* @param {number} index - Index of the item to retrieve
|
|
338
|
+
* @returns {Promise<ClipboardItem|null>} A promise resolving with a clipboard item.
|
|
339
|
+
*/
|
|
340
|
+
async readIndex(index) {
|
|
341
|
+
const value = await this._read(index);
|
|
342
|
+
if (value !== null && !(value instanceof ClipboardItem))
|
|
343
|
+
throw new Error(`Value at index ${index} is not a ClipboardItem.`);
|
|
344
|
+
return value;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Read all clipboard content without any filters.
|
|
348
|
+
*
|
|
349
|
+
* @returns {Promise<ClipboardItems>} A promise resolving with all clipboard items.
|
|
350
|
+
*/
|
|
351
|
+
async readAll() {
|
|
352
|
+
const value = await this._read(null);
|
|
353
|
+
if (!Array.isArray(value))
|
|
354
|
+
throw new Error('Expected array result from clipboard read.');
|
|
355
|
+
for (const item of value) {
|
|
356
|
+
if (!(item instanceof ClipboardItem))
|
|
357
|
+
throw new Error('Invalid item type found in clipboard result.');
|
|
358
|
+
}
|
|
359
|
+
return value;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Returns whether the legacy `document.execCommand()` API is available.
|
|
363
|
+
* This can be used to determine if a fallback clipboard method is usable.
|
|
364
|
+
*
|
|
365
|
+
* @returns {boolean} True if `document.execCommand` is available.
|
|
366
|
+
*/
|
|
367
|
+
isExecCommandAvailable() {
|
|
368
|
+
return this.#existExecCommand;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Returns whether the modern Clipboard API (`navigator.clipboard`) is available.
|
|
372
|
+
* Useful to know if full clipboard features can be accessed.
|
|
373
|
+
*
|
|
374
|
+
* @returns {boolean} True if `navigator.clipboard` is available.
|
|
375
|
+
*/
|
|
376
|
+
isNavigatorClipboardAvailable() {
|
|
377
|
+
return this.#existNavigator;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Returns the function used to copy plain text to the clipboard.
|
|
381
|
+
* This function may be built-in or set manually via `setCopyText`.
|
|
382
|
+
*
|
|
383
|
+
* @returns {((text: string) => Promise<void>) | null} The current text copy function or null if unavailable.
|
|
384
|
+
*/
|
|
385
|
+
getCopyTextFunc() {
|
|
386
|
+
return this.#copyText;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Returns the function used to copy Blob (binary data) to the clipboard.
|
|
390
|
+
* This function may be built-in or set manually via `setCopyBlob`.
|
|
391
|
+
*
|
|
392
|
+
* @returns {((blob: Blob) => Promise<void>) | null} The current blob copy function or null if unavailable.
|
|
393
|
+
*/
|
|
394
|
+
getCopyBlobFunc() {
|
|
395
|
+
return this.#copyBlob;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
export default TinyClipboard;
|
|
@@ -94,6 +94,21 @@ const {
|
|
|
94
94
|
* @typedef {Element|Window|Document} ElementAndWinAndDoc
|
|
95
95
|
*/
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Represents a raw DOM element with document or an instance of TinyHtml.
|
|
99
|
+
* This type is used to abstract interactions with both plain elements
|
|
100
|
+
* and wrapped elements via the TinyHtml class.
|
|
101
|
+
*
|
|
102
|
+
* @typedef {ElementWithDoc|TinyHtml} TinyElementWithDoc
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Represents a value that can be either a DOM Element, or the document object.
|
|
107
|
+
* Useful for functions that operate generically on measurable targets.
|
|
108
|
+
*
|
|
109
|
+
* @typedef {Element|Document} ElementWithDoc
|
|
110
|
+
*/
|
|
111
|
+
|
|
97
112
|
/**
|
|
98
113
|
* A parameter type used for filtering or matching elements.
|
|
99
114
|
* It can be:
|
|
@@ -113,12 +128,6 @@ const {
|
|
|
113
128
|
* @typedef {Window|Element|Document|Text} ConstructorElValues
|
|
114
129
|
*/
|
|
115
130
|
|
|
116
|
-
/**
|
|
117
|
-
* The handler function used in event listeners.
|
|
118
|
-
*
|
|
119
|
-
* @typedef {(e: Event) => any} EventRegistryHandle
|
|
120
|
-
*/
|
|
121
|
-
|
|
122
131
|
/**
|
|
123
132
|
* Options passed to `addEventListener` or `removeEventListener`.
|
|
124
133
|
* Can be a boolean or an object of type `AddEventListenerOptions`.
|
|
@@ -130,7 +139,7 @@ const {
|
|
|
130
139
|
* Structure describing a registered event callback and its options.
|
|
131
140
|
*
|
|
132
141
|
* @typedef {Object} EventRegistryItem
|
|
133
|
-
* @property {
|
|
142
|
+
* @property {EventListenerOrEventListenerObject|null} handler - The function to be executed when the event is triggered.
|
|
134
143
|
* @property {EventRegistryOptions} [options] - Optional configuration passed to the listener.
|
|
135
144
|
*/
|
|
136
145
|
|
|
@@ -689,9 +698,9 @@ class TinyHtml {
|
|
|
689
698
|
* Ensures the input is returned as an array.
|
|
690
699
|
* Useful to normalize operations across multiple or single element/window/document elements.
|
|
691
700
|
*
|
|
692
|
-
* @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/window element or array of html elements.
|
|
701
|
+
* @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/document/window element or array of html elements.
|
|
693
702
|
* @param {string} where - The method or context name where validation is being called.
|
|
694
|
-
* @returns {ElementAndWindow[]} - Always returns an array of element/window elements.
|
|
703
|
+
* @returns {ElementAndWindow[]} - Always returns an array of element/document/window elements.
|
|
695
704
|
* @readonly
|
|
696
705
|
*/
|
|
697
706
|
static _preElemsAndWinAndDoc(elems, where) {
|
|
@@ -708,9 +717,9 @@ class TinyHtml {
|
|
|
708
717
|
* Ensures the input is returned as an single element/window/document element.
|
|
709
718
|
* Useful to normalize operations across multiple or single element/window/document elements.
|
|
710
719
|
*
|
|
711
|
-
* @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/window element or array of html elements.
|
|
720
|
+
* @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/document/window element or array of html elements.
|
|
712
721
|
* @param {string} where - The method or context name where validation is being called.
|
|
713
|
-
* @returns {ElementAndWindow} - Always returns an single element/window element.
|
|
722
|
+
* @returns {ElementAndWindow} - Always returns an single element/document/window element.
|
|
714
723
|
* @readonly
|
|
715
724
|
*/
|
|
716
725
|
static _preElemAndWinAndDoc(elems, where) {
|
|
@@ -724,6 +733,32 @@ class TinyHtml {
|
|
|
724
733
|
return result;
|
|
725
734
|
}
|
|
726
735
|
|
|
736
|
+
/**
|
|
737
|
+
* Ensures the input is returned as an array.
|
|
738
|
+
* Useful to normalize operations across multiple or single element with document elements.
|
|
739
|
+
*
|
|
740
|
+
* @param {TinyElementWithDoc|TinyElementWithDoc[]} elems - A single element with document element or array of html elements.
|
|
741
|
+
* @param {string} where - The method or context name where validation is being called.
|
|
742
|
+
* @returns {ElementWithDoc[]} - Always returns an array of element with document elements.
|
|
743
|
+
* @readonly
|
|
744
|
+
*/
|
|
745
|
+
static _preElemsWithDoc(elems, where) {
|
|
746
|
+
return TinyHtml._preElemsTemplate(elems, where, [Element, Document], ['Element', 'Document']);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Ensures the input is returned as an single element with document element.
|
|
751
|
+
* Useful to normalize operations across multiple or single element with document elements.
|
|
752
|
+
*
|
|
753
|
+
* @param {TinyElementWithDoc|TinyElementWithDoc[]} elems - A single element/window element or array of html elements.
|
|
754
|
+
* @param {string} where - The method or context name where validation is being called.
|
|
755
|
+
* @returns {ElementWithDoc} - Always returns an single element/window element.
|
|
756
|
+
* @readonly
|
|
757
|
+
*/
|
|
758
|
+
static _preElemWithDoc(elems, where) {
|
|
759
|
+
return TinyHtml._preElemTemplate(elems, where, [Element, Document], ['Element', 'Document']);
|
|
760
|
+
}
|
|
761
|
+
|
|
727
762
|
/**
|
|
728
763
|
* Normalizes and converts one or more DOM elements (or TinyHtml instances)
|
|
729
764
|
* into an array of `TinyHtml` instances.
|
|
@@ -3983,12 +4018,120 @@ class TinyHtml {
|
|
|
3983
4018
|
|
|
3984
4019
|
////////////////////////////////////////////
|
|
3985
4020
|
|
|
4021
|
+
/**
|
|
4022
|
+
* Registers a listener for the "paste" event to extract files and text from the clipboard (e.g., when the user presses Ctrl+V).
|
|
4023
|
+
*
|
|
4024
|
+
* This method allows reacting to pasted content by providing separate callbacks for files and plain text.
|
|
4025
|
+
* Useful for building file upload areas, rich-text editors, or input enhancements.
|
|
4026
|
+
*
|
|
4027
|
+
* @param {TinyElementWithDoc|TinyElementWithDoc[]} el - The target element(s) where the "paste" event will be listened.
|
|
4028
|
+
* @param {Object} [settings={}] - Optional callbacks to handle clipboard content.
|
|
4029
|
+
* @param {(data: DataTransferItem, file: File) => void} [settings.onFilePaste] - Called for each file pasted from the clipboard (e.g., images).
|
|
4030
|
+
* @param {(data: DataTransferItem, text: string) => void} [settings.onTextPaste] - Called when plain text is pasted from the clipboard.
|
|
4031
|
+
* @returns {EventListenerOrEventListenerObject} The internal "paste" event handler used.
|
|
4032
|
+
*/
|
|
4033
|
+
static listenForPaste(el, { onFilePaste, onTextPaste } = {}) {
|
|
4034
|
+
if (typeof onFilePaste !== 'undefined' && typeof onFilePaste !== 'function')
|
|
4035
|
+
throw new TypeError('onFilePaste must be a function.');
|
|
4036
|
+
if (typeof onTextPaste !== 'undefined' && typeof onTextPaste !== 'function')
|
|
4037
|
+
throw new TypeError('onTextPaste must be a function.');
|
|
4038
|
+
|
|
4039
|
+
/** @type {EventListenerOrEventListenerObject} */
|
|
4040
|
+
const pasteEvent = (event) => {
|
|
4041
|
+
if (!(event instanceof ClipboardEvent)) return;
|
|
4042
|
+
const items = event.clipboardData?.items || [];
|
|
4043
|
+
for (const item of items) {
|
|
4044
|
+
if (item.kind === 'file') {
|
|
4045
|
+
if (typeof onFilePaste === 'function') {
|
|
4046
|
+
const file = item.getAsFile();
|
|
4047
|
+
if (file) onFilePaste(item, file);
|
|
4048
|
+
}
|
|
4049
|
+
} else if (item.kind === 'string') {
|
|
4050
|
+
if (typeof onTextPaste === 'function')
|
|
4051
|
+
item.getAsString((text) => onTextPaste(item, text));
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
};
|
|
4055
|
+
|
|
4056
|
+
TinyHtml._preElemsWithDoc(el, 'listenForPaste').forEach((elem) =>
|
|
4057
|
+
TinyHtml.on(elem, 'paste', pasteEvent),
|
|
4058
|
+
);
|
|
4059
|
+
return pasteEvent;
|
|
4060
|
+
}
|
|
4061
|
+
|
|
4062
|
+
/**
|
|
4063
|
+
* Registers a listener for the "paste" event to extract files and text from the clipboard (e.g., when the user presses Ctrl+V).
|
|
4064
|
+
*
|
|
4065
|
+
* This method allows reacting to pasted content by providing separate callbacks for files and plain text.
|
|
4066
|
+
* Useful for building file upload areas, rich-text editors, or input enhancements.
|
|
4067
|
+
*
|
|
4068
|
+
* @param {Object} [settings={}] - Optional callbacks to handle clipboard content.
|
|
4069
|
+
* @param {(data: DataTransferItem, file: File) => void} [settings.onFilePaste] - Called for each file pasted from the clipboard (e.g., images).
|
|
4070
|
+
* @param {(data: DataTransferItem, text: string) => void} [settings.onTextPaste] - Called when plain text is pasted from the clipboard.
|
|
4071
|
+
* @returns {EventListenerOrEventListenerObject} The internal "paste" event handler used.
|
|
4072
|
+
*/
|
|
4073
|
+
listenForPaste({ onFilePaste, onTextPaste } = {}) {
|
|
4074
|
+
return TinyHtml.listenForPaste(this, { onFilePaste, onTextPaste });
|
|
4075
|
+
}
|
|
4076
|
+
|
|
4077
|
+
/**
|
|
4078
|
+
* Checks if the element has a listener for a specific event.
|
|
4079
|
+
*
|
|
4080
|
+
* @param {TinyEventTarget} el - The element to check.
|
|
4081
|
+
* @param {string} event - The event name to check.
|
|
4082
|
+
* @returns {boolean}
|
|
4083
|
+
*/
|
|
4084
|
+
static hasEventListener(el, event) {
|
|
4085
|
+
const elem = TinyHtml._preEventTargetElem(el, 'hasEventListener');
|
|
4086
|
+
if (!__eventRegistry.has(elem)) return false;
|
|
4087
|
+
const events = __eventRegistry.get(elem);
|
|
4088
|
+
return !!(events && Array.isArray(events[event]) && events[event].length > 0);
|
|
4089
|
+
}
|
|
4090
|
+
|
|
4091
|
+
/**
|
|
4092
|
+
* Checks if the element has a listener for a specific event.
|
|
4093
|
+
*
|
|
4094
|
+
* @param {string} event - The event name to check.
|
|
4095
|
+
* @returns {boolean}
|
|
4096
|
+
*/
|
|
4097
|
+
hasEventListener(event) {
|
|
4098
|
+
return TinyHtml.hasEventListener(this, event);
|
|
4099
|
+
}
|
|
4100
|
+
|
|
4101
|
+
/**
|
|
4102
|
+
* Checks if the element has the exact handler registered for a specific event.
|
|
4103
|
+
*
|
|
4104
|
+
* @param {TinyEventTarget} el - The element to check.
|
|
4105
|
+
* @param {string} event - The event name to check.
|
|
4106
|
+
* @param {EventListenerOrEventListenerObject} handler - The handler function to check.
|
|
4107
|
+
* @returns {boolean}
|
|
4108
|
+
*/
|
|
4109
|
+
static hasExactEventListener(el, event, handler) {
|
|
4110
|
+
const elem = TinyHtml._preEventTargetElem(el, 'hasExactEventListener');
|
|
4111
|
+
if (typeof handler !== 'function') throw new TypeError('The "handler" must be a function.');
|
|
4112
|
+
if (!__eventRegistry.has(elem)) return false;
|
|
4113
|
+
const events = __eventRegistry.get(elem);
|
|
4114
|
+
if (!events || !Array.isArray(events[event])) return false;
|
|
4115
|
+
return events[event].some((item) => item.handler === handler);
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
/**
|
|
4119
|
+
* Checks if the element has the exact handler registered for a specific event.
|
|
4120
|
+
*
|
|
4121
|
+
* @param {string} event - The event name to check.
|
|
4122
|
+
* @param {EventListenerOrEventListenerObject} handler - The handler function to check.
|
|
4123
|
+
* @returns {boolean}
|
|
4124
|
+
*/
|
|
4125
|
+
hasExactEventListener(event, handler) {
|
|
4126
|
+
return TinyHtml.hasExactEventListener(this, event, handler);
|
|
4127
|
+
}
|
|
4128
|
+
|
|
3986
4129
|
/**
|
|
3987
4130
|
* Registers an event listener on the specified element.
|
|
3988
4131
|
*
|
|
3989
4132
|
* @param {TinyEventTarget|TinyEventTarget[]} el - The target to listen on.
|
|
3990
4133
|
* @param {string} event - The event type (e.g. 'click', 'keydown').
|
|
3991
|
-
* @param {
|
|
4134
|
+
* @param {EventListenerOrEventListenerObject|null} handler - The callback function to run on event.
|
|
3992
4135
|
* @param {EventRegistryOptions} [options] - Optional event listener options.
|
|
3993
4136
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
3994
4137
|
*/
|
|
@@ -4010,7 +4153,7 @@ class TinyHtml {
|
|
|
4010
4153
|
* Registers an event listener on the specified element.
|
|
4011
4154
|
*
|
|
4012
4155
|
* @param {string} event - The event type (e.g. 'click', 'keydown').
|
|
4013
|
-
* @param {
|
|
4156
|
+
* @param {EventListenerOrEventListenerObject|null} handler - The callback function to run on event.
|
|
4014
4157
|
* @param {EventRegistryOptions} [options] - Optional event listener options.
|
|
4015
4158
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
4016
4159
|
*/
|
|
@@ -4023,17 +4166,17 @@ class TinyHtml {
|
|
|
4023
4166
|
*
|
|
4024
4167
|
* @param {TinyEventTarget|TinyEventTarget[]} el - The target to listen on.
|
|
4025
4168
|
* @param {string} event - The event type (e.g. 'click', 'keydown').
|
|
4026
|
-
* @param {
|
|
4169
|
+
* @param {EventListenerOrEventListenerObject} handler - The callback function to run on event.
|
|
4027
4170
|
* @param {EventRegistryOptions} [options={}] - Optional event listener options.
|
|
4028
4171
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
4029
4172
|
*/
|
|
4030
4173
|
static once(el, event, handler, options = {}) {
|
|
4031
4174
|
if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
|
|
4032
4175
|
TinyHtml._preEventTargetElems(el, 'once').forEach((elem) => {
|
|
4033
|
-
/** @type {
|
|
4176
|
+
/** @type {EventListenerOrEventListenerObject} */
|
|
4034
4177
|
const wrapped = (e) => {
|
|
4035
4178
|
TinyHtml.off(elem, event, wrapped);
|
|
4036
|
-
handler(e);
|
|
4179
|
+
if (typeof handler === 'function') handler(e);
|
|
4037
4180
|
};
|
|
4038
4181
|
|
|
4039
4182
|
TinyHtml.on(
|
|
@@ -4050,7 +4193,7 @@ class TinyHtml {
|
|
|
4050
4193
|
* Registers an event listener that runs only once, then is removed.
|
|
4051
4194
|
*
|
|
4052
4195
|
* @param {string} event - The event type (e.g. 'click', 'keydown').
|
|
4053
|
-
* @param {
|
|
4196
|
+
* @param {EventListenerOrEventListenerObject} handler - The callback function to run on event.
|
|
4054
4197
|
* @param {EventRegistryOptions} [options={}] - Optional event listener options.
|
|
4055
4198
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
4056
4199
|
*/
|
|
@@ -4063,7 +4206,7 @@ class TinyHtml {
|
|
|
4063
4206
|
*
|
|
4064
4207
|
* @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
|
|
4065
4208
|
* @param {string} event - The event type.
|
|
4066
|
-
* @param {
|
|
4209
|
+
* @param {EventListenerOrEventListenerObject|null} handler - The function originally bound to the event.
|
|
4067
4210
|
* @param {boolean|EventListenerOptions} [options] - Optional listener options.
|
|
4068
4211
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
4069
4212
|
*/
|
|
@@ -4085,7 +4228,7 @@ class TinyHtml {
|
|
|
4085
4228
|
* Removes a specific event listener from an element.
|
|
4086
4229
|
*
|
|
4087
4230
|
* @param {string} event - The event type.
|
|
4088
|
-
* @param {
|
|
4231
|
+
* @param {EventListenerOrEventListenerObject|null} handler - The function originally bound to the event.
|
|
4089
4232
|
* @param {boolean|EventListenerOptions} [options] - Optional listener options.
|
|
4090
4233
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
4091
4234
|
*/
|
|
@@ -4128,7 +4271,7 @@ class TinyHtml {
|
|
|
4128
4271
|
* Removes all event listeners of all types from the element.
|
|
4129
4272
|
*
|
|
4130
4273
|
* @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
|
|
4131
|
-
* @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
|
|
4274
|
+
* @param {((handler: EventListenerOrEventListenerObject|null, event: string) => boolean)|null} [filterFn=null] -
|
|
4132
4275
|
* Optional filter function to selectively remove specific handlers.
|
|
4133
4276
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
4134
4277
|
*/
|
|
@@ -4155,7 +4298,7 @@ class TinyHtml {
|
|
|
4155
4298
|
/**
|
|
4156
4299
|
* Removes all event listeners of all types from the element.
|
|
4157
4300
|
*
|
|
4158
|
-
* @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
|
|
4301
|
+
* @param {((handler: EventListenerOrEventListenerObject|null, event: string) => boolean)|null} [filterFn=null] -
|
|
4159
4302
|
* Optional filter function to selectively remove specific handlers.
|
|
4160
4303
|
* @returns {TinyEventTarget|TinyEventTarget[]}
|
|
4161
4304
|
*/
|