tiny-essentials 1.14.0 → 1.16.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.
Files changed (41) hide show
  1. package/dist/v1/TinyBasicsEs.js +4585 -270
  2. package/dist/v1/TinyBasicsEs.min.js +1 -1
  3. package/dist/v1/TinyDragger.js +4386 -471
  4. package/dist/v1/TinyDragger.min.js +1 -1
  5. package/dist/v1/TinyEssentials.js +4634 -242
  6. package/dist/v1/TinyEssentials.min.js +1 -1
  7. package/dist/v1/TinyHtml.js +4327 -0
  8. package/dist/v1/TinyHtml.min.js +1 -0
  9. package/dist/v1/TinyUploadClicker.js +4628 -246
  10. package/dist/v1/TinyUploadClicker.min.js +1 -1
  11. package/dist/v1/basics/collision.cjs +413 -0
  12. package/dist/v1/basics/collision.d.mts +187 -0
  13. package/dist/v1/basics/collision.mjs +350 -0
  14. package/dist/v1/basics/html.cjs +5 -109
  15. package/dist/v1/basics/html.d.mts +2 -28
  16. package/dist/v1/basics/html.mjs +5 -91
  17. package/dist/v1/basics/html_deprecated.cjs +124 -0
  18. package/dist/v1/basics/html_deprecated.d.mts +40 -0
  19. package/dist/v1/basics/html_deprecated.mjs +97 -0
  20. package/dist/v1/basics/index.cjs +27 -5
  21. package/dist/v1/basics/index.d.mts +26 -6
  22. package/dist/v1/basics/index.mjs +4 -2
  23. package/dist/v1/build/TinyHtml.cjs +7 -0
  24. package/dist/v1/build/TinyHtml.d.mts +3 -0
  25. package/dist/v1/build/TinyHtml.mjs +2 -0
  26. package/dist/v1/index.cjs +29 -5
  27. package/dist/v1/index.d.mts +27 -6
  28. package/dist/v1/index.mjs +5 -2
  29. package/dist/v1/libs/TinyDragger.cjs +91 -16
  30. package/dist/v1/libs/TinyDragger.d.mts +78 -2
  31. package/dist/v1/libs/TinyDragger.mjs +93 -17
  32. package/dist/v1/libs/TinyHtml.cjs +3859 -0
  33. package/dist/v1/libs/TinyHtml.d.mts +2151 -0
  34. package/dist/v1/libs/TinyHtml.mjs +3457 -0
  35. package/docs/v1/README.md +2 -0
  36. package/docs/v1/basics/collision.md +237 -0
  37. package/docs/v1/basics/html.md +1 -105
  38. package/docs/v1/basics/html_deprecated.md +127 -0
  39. package/docs/v1/libs/TinyDragger.md +45 -15
  40. package/docs/v1/libs/TinyHtml.md +1333 -0
  41. package/package.json +8 -2
@@ -0,0 +1,3859 @@
1
+ 'use strict';
2
+
3
+ var collision = require('../basics/collision.cjs');
4
+
5
+ const {
6
+ areElsColliding,
7
+ areElsPerfColliding,
8
+ areElsCollTop,
9
+ areElsCollBottom,
10
+ areElsCollLeft,
11
+ areElsCollRight,
12
+ } = collision;
13
+
14
+ /**
15
+ * Represents a raw Node element or an instance of TinyHtml.
16
+ * This type is used to abstract interactions with both plain elements
17
+ * and wrapped elements via the TinyHtml class.
18
+ *
19
+ * @typedef {Node|TinyHtml|null} TinyNode
20
+ */
21
+
22
+ /**
23
+ * Represents a raw DOM element or an instance of TinyHtml.
24
+ * This type is used to abstract interactions with both plain elements
25
+ * and wrapped elements via the TinyHtml class.
26
+ *
27
+ * @typedef {Element|TinyHtml} TinyElement
28
+ */
29
+
30
+ /**
31
+ * Represents a raw DOM html element or an instance of TinyHtml.
32
+ * This type is used to abstract interactions with both plain elements
33
+ * and wrapped elements via the TinyHtml class.
34
+ *
35
+ * @typedef {HTMLElement|TinyHtml} TinyHtmlElement
36
+ */
37
+
38
+ /**
39
+ * Represents a raw DOM event target element or an instance of TinyHtml.
40
+ * This type is used to abstract interactions with both plain elements
41
+ * and wrapped elements via the TinyHtml class.
42
+ *
43
+ * @typedef {EventTarget|TinyHtml} TinyEventTarget
44
+ */
45
+
46
+ /**
47
+ * Represents a raw DOM input element or an instance of TinyHtml.
48
+ * This type is used to abstract interactions with both plain elements
49
+ * and wrapped elements via the TinyHtml class.
50
+ *
51
+ * @typedef {InputElement|TinyHtml} TinyInputElement
52
+ */
53
+
54
+ /**
55
+ * Represents a raw DOM element/window or an instance of TinyHtml.
56
+ * This type is used to abstract interactions with both plain elements
57
+ * and wrapped elements via the TinyHtml class.
58
+ *
59
+ * @typedef {ElementAndWindow|TinyHtml} TinyElementAndWindow
60
+ */
61
+
62
+ /**
63
+ * Represents a value that can be either a DOM Element or the global Window object.
64
+ * Useful for functions that operate generically on scrollable or measurable targets.
65
+ *
66
+ * @typedef {Element|Window} ElementAndWindow
67
+ */
68
+
69
+ /**
70
+ * A parameter type used for filtering or matching elements.
71
+ * It can be:
72
+ * - A string (CSS selector),
73
+ * - A raw DOM element,
74
+ * - An array of raw DOM elements,
75
+ * - A filtering function that receives an index and element,
76
+ * and returns true if it matches.
77
+ *
78
+ * @typedef {string|Element|Element[]|((index: number, el: Element) => boolean)} WinnowRequest
79
+ */
80
+
81
+ /**
82
+ * Elements accepted as constructor values for TinyHtml.
83
+ * These include common DOM elements and root containers.
84
+ *
85
+ * @typedef {Window|Element|Document} ConstructorElValues
86
+ */
87
+
88
+ /**
89
+ * The handler function used in event listeners.
90
+ *
91
+ * @typedef {(e: Event) => any} EventRegistryHandle
92
+ */
93
+
94
+ /**
95
+ * Options passed to `addEventListener` or `removeEventListener`.
96
+ * Can be a boolean or an object of type `AddEventListenerOptions`.
97
+ *
98
+ * @typedef {boolean|AddEventListenerOptions} EventRegistryOptions
99
+ */
100
+
101
+ /**
102
+ * Structure describing a registered event callback and its options.
103
+ *
104
+ * @typedef {Object} EventRegistryItem
105
+ * @property {EventRegistryHandle} handler - The function to be executed when the event is triggered.
106
+ * @property {EventRegistryOptions} [options] - Optional configuration passed to the listener.
107
+ */
108
+
109
+ /**
110
+ * Maps event names (e.g., `"click"`, `"keydown"`) to a list of registered handlers and options.
111
+ *
112
+ * @typedef {Record<string, EventRegistryItem[]>} EventRegistryList
113
+ */
114
+
115
+ /**
116
+ * WeakMap storing all event listeners per element.
117
+ * Each element has a registry mapping event names to their handler lists.
118
+ *
119
+ * @type {WeakMap<ConstructorElValues|EventTarget, EventRegistryList>}
120
+ */
121
+ const __eventRegistry = new WeakMap();
122
+
123
+ /**
124
+ * A key-value store associated with a specific DOM element.
125
+ * Keys are strings, and values can be of any type.
126
+ *
127
+ * @typedef {Record<string, *>} ElementDataStore
128
+ */
129
+
130
+ /**
131
+ * WeakMap to hold private data for elements
132
+ *
133
+ * @type {WeakMap<ConstructorElValues, ElementDataStore>}
134
+ */
135
+ const __elementDataMap = new WeakMap();
136
+
137
+ /**
138
+ * Stores directional collision locks separately.
139
+ * Each direction has its own WeakMap to allow independent locking.
140
+ *
141
+ * @type {{
142
+ * top: WeakMap<Element, true>,
143
+ * bottom: WeakMap<Element, true>,
144
+ * left: WeakMap<Element, true>,
145
+ * right: WeakMap<Element, true>
146
+ * }}
147
+ */
148
+ const __elemCollision = {
149
+ top: new WeakMap(),
150
+ bottom: new WeakMap(),
151
+ left: new WeakMap(),
152
+ right: new WeakMap(),
153
+ };
154
+
155
+ /**
156
+ * Possible directions from which a collision was detected and locked.
157
+ *
158
+ * @typedef {'top'|'bottom'|'left'|'right'} CollisionDirLock
159
+ */
160
+
161
+ /**
162
+ * @typedef {Object} HtmlElBoxSides
163
+ * @property {number} x - Total horizontal size (left + right)
164
+ * @property {number} y - Total vertical size (top + bottom)
165
+ * @property {number} left
166
+ * @property {number} right
167
+ * @property {number} top
168
+ * @property {number} bottom
169
+ */
170
+
171
+ /**
172
+ * @typedef {string | number | Date | boolean | null} SetValueBase - Primitive types accepted as input values.
173
+ */
174
+
175
+ /**
176
+ * @typedef {'string' | 'date' | 'number'} GetValueTypes
177
+ * Types of value extractors supported by TinyHtml._valTypes.
178
+ */
179
+
180
+ /**
181
+ * @typedef {SetValueBase|SetValueBase[]} SetValueList - A single value or an array of values to be assigned to the input element.
182
+ */
183
+
184
+ /**
185
+ * A list of HTML form elements that can have a `.value` property used by TinyHtml.
186
+ * Includes common input types used in forms.
187
+ *
188
+ * @typedef {HTMLInputElement|HTMLSelectElement|HTMLTextAreaElement|HTMLOptionElement} InputElement
189
+ */
190
+
191
+ /**
192
+ * TinyHtml is a utility class that provides static and instance-level methods
193
+ * for precise dimension and position computations on HTML elements.
194
+ * It mimics some jQuery functionalities while using native browser APIs.
195
+ *
196
+ * Inspired by the jQuery project's open source implementations of element dimension
197
+ * and offset utilities. This class serves as a lightweight alternative using modern DOM APIs.
198
+ *
199
+ * @class
200
+ */
201
+ class TinyHtml {
202
+ /** @typedef {import('../basics/collision.mjs').ObjRect} ObjRect */
203
+
204
+ static Utils = { ...collision };
205
+
206
+ /**
207
+ * Queries the document for the first element matching the CSS selector and wraps it in a TinyHtml instance.
208
+ *
209
+ * @param {string} selector - A valid CSS selector string.
210
+ * @returns {TinyHtml} A TinyHtml instance wrapping the matched element.
211
+ * @throws {Error} If no element matches the selector.
212
+ */
213
+ static query(selector) {
214
+ const newEl = document.querySelector(selector);
215
+ if (!newEl) throw new Error(`[TinyHtml] query(): No element found for selector "${selector}".`);
216
+ return new TinyHtml(newEl);
217
+ }
218
+
219
+ /**
220
+ * Queries the document for all elements matching the CSS selector and wraps them in TinyHtml instances.
221
+ *
222
+ * @param {string} selector - A valid CSS selector string.
223
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the matched elements.
224
+ */
225
+ static queryAll(selector) {
226
+ const newEls = document.querySelectorAll(selector);
227
+ return TinyHtml.toTinyElm([...newEls]);
228
+ }
229
+
230
+ /**
231
+ * Retrieves an element by its ID and wraps it in a TinyHtml instance.
232
+ *
233
+ * @param {string} selector - The ID of the element to retrieve.
234
+ * @returns {TinyHtml} A TinyHtml instance wrapping the found element.
235
+ * @throws {Error} If no element is found with the specified ID.
236
+ */
237
+ static getById(selector) {
238
+ const newEl = document.getElementById(selector);
239
+ if (!newEl) throw new Error(`[TinyHtml] getById(): No element found with ID "${selector}".`);
240
+ return new TinyHtml(newEl);
241
+ }
242
+
243
+ /**
244
+ * Retrieves all elements with the specified class name and wraps them in TinyHtml instances.
245
+ *
246
+ * @param {string} selector - The class name to search for.
247
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
248
+ */
249
+ static getByClassName(selector) {
250
+ const newEls = document.getElementsByClassName(selector);
251
+ return TinyHtml.toTinyElm([...newEls]);
252
+ }
253
+
254
+ /**
255
+ * Retrieves all elements with the specified name attribute and wraps them in TinyHtml instances.
256
+ *
257
+ * @param {string} selector - The name attribute to search for.
258
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
259
+ */
260
+ static getByName(selector) {
261
+ const newEls = document.getElementsByName(selector);
262
+ return TinyHtml.toTinyElm([...newEls]);
263
+ }
264
+
265
+ /**
266
+ * Retrieves all elements with the specified local tag name within the given namespace URI,
267
+ * and wraps them in TinyHtml instances.
268
+ *
269
+ * @param {string} localName - The local name (tag) of the elements to search for.
270
+ * @param {string|null} [namespaceURI='http://www.w3.org/1999/xhtml'] - The namespace URI to search within.
271
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
272
+ */
273
+ static getByTagNameNS(localName, namespaceURI = 'http://www.w3.org/1999/xhtml') {
274
+ const newEls = document.getElementsByTagNameNS(namespaceURI, localName);
275
+ return TinyHtml.toTinyElm([...newEls]);
276
+ }
277
+
278
+ //////////////////////////////////////////////////////////////////
279
+
280
+ /**
281
+ * Returns the current target held by this instance.
282
+ *
283
+ * @returns {ConstructorElValues} - The instance's target element.
284
+ */
285
+ get() {
286
+ return this.#el;
287
+ }
288
+
289
+ /**
290
+ * Returns the current Element held by this instance.
291
+ *
292
+ * @param {string} where - The method name or context calling this.
293
+ * @returns {ConstructorElValues} - The instance's element.
294
+ * @readonly
295
+ */
296
+ _getElement(where) {
297
+ if (
298
+ !(this.#el instanceof Element) &&
299
+ !(this.#el instanceof Window) &&
300
+ !(this.#el instanceof Document)
301
+ )
302
+ throw new Error(`[TinyHtml] Invalid Element in ${where}().`);
303
+ return this.#el;
304
+ }
305
+
306
+ //////////////////////////////////////////////////////
307
+
308
+ /**
309
+ * @param {TinyElement|EventTarget|null|(TinyElement|EventTarget|null)[]} elems
310
+ * @param {string} where
311
+ * @param {any[]} TheTinyElements
312
+ * @param {string[]} elemName
313
+ * @returns {any[]}
314
+ * @readonly
315
+ */
316
+ static _preElemsTemplate(elems, where, TheTinyElements, elemName) {
317
+ /** @param {(TinyElement|EventTarget|null)[]} item */
318
+ const checkElement = (item) =>
319
+ item.map((elem) => {
320
+ const result = elem instanceof TinyHtml ? elem._getElement(where) : elem;
321
+ let allowed = false;
322
+ for (const TheTinyElement of TheTinyElements) {
323
+ if (result instanceof TheTinyElement) {
324
+ allowed = true;
325
+ break;
326
+ }
327
+ }
328
+ if (!allowed)
329
+ throw new Error(
330
+ `[TinyHtml] Invalid element of the list "${elemName.join(',')}" in ${where}().`,
331
+ );
332
+ return result;
333
+ });
334
+ if (!Array.isArray(elems)) return checkElement([elems]);
335
+ return checkElement(elems);
336
+ }
337
+
338
+ /**
339
+ * @param {TinyElement|EventTarget|null|(TinyElement|EventTarget|null)[]} elems
340
+ * @param {string} where
341
+ * @param {any[]} TheTinyElements
342
+ * @param {string[]} elemName
343
+ * @param {boolean} [canNull=false]
344
+ * @returns {any}
345
+ * @readonly
346
+ */
347
+ static _preElemTemplate(elems, where, TheTinyElements, elemName, canNull = false) {
348
+ /** @param {(TinyElement|EventTarget|null)[]} item */
349
+ const checkElement = (item) => {
350
+ const elem = item[0];
351
+ let result = elem instanceof TinyHtml ? elem._getElement(where) : elem;
352
+ let allowed = false;
353
+ for (const TheTinyElement of TheTinyElements) {
354
+ if (result instanceof TheTinyElement) {
355
+ allowed = true;
356
+ break;
357
+ }
358
+ }
359
+
360
+ if (canNull && (result === null || typeof result === 'undefined')) {
361
+ result = null;
362
+ allowed = true;
363
+ }
364
+
365
+ if (!allowed)
366
+ throw new Error(
367
+ `[TinyHtml] Invalid element of the list "${elemName.join(',')}" in ${where}().`,
368
+ );
369
+ return result;
370
+ };
371
+ if (!Array.isArray(elems)) return checkElement([elems]);
372
+ if (elems.length > 1)
373
+ throw new Error(
374
+ `[TinyHtml] Invalid element amount in ${where}() (Received ${elems.length}/1).`,
375
+ );
376
+ return checkElement(elems);
377
+ }
378
+
379
+ /**
380
+ * Ensures the input is returned as an array.
381
+ * Useful to normalize operations across multiple or single elements.
382
+ *
383
+ * @param {TinyElement|TinyElement[]} elems - A single element or array of elements.
384
+ * @param {string} where - The method or context name where validation is being called.
385
+ * @returns {Element[]} - Always returns an array of elements.
386
+ * @readonly
387
+ */
388
+ static _preElems(elems, where) {
389
+ return TinyHtml._preElemsTemplate(elems, where, [Element], ['Element']);
390
+ }
391
+
392
+ /**
393
+ * Ensures the input is returned as an single element.
394
+ * Useful to normalize operations across multiple or single elements.
395
+ *
396
+ * @param {TinyElement|TinyElement[]} elems - A single element or array of elements.
397
+ * @param {string} where - The method or context name where validation is being called.
398
+ * @returns {Element} - Always returns an single element.
399
+ * @readonly
400
+ */
401
+ static _preElem(elems, where) {
402
+ return TinyHtml._preElemTemplate(elems, where, [Element], ['Element']);
403
+ }
404
+
405
+ /**
406
+ * Ensures the input is returned as an array.
407
+ * Useful to normalize operations across multiple or single nodes.
408
+ *
409
+ * @param {TinyNode|TinyNode[]} elems - A single node or array of nodes.
410
+ * @param {string} where - The method or context name where validation is being called.
411
+ * @returns {Node[]} - Always returns an array of nodes.
412
+ * @readonly
413
+ */
414
+ static _preNodeElems(elems, where) {
415
+ return TinyHtml._preElemsTemplate(elems, where, [Node], ['Node']);
416
+ }
417
+
418
+ /**
419
+ * Ensures the input is returned as an single node.
420
+ * Useful to normalize operations across multiple or single nodes.
421
+ *
422
+ * @param {TinyNode|TinyNode[]} elems - A single node or array of nodes.
423
+ * @param {string} where - The method or context name where validation is being called.
424
+ * @returns {Node} - Always returns an single node.
425
+ * @readonly
426
+ */
427
+ static _preNodeElem(elems, where) {
428
+ return TinyHtml._preElemTemplate(elems, where, [Node], ['Node']);
429
+ }
430
+
431
+ /**
432
+ * Ensures the input is returned as an single node.
433
+ * Useful to normalize operations across multiple or single nodes.
434
+ *
435
+ * @param {TinyNode|TinyNode[]} elems - A single node or array of nodes.
436
+ * @param {string} where - The method or context name where validation is being called.
437
+ * @returns {Node|null} - Always returns an single node or null.
438
+ * @readonly
439
+ */
440
+ static _preNodeElemWithNull(elems, where) {
441
+ return TinyHtml._preElemTemplate(elems, where, [Node], ['Node'], true);
442
+ }
443
+
444
+ /**
445
+ * Ensures the input is returned as an array.
446
+ * Useful to normalize operations across multiple or single html elements.
447
+ *
448
+ * @param {TinyElement|TinyElement[]} elems - A single html element or array of html elements.
449
+ * @param {string} where - The method or context name where validation is being called.
450
+ * @returns {HTMLElement[]} - Always returns an array of html elements.
451
+ * @readonly
452
+ */
453
+ static _preHtmlElems(elems, where) {
454
+ return TinyHtml._preElemsTemplate(elems, where, [HTMLElement], ['HTMLElement']);
455
+ }
456
+
457
+ /**
458
+ * Ensures the input is returned as an single html element.
459
+ * Useful to normalize operations across multiple or single html elements.
460
+ *
461
+ * @param {TinyElement|TinyElement[]} elems - A single html element or array of html elements.
462
+ * @param {string} where - The method or context name where validation is being called.
463
+ * @returns {HTMLElement} - Always returns an single html element.
464
+ * @readonly
465
+ */
466
+ static _preHtmlElem(elems, where) {
467
+ return TinyHtml._preElemTemplate(elems, where, [HTMLElement], ['HTMLElement']);
468
+ }
469
+
470
+ /**
471
+ * Ensures the input is returned as an array.
472
+ * Useful to normalize operations across multiple or single event target elements.
473
+ *
474
+ * @param {TinyInputElement|TinyInputElement[]} elems - A single event target element or array of html elements.
475
+ * @param {string} where - The method or context name where validation is being called.
476
+ * @returns {InputElement[]} - Always returns an array of event target elements.
477
+ * @readonly
478
+ */
479
+ static _preInputElems(elems, where) {
480
+ return TinyHtml._preElemsTemplate(
481
+ elems,
482
+ where,
483
+ [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, HTMLOptionElement],
484
+ ['HTMLInputElement', 'HTMLSelectElement', 'HTMLTextAreaElement', 'HTMLOptionElement'],
485
+ );
486
+ }
487
+
488
+ /**
489
+ * Ensures the input is returned as an single event target element.
490
+ * Useful to normalize operations across multiple or single event target elements.
491
+ *
492
+ * @param {TinyInputElement|TinyInputElement[]} elems - A single event target element or array of html elements.
493
+ * @param {string} where - The method or context name where validation is being called.
494
+ * @returns {InputElement} - Always returns an single event target element.
495
+ * @readonly
496
+ */
497
+ static _preInputElem(elems, where) {
498
+ return TinyHtml._preElemTemplate(
499
+ elems,
500
+ where,
501
+ [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, HTMLOptionElement],
502
+ ['HTMLInputElement', 'HTMLSelectElement', 'HTMLTextAreaElement', 'HTMLOptionElement'],
503
+ );
504
+ }
505
+
506
+ /**
507
+ * Ensures the input is returned as an array.
508
+ * Useful to normalize operations across multiple or single event target elements.
509
+ *
510
+ * @param {TinyEventTarget|TinyEventTarget[]} elems - A single event target element or array of html elements.
511
+ * @param {string} where - The method or context name where validation is being called.
512
+ * @returns {EventTarget[]} - Always returns an array of event target elements.
513
+ * @readonly
514
+ */
515
+ static _preEventTargetElems(elems, where) {
516
+ return TinyHtml._preElemsTemplate(elems, where, [EventTarget], ['EventTarget']);
517
+ }
518
+
519
+ /**
520
+ * Ensures the input is returned as an single event target element.
521
+ * Useful to normalize operations across multiple or single event target elements.
522
+ *
523
+ * @param {TinyEventTarget|TinyEventTarget[]} elems - A single event target element or array of html elements.
524
+ * @param {string} where - The method or context name where validation is being called.
525
+ * @returns {EventTarget} - Always returns an single event target element.
526
+ * @readonly
527
+ */
528
+ static _preEventTargetElem(elems, where) {
529
+ return TinyHtml._preElemTemplate(elems, where, [EventTarget], ['EventTarget']);
530
+ }
531
+
532
+ /**
533
+ * Ensures the input is returned as an array.
534
+ * Useful to normalize operations across multiple or single element/window elements.
535
+ *
536
+ * @param {TinyElementAndWindow|TinyElementAndWindow[]} elems - A single element/window element or array of html elements.
537
+ * @param {string} where - The method or context name where validation is being called.
538
+ * @returns {ElementAndWindow[]} - Always returns an array of element/window elements.
539
+ * @readonly
540
+ */
541
+ static _preElemsAndWindow(elems, where) {
542
+ return TinyHtml._preElemsTemplate(elems, where, [Element, Window], ['Element', 'Window']);
543
+ }
544
+
545
+ /**
546
+ * Ensures the input is returned as an single element/window element.
547
+ * Useful to normalize operations across multiple or single element/window elements.
548
+ *
549
+ * @param {TinyElementAndWindow|TinyElementAndWindow[]} elems - A single element/window element or array of html elements.
550
+ * @param {string} where - The method or context name where validation is being called.
551
+ * @returns {ElementAndWindow} - Always returns an single element/window element.
552
+ * @readonly
553
+ */
554
+ static _preElemAndWindow(elems, where) {
555
+ return TinyHtml._preElemTemplate(elems, where, [Element, Window], ['Element', 'Window']);
556
+ }
557
+
558
+ /**
559
+ * Normalizes and converts one or more DOM elements (or TinyHtml instances)
560
+ * into an array of `TinyHtml` instances.
561
+ *
562
+ * - If a plain DOM element is passed, it is wrapped into a `TinyHtml` instance.
563
+ * - If a `TinyHtml` instance is already passed, it is preserved.
564
+ * - If an array is passed, all elements inside are converted accordingly.
565
+ *
566
+ * This ensures consistent access to methods of the `TinyHtml` class regardless
567
+ * of the input form.
568
+ *
569
+ * @param {TinyElement|TinyElement[]} elems - A single element or an array of elements (DOM or TinyHtml).
570
+ * @returns {TinyHtml[]} An array of TinyHtml instances corresponding to the input elements.
571
+ */
572
+ static toTinyElm(elems) {
573
+ /** @param {TinyElement[]} item */
574
+ const checkElement = (item) =>
575
+ item.map((elem) => (!(elem instanceof TinyHtml) ? new TinyHtml(elem) : elem));
576
+ if (!Array.isArray(elems)) return checkElement([elems]);
577
+ return checkElement(elems);
578
+ }
579
+
580
+ /**
581
+ * Extracts native `Element` instances from one or more elements,
582
+ * which can be either raw DOM elements or wrapped in `TinyHtml`.
583
+ *
584
+ * - If a `TinyHtml` instance is passed, its internal DOM element is extracted.
585
+ * - If a raw DOM element is passed, it is returned as-is.
586
+ * - If an array is passed, each element is processed accordingly.
587
+ *
588
+ * This function guarantees that the return value is always an array of
589
+ * raw `Element` objects, regardless of whether the input was
590
+ * a mix of `TinyHtml` or native DOM elements.
591
+ *
592
+ * @param {TinyElement|TinyElement[]} elems - A single element or an array of elements (DOM or TinyHtml`).
593
+ * @returns {Element[]} An array of Element instances extracted from the input.
594
+ */
595
+ static fromTinyElm(elems) {
596
+ /** @param {TinyElement[]} item */
597
+ const checkElement = (item) =>
598
+ item.map(
599
+ (elem) =>
600
+ /** @type {Element} */ (
601
+ elem instanceof TinyHtml ? elem._getElement('fromTinyElm') : elem
602
+ ),
603
+ );
604
+ if (!Array.isArray(elems)) return checkElement([elems]);
605
+ return checkElement(elems);
606
+ }
607
+
608
+ /**
609
+ * Filters an array of elements based on a selector, function, element, or array of elements.
610
+ *
611
+ * @param {TinyElement|TinyElement[]} elems
612
+ * @param {WinnowRequest} qualifier
613
+ * @param {string} where - The context/method name using this validation.
614
+ * @param {boolean} not Whether to invert the result (used for .not())
615
+ * @returns {Element[]}
616
+ */
617
+ static winnow(elems, qualifier, where, not = false) {
618
+ if (typeof not !== 'boolean') throw new TypeError('The "not" must be a boolean.');
619
+ if (typeof qualifier === 'function') {
620
+ return TinyHtml._preElems(elems, where).filter(
621
+ (el, i) => !!qualifier.call(el, i, el) !== not,
622
+ );
623
+ }
624
+
625
+ if (qualifier instanceof Element) {
626
+ return TinyHtml._preElems(elems, where).filter((el) => (el === qualifier) !== not);
627
+ }
628
+
629
+ if (
630
+ Array.isArray(qualifier) ||
631
+ (typeof qualifier !== 'string' &&
632
+ // @ts-ignore
633
+ qualifier.length != null)
634
+ ) {
635
+ return TinyHtml._preElems(elems, where).filter((el) => qualifier.includes(el) !== not);
636
+ }
637
+
638
+ // Assume it's a selector string
639
+ let selector = qualifier;
640
+ if (not) selector = `:not(${selector})`;
641
+ return TinyHtml._preElems(elems, where).filter(
642
+ (el) => el.nodeType === 1 && el.matches(selector),
643
+ );
644
+ }
645
+
646
+ /**
647
+ * Filters a set of elements by a CSS selector.
648
+ *
649
+ * @param {TinyElement|TinyElement[]} elems
650
+ * @param {string} selector
651
+ * @param {boolean} not
652
+ * @returns {Element[]}
653
+ */
654
+ static filter(elems, selector, not = false) {
655
+ if (not) selector = `:not(${selector})`;
656
+ return TinyHtml._preElems(elems, 'filter').filter(
657
+ (el) => el.nodeType === 1 && el.matches(selector),
658
+ );
659
+ }
660
+
661
+ /**
662
+ * Returns only the elements matching the given selector or function.
663
+ *
664
+ * @param {TinyElement|TinyElement[]} elems
665
+ * @param {WinnowRequest} selector
666
+ * @returns {Element[]}
667
+ */
668
+ static filterOnly(elems, selector) {
669
+ return TinyHtml.winnow(elems, selector, 'filterOnly', false);
670
+ }
671
+
672
+ /**
673
+ * Returns only the elements **not** matching the given selector or function.
674
+ *
675
+ * @param {TinyElement|TinyElement[]} elems
676
+ * @param {WinnowRequest} selector
677
+ * @returns {Element[]}
678
+ */
679
+ static not(elems, selector) {
680
+ return TinyHtml.winnow(elems, selector, 'not', true);
681
+ }
682
+
683
+ /**
684
+ * Returns only the elements **not** matching the given selector or function.
685
+ *
686
+ * @param {WinnowRequest} selector
687
+ * @returns {Element[]}
688
+ */
689
+ not(selector) {
690
+ return TinyHtml.not(this, selector);
691
+ }
692
+
693
+ /**
694
+ * Finds elements matching a selector within a context.
695
+ *
696
+ * @param {TinyElement|TinyElement[]} context
697
+ * @param {string} selector
698
+ * @returns {Element[]}
699
+ */
700
+ static find(context, selector) {
701
+ const result = [];
702
+ for (const el of TinyHtml._preElems(context, 'find')) {
703
+ result.push(...el.querySelectorAll(selector));
704
+ }
705
+ return [...new Set(result)];
706
+ }
707
+
708
+ /**
709
+ * Finds elements in your element matching a selector within a context.
710
+ *
711
+ * @param {string} selector
712
+ * @returns {Element[]}
713
+ */
714
+ find(selector) {
715
+ return TinyHtml.find(this, selector);
716
+ }
717
+
718
+ /**
719
+ * Checks if at least one element matches the selector.
720
+ *
721
+ * @param {TinyElement|TinyElement[]} elems
722
+ * @param {WinnowRequest} selector
723
+ * @returns {boolean}
724
+ */
725
+ static is(elems, selector) {
726
+ return TinyHtml.winnow(elems, selector, 'is', false).length > 0;
727
+ }
728
+
729
+ /**
730
+ * Checks if the element matches the selector.
731
+ *
732
+ * @param {WinnowRequest} selector
733
+ * @returns {boolean}
734
+ */
735
+ is(selector) {
736
+ return TinyHtml.is(this, selector);
737
+ }
738
+
739
+ /**
740
+ * Returns elements from the current list that contain the given target(s).
741
+ * @param {TinyElement|TinyElement[]} roots - A single element or an array of elements (DOM or TinyHtml).
742
+ * @param {string|TinyElement|TinyElement[]} target - Selector or DOM element(s).
743
+ * @returns {Element[]} Elements that contain the target.
744
+ */
745
+ static has(roots, target) {
746
+ const targets =
747
+ typeof target === 'string'
748
+ ? [...document.querySelectorAll(target)]
749
+ : TinyHtml._preElems(target, 'has');
750
+
751
+ return TinyHtml._preElems(roots, 'has').filter((root) =>
752
+ targets.some((t) => root && root.contains(t)),
753
+ );
754
+ }
755
+
756
+ /**
757
+ * Return if the element has the target(s).
758
+ * @param {string|TinyElement|TinyElement[]} target - Selector or DOM element(s).
759
+ * @returns {boolean} Elements that contain the target.
760
+ */
761
+ has(target) {
762
+ return TinyHtml.has(this, target).length > 0;
763
+ }
764
+
765
+ /**
766
+ * Finds the closest ancestor (including self) that matches the selector.
767
+ *
768
+ * @param {TinyElement|TinyElement[]} els - A single element or an array of elements (DOM or TinyHtml).
769
+ * @param {string|Element} selector - A selector string or DOM element to match.
770
+ * @param {Element|null} [context] - An optional context to stop searching.
771
+ * @returns {Element[]}
772
+ */
773
+ static closest(els, selector, context) {
774
+ const matched = [];
775
+
776
+ for (const el of TinyHtml._preElems(els, 'closest')) {
777
+ /** @type {Element | null} */
778
+ let current = el;
779
+ while (current && current !== context) {
780
+ if (
781
+ current.nodeType === 1 &&
782
+ (typeof selector === 'string' ? current.matches(selector) : current === selector)
783
+ ) {
784
+ matched.push(current);
785
+ break;
786
+ }
787
+ current = current.parentElement;
788
+ }
789
+ }
790
+
791
+ return [...new Set(matched)];
792
+ }
793
+
794
+ /**
795
+ * Finds the closest ancestor (including self) that matches the selector.
796
+ *
797
+ * @param {string|Element} selector - A selector string or DOM element to match.
798
+ * @param {Element|null} [context] - An optional context to stop searching.
799
+ * @returns {Element[]}
800
+ */
801
+ closest(selector, context) {
802
+ return TinyHtml.closest(this, selector, context);
803
+ }
804
+
805
+ /**
806
+ * Compares two DOM elements to determine if they refer to the same node in the document.
807
+ *
808
+ * This performs a strict equality check (`===`) between the two elements.
809
+ *
810
+ * @param {TinyNode} elem - The first DOM element to compare.
811
+ * @param {TinyNode} otherElem - The second DOM element to compare.
812
+ * @returns {boolean} `true` if both elements are the same DOM node; otherwise, `false`.
813
+ */
814
+ static isSameDom(elem, otherElem) {
815
+ return (
816
+ TinyHtml._preNodeElem(elem, 'isSameDom') === TinyHtml._preNodeElem(otherElem, 'isSameDom')
817
+ );
818
+ }
819
+
820
+ /**
821
+ * Compares two DOM elements to determine if they refer to the same node in the document.
822
+ *
823
+ * This performs a strict equality check (`===`) between the two elements.
824
+ *
825
+ * @param {TinyNode} elem - The DOM element to compare.
826
+ * @returns {boolean} `true` if both elements are the same DOM node; otherwise, `false`.
827
+ */
828
+ isSameDom(elem) {
829
+ return TinyHtml.isSameDom(this, elem);
830
+ }
831
+
832
+ //////////////////////////////////////////////////////////////////
833
+
834
+ /** @type {ElementDataStore} */
835
+ _data = {};
836
+
837
+ /**
838
+ * Internal data selectors for accessing public or private data stores.
839
+ *
840
+ * @type {Record<string, (where: string, elem: TinyElement) => ElementDataStore>}
841
+ * @readonly
842
+ */
843
+ static _dataSelector = {
844
+ public: (where, el) => {
845
+ const elem = TinyHtml._preElem(el, where);
846
+ let data = __elementDataMap.get(elem);
847
+ if (!data) {
848
+ data = {};
849
+ __elementDataMap.set(elem, data);
850
+ }
851
+ return data;
852
+ },
853
+ private: (where, el) => {
854
+ if (!(el instanceof TinyHtml))
855
+ throw new Error(`Element must be a TinyHtml instance to execute ${where}().`);
856
+ return el._data;
857
+ },
858
+ };
859
+
860
+ /**
861
+ * Retrieves data associated with a DOM element.
862
+ *
863
+ * If a `key` is provided, the corresponding value is returned.
864
+ * If no `key` is given, a shallow copy of all stored data is returned.
865
+ *
866
+ * @param {TinyElement} el - The DOM element.
867
+ * @param {string|null} [key] - The specific key to retrieve from the data store.
868
+ * @param {boolean} [isPrivate=false] - Whether to access the private data store.
869
+ * @returns {ElementDataStore|undefined|any} - The stored value, all data, or undefined if the key doesn't exist.
870
+ */
871
+ static data(el, key, isPrivate = false) {
872
+ // Get or initialize the data object
873
+ const data = TinyHtml._dataSelector[!isPrivate ? 'public' : 'private']('data', el);
874
+
875
+ // Getter for all
876
+ if (key === undefined || key === null) return { ...data };
877
+
878
+ // Getter for specific key
879
+ if (typeof key !== 'string') throw new TypeError('The key must be a string.');
880
+ return data.hasOwnProperty(key) ? data[key] : undefined;
881
+ }
882
+
883
+ /**
884
+ * Retrieves data associated with a DOM element.
885
+ *
886
+ * If a `key` is provided, the corresponding value is returned.
887
+ * If no `key` is given, a shallow copy of all stored data is returned.
888
+ *
889
+ * @param {string} [key] - The specific key to retrieve from the data store.
890
+ * @param {boolean} [isPrivate=false] - Whether to access the private data store.
891
+ * @returns {ElementDataStore|undefined|any} - The stored value, all data, or undefined if the key doesn't exist.
892
+ */
893
+ data(key, isPrivate) {
894
+ return TinyHtml.data(this, key, isPrivate);
895
+ }
896
+
897
+ /**
898
+ * Stores a value associated with a specific key for a DOM element.
899
+ *
900
+ * @param {TinyElement} el - The DOM element.
901
+ * @param {string} key - The key under which the data will be stored.
902
+ * @param {any} value - The value to store.
903
+ * @param {boolean} [isPrivate=false] - Whether to store the data in the private store.
904
+ * @returns {void}
905
+ */
906
+ static setData(el, key, value, isPrivate = false) {
907
+ const data = TinyHtml._dataSelector[!isPrivate ? 'public' : 'private']('setData', el);
908
+ if (typeof key !== 'string') throw new TypeError('The key must be a string.');
909
+ data[key] = value;
910
+ }
911
+
912
+ /**
913
+ * Stores a value associated with a specific key for a DOM element.
914
+ *
915
+ * @param {string} key - The key under which the data will be stored.
916
+ * @param {any} value - The value to store.
917
+ * @param {boolean} [isPrivate=false] - Whether to store the data in the private store.
918
+ * @returns {void}
919
+ */
920
+ setData(key, value, isPrivate = false) {
921
+ return TinyHtml.setData(this, key, value, isPrivate);
922
+ }
923
+
924
+ //////////////////////////////////////////////////////
925
+
926
+ /**
927
+ * Get the sibling element in a given direction.
928
+ *
929
+ * @param {TinyNode} el
930
+ * @param {"previousSibling"|"nextSibling"} direction
931
+ * @param {string} where
932
+ * @returns {ChildNode|null}
933
+ * @readonly
934
+ */
935
+ static _getSibling(el, direction, where) {
936
+ /** @type {Node|null} */
937
+ let newCurrent = TinyHtml._preNodeElemWithNull(el, where);
938
+ while (newCurrent && (newCurrent = newCurrent[direction]) && newCurrent.nodeType !== 1) {}
939
+ if (!(newCurrent instanceof Node)) return null;
940
+ return /** @type {ChildNode} */ (newCurrent);
941
+ }
942
+
943
+ /**
944
+ * Get all sibling elements excluding the given one.
945
+ *
946
+ * @param {Node|null} start
947
+ * @param {Node|null} [exclude]
948
+ * @returns {ChildNode[]}
949
+ * @readonly
950
+ */
951
+ static _getSiblings(start, exclude) {
952
+ /** @type {Node|null} */
953
+ let st = start;
954
+ const siblings = [];
955
+ for (; st; st = st.nextSibling) {
956
+ if (st.nodeType === 1 && st !== exclude) {
957
+ siblings.push(st);
958
+ }
959
+ }
960
+ return /** @type {ChildNode[]} */ (siblings);
961
+ }
962
+
963
+ /**
964
+ * Traverse DOM in a direction collecting elements.
965
+ *
966
+ * @param {TinyNode} el
967
+ * @param {"parentNode"|"nextSibling"|"previousSibling"} direction
968
+ * @param {TinyNode|string} [until]
969
+ * @param {string} [where='domDir']
970
+ * @returns {ChildNode[]}
971
+ */
972
+ static domDir(el, direction, until, where = 'domDir') {
973
+ if (typeof direction !== 'string') throw new TypeError('The "direction" must be a string.');
974
+ let elem = TinyHtml._preNodeElemWithNull(el, where);
975
+ const matched = [];
976
+ // @ts-ignore
977
+ while (elem && (elem = elem[direction])) {
978
+ if (elem.nodeType !== 1) continue;
979
+ if (
980
+ until &&
981
+ (typeof until === 'string'
982
+ ? // @ts-ignore
983
+ elem.matches(until)
984
+ : elem === until)
985
+ )
986
+ break;
987
+ matched.push(elem);
988
+ }
989
+ return /** @type {ChildNode[]} */ (matched);
990
+ }
991
+
992
+ /**
993
+ * Returns the direct parent node of the given element, excluding document fragments.
994
+ *
995
+ * @param {TinyNode} el - The DOM node to find the parent of.
996
+ * @returns {ParentNode|null} The parent node or null if not found.
997
+ */
998
+ static parent(el) {
999
+ let elem = TinyHtml._preNodeElemWithNull(el, 'parent');
1000
+ const parent = elem ? elem.parentNode : null;
1001
+ return parent && parent.nodeType !== 11 ? parent : null;
1002
+ }
1003
+
1004
+ /**
1005
+ * Returns the direct parent node of the given element, excluding document fragments.
1006
+ *
1007
+ * @returns {ParentNode|null} The parent node or null if not found.
1008
+ */
1009
+ parent() {
1010
+ return TinyHtml.parent(this);
1011
+ }
1012
+
1013
+ /**
1014
+ * Returns all ancestor nodes of the given element, optionally stopping before a specific ancestor.
1015
+ *
1016
+ * @param {TinyNode} el - The DOM node to start from.
1017
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
1018
+ * @returns {ChildNode[]} An array of ancestor nodes.
1019
+ */
1020
+ static parents(el, until) {
1021
+ return TinyHtml.domDir(el, 'parentNode', until, 'parents');
1022
+ }
1023
+
1024
+ /**
1025
+ * Returns all ancestor nodes of the given element, optionally stopping before a specific ancestor.
1026
+ *
1027
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
1028
+ * @returns {ChildNode[]} An array of ancestor nodes.
1029
+ */
1030
+ parents(until) {
1031
+ return TinyHtml.parents(this, until);
1032
+ }
1033
+
1034
+ /**
1035
+ * Returns the next sibling of the given element.
1036
+ *
1037
+ * @param {TinyNode} el - The DOM node to start from.
1038
+ * @returns {ChildNode|null} The next sibling or null if none found.
1039
+ */
1040
+ static next(el) {
1041
+ return TinyHtml._getSibling(el, 'nextSibling', 'next');
1042
+ }
1043
+
1044
+ /**
1045
+ * Returns the next sibling of the given element.
1046
+ *
1047
+ * @returns {ChildNode|null} The next sibling or null if none found.
1048
+ */
1049
+ next() {
1050
+ return TinyHtml.next(this);
1051
+ }
1052
+
1053
+ /**
1054
+ * Returns the previous sibling of the given element.
1055
+ *
1056
+ * @param {TinyNode} el - The DOM node to start from.
1057
+ * @returns {ChildNode|null} The previous sibling or null if none found.
1058
+ */
1059
+ static prev(el) {
1060
+ return TinyHtml._getSibling(el, 'previousSibling', 'prev');
1061
+ }
1062
+
1063
+ /**
1064
+ * Returns the previous sibling of the given element.
1065
+ *
1066
+ * @returns {ChildNode|null} The previous sibling or null if none found.
1067
+ */
1068
+ prev() {
1069
+ return TinyHtml.prev(this);
1070
+ }
1071
+
1072
+ /**
1073
+ * Returns all next sibling nodes after the given element.
1074
+ *
1075
+ * @param {TinyNode} el - The DOM node to start from.
1076
+ * @returns {ChildNode[]} An array of next sibling nodes.
1077
+ */
1078
+ static nextAll(el) {
1079
+ return TinyHtml.domDir(el, 'nextSibling', undefined, 'nextAll');
1080
+ }
1081
+
1082
+ /**
1083
+ * Returns all next sibling nodes after the given element.
1084
+ *
1085
+ * @returns {ChildNode[]} An array of next sibling nodes.
1086
+ */
1087
+ nextAll() {
1088
+ return TinyHtml.nextAll(this);
1089
+ }
1090
+
1091
+ /**
1092
+ * Returns all previous sibling nodes before the given element.
1093
+ *
1094
+ * @param {TinyNode} el - The DOM node to start from.
1095
+ * @returns {ChildNode[]} An array of previous sibling nodes.
1096
+ */
1097
+ static prevAll(el) {
1098
+ return TinyHtml.domDir(el, 'previousSibling', undefined, 'prevAll');
1099
+ }
1100
+
1101
+ /**
1102
+ * Returns all previous sibling nodes before the given element.
1103
+ *
1104
+ * @returns {ChildNode[]} An array of previous sibling nodes.
1105
+ */
1106
+ prevAll() {
1107
+ return TinyHtml.prevAll(this);
1108
+ }
1109
+
1110
+ /**
1111
+ * Returns all next sibling nodes up to (but not including) the node matched by a selector or element.
1112
+ *
1113
+ * @param {TinyNode} el - The DOM node to start from.
1114
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
1115
+ * @returns {ChildNode[]} An array of next sibling nodes.
1116
+ */
1117
+ static nextUntil(el, until) {
1118
+ return TinyHtml.domDir(el, 'nextSibling', until, 'nextUtil');
1119
+ }
1120
+
1121
+ /**
1122
+ * Returns all next sibling nodes up to (but not including) the node matched by a selector or element.
1123
+ *
1124
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
1125
+ * @returns {ChildNode[]} An array of next sibling nodes.
1126
+ */
1127
+ nextUntil(until) {
1128
+ return TinyHtml.nextUntil(this, until);
1129
+ }
1130
+
1131
+ /**
1132
+ * Returns all previous sibling nodes up to (but not including) the node matched by a selector or element.
1133
+ *
1134
+ * @param {TinyNode} el - The DOM node to start from.
1135
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
1136
+ * @returns {ChildNode[]} An array of previous sibling nodes.
1137
+ */
1138
+ static prevUntil(el, until) {
1139
+ return TinyHtml.domDir(el, 'previousSibling', until, 'prevUtil');
1140
+ }
1141
+
1142
+ /**
1143
+ * Returns all previous sibling nodes up to (but not including) the node matched by a selector or element.
1144
+ *
1145
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
1146
+ * @returns {ChildNode[]} An array of previous sibling nodes.
1147
+ */
1148
+ prevUntil(until) {
1149
+ return TinyHtml.prevUntil(this, until);
1150
+ }
1151
+
1152
+ /**
1153
+ * Returns all sibling nodes of the given element, excluding itself.
1154
+ *
1155
+ * @param {TinyNode} el - The DOM node to find siblings of.
1156
+ * @returns {ChildNode[]} An array of sibling nodes.
1157
+ */
1158
+ static siblings(el) {
1159
+ const elem = TinyHtml._preNodeElemWithNull(el, 'siblings');
1160
+ return TinyHtml._getSiblings(elem && elem.parentNode ? elem.parentNode.firstChild : null, elem);
1161
+ }
1162
+
1163
+ /**
1164
+ * Returns all sibling nodes of the given element, excluding itself.
1165
+ *
1166
+ * @returns {ChildNode[]} An array of sibling nodes.
1167
+ */
1168
+ siblings() {
1169
+ return TinyHtml.siblings(this);
1170
+ }
1171
+
1172
+ /**
1173
+ * Returns all child nodes of the given element.
1174
+ *
1175
+ * @param {TinyNode} el - The DOM node to get children from.
1176
+ * @returns {ChildNode[]} An array of child nodes.
1177
+ */
1178
+ static children(el) {
1179
+ const elem = TinyHtml._preNodeElemWithNull(el, 'children');
1180
+ return TinyHtml._getSiblings(elem ? elem.firstChild : null);
1181
+ }
1182
+
1183
+ /**
1184
+ * Returns all child nodes of the given element.
1185
+ *
1186
+ * @returns {ChildNode[]} An array of child nodes.
1187
+ */
1188
+ children() {
1189
+ return TinyHtml.children(this);
1190
+ }
1191
+
1192
+ /**
1193
+ * Returns the contents of the given node. For `<template>` it returns its content; for `<iframe>`, the document.
1194
+ *
1195
+ * @param {TinyNode} el - The DOM node to get contents from.
1196
+ * @returns {(ChildNode|DocumentFragment)[]|Document[]} An array of child nodes or the content document of an iframe.
1197
+ */
1198
+ static contents(el) {
1199
+ const elem = TinyHtml._preNodeElemWithNull(el, 'contents');
1200
+ if (
1201
+ elem instanceof HTMLIFrameElement &&
1202
+ elem.contentDocument != null &&
1203
+ Object.getPrototypeOf(elem.contentDocument)
1204
+ ) {
1205
+ return [elem.contentDocument];
1206
+ }
1207
+
1208
+ if (elem instanceof HTMLTemplateElement) {
1209
+ return Array.from((elem.content || elem).childNodes);
1210
+ }
1211
+
1212
+ if (elem) return Array.from(elem.childNodes);
1213
+ return [];
1214
+ }
1215
+
1216
+ /**
1217
+ * Returns the contents of the given node. For `<template>` it returns its content; for `<iframe>`, the document.
1218
+ *
1219
+ * @returns {(ChildNode|DocumentFragment)[]|Document[]} An array of child nodes or the content document of an iframe.
1220
+ */
1221
+ contents() {
1222
+ return TinyHtml.contents(this);
1223
+ }
1224
+
1225
+ /**
1226
+ * Clone each element.
1227
+ * @param {TinyNode|TinyNode[]} el
1228
+ * @param {boolean} [deep=true]
1229
+ * @returns {Node[]}
1230
+ */
1231
+ static clone(el, deep = true) {
1232
+ if (typeof deep !== 'boolean') throw new TypeError('The "deep" must be a boolean.');
1233
+ return TinyHtml._preNodeElems(el, 'clone').map((el) => el.cloneNode(deep));
1234
+ }
1235
+
1236
+ /**
1237
+ * Clone the element.
1238
+ * @param {boolean} [deep=true]
1239
+ * @returns {Node}
1240
+ */
1241
+ clone(deep) {
1242
+ return TinyHtml.clone(this, deep)[0];
1243
+ }
1244
+
1245
+ /**
1246
+ * Normalize and validate nodes before DOM insertion.
1247
+ * Converts TinyNode-like structures or strings into DOM-compatible nodes.
1248
+ * @type {(where: string, ...nodes: (TinyNode | TinyNode[] | string)[]) => (Node | string)[]}
1249
+ * @readonly
1250
+ */
1251
+ static _appendChecker(where, ...nodes) {
1252
+ const results = [];
1253
+ const nds = [...nodes];
1254
+ for (const index in nds) {
1255
+ if (typeof nds[index] !== 'string') {
1256
+ results.push(TinyHtml._preNodeElem(nds[index], where));
1257
+ } else results.push(nds[index]);
1258
+ }
1259
+ return results;
1260
+ }
1261
+
1262
+ /**
1263
+ * Appends child elements or strings to the end of the target element(s).
1264
+ *
1265
+ * @param {TinyElement | TinyElement[]} el - The target element(s) to receive children.
1266
+ * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to append.
1267
+ */
1268
+ static append(el, ...children) {
1269
+ const elem = TinyHtml._preElem(el, 'append');
1270
+ elem.append(...TinyHtml._appendChecker('append', ...children));
1271
+ }
1272
+
1273
+ /**
1274
+ * Appends child elements or strings to the end of the target element(s).
1275
+ *
1276
+ * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to append.
1277
+ */
1278
+ append(...children) {
1279
+ return TinyHtml.append(this, ...children);
1280
+ }
1281
+
1282
+ /**
1283
+ * Prepends child elements or strings to the beginning of the target element(s).
1284
+ *
1285
+ * @param {TinyElement | TinyElement[]} el - The target element(s) to receive children.
1286
+ * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to prepend.
1287
+ */
1288
+ static prepend(el, ...children) {
1289
+ const elem = TinyHtml._preElem(el, 'prepend');
1290
+ elem.prepend(...TinyHtml._appendChecker('prepend', ...children));
1291
+ }
1292
+
1293
+ /**
1294
+ * Prepends child elements or strings to the beginning of the target element(s).
1295
+ *
1296
+ * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to prepend.
1297
+ */
1298
+ prepend(...children) {
1299
+ return TinyHtml.prepend(this, ...children);
1300
+ }
1301
+
1302
+ /**
1303
+ * Inserts elements or strings immediately before the target element(s) in the DOM.
1304
+ *
1305
+ * @param {TinyElement | TinyElement[]} el - The target element(s) before which new content is inserted.
1306
+ * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert before the target.
1307
+ */
1308
+ static before(el, ...children) {
1309
+ const elem = TinyHtml._preElem(el, 'before');
1310
+ elem.before(...TinyHtml._appendChecker('before', ...children));
1311
+ }
1312
+
1313
+ /**
1314
+ * Inserts elements or strings immediately before the target element(s) in the DOM.
1315
+ *
1316
+ * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert before the target.
1317
+ */
1318
+ before(...children) {
1319
+ return TinyHtml.before(this, ...children);
1320
+ }
1321
+
1322
+ /**
1323
+ * Inserts elements or strings immediately after the target element(s) in the DOM.
1324
+ *
1325
+ * @param {TinyElement | TinyElement[]} el - The target element(s) after which new content is inserted.
1326
+ * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert after the target.
1327
+ */
1328
+ static after(el, ...children) {
1329
+ const elem = TinyHtml._preElem(el, 'after');
1330
+ elem.after(...TinyHtml._appendChecker('after', ...children));
1331
+ }
1332
+
1333
+ /**
1334
+ * Inserts elements or strings immediately after the target element(s) in the DOM.
1335
+ *
1336
+ * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert after the target.
1337
+ */
1338
+ after(...children) {
1339
+ return TinyHtml.after(this, ...children);
1340
+ }
1341
+
1342
+ /**
1343
+ * Replaces the target element(s) in the DOM with new elements or text.
1344
+ *
1345
+ * @param {TinyElement | TinyElement[]} el - The element(s) to be replaced.
1346
+ * @param {...(TinyNode | TinyNode[] | string)} newNodes - New elements or text to replace the target.
1347
+ */
1348
+ static replaceWith(el, ...newNodes) {
1349
+ const elem = TinyHtml._preElem(el, 'replaceWith');
1350
+ elem.replaceWith(...TinyHtml._appendChecker('replaceWith', ...newNodes));
1351
+ }
1352
+
1353
+ /**
1354
+ * Replaces the target element(s) in the DOM with new elements or text.
1355
+ *
1356
+ * @param {...(TinyNode | TinyNode[] | string)} newNodes - New elements or text to replace the target.
1357
+ */
1358
+ replaceWith(...newNodes) {
1359
+ return TinyHtml.replaceWith(this, ...newNodes);
1360
+ }
1361
+
1362
+ /**
1363
+ * Appends the given element(s) to each target element in sequence.
1364
+ *
1365
+ * @param {TinyNode | TinyNode[]} el - The element(s) to append.
1366
+ * @param {TinyNode | TinyNode[]} targets - Target element(s) where content will be appended.
1367
+ */
1368
+ static appendTo(el, targets) {
1369
+ const elems = TinyHtml._preNodeElems(el, 'appendTo');
1370
+ const tars = TinyHtml._preNodeElems(targets, 'appendTo');
1371
+ tars.forEach((target, i) => {
1372
+ elems.forEach((elem) =>
1373
+ target.appendChild(i === tars.length - 1 ? elem : elem.cloneNode(true)),
1374
+ );
1375
+ });
1376
+ }
1377
+
1378
+ /**
1379
+ * Appends the given element(s) to each target element in sequence.
1380
+ *
1381
+ * @param {TinyNode | TinyNode[]} targets - Target element(s) where content will be appended.
1382
+ */
1383
+ appendTo(targets) {
1384
+ return TinyHtml.appendTo(this, targets);
1385
+ }
1386
+
1387
+ /**
1388
+ * Prepends the given element(s) to each target element in sequence.
1389
+ *
1390
+ * @param {TinyElement | TinyElement[]} el - The element(s) to prepend.
1391
+ * @param {TinyElement | TinyElement[]} targets - Target element(s) where content will be prepended.
1392
+ */
1393
+ static prependTo(el, targets) {
1394
+ const elems = TinyHtml._preElems(el, 'prependTo');
1395
+ const tars = TinyHtml._preElems(targets, 'prependTo');
1396
+ tars.forEach((target, i) => {
1397
+ elems
1398
+ .slice()
1399
+ .reverse()
1400
+ .forEach((elem) => target.prepend(i === tars.length - 1 ? elem : elem.cloneNode(true)));
1401
+ });
1402
+ }
1403
+
1404
+ /**
1405
+ * Prepends the given element(s) to each target element in sequence.
1406
+ *
1407
+ * @param {TinyElement | TinyElement[]} targets - Target element(s) where content will be prepended.
1408
+ */
1409
+ prependTo(targets) {
1410
+ return TinyHtml.prependTo(this, targets);
1411
+ }
1412
+
1413
+ /**
1414
+ * Inserts the element before a child of a given target, or before the target itself.
1415
+ *
1416
+ * @param {TinyNode | TinyNode[]} el - The element(s) to insert.
1417
+ * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
1418
+ * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert before, defaults to target.
1419
+ */
1420
+ static insertBefore(el, target, child = null) {
1421
+ const elem = TinyHtml._preNodeElem(el, 'insertBefore');
1422
+ const targ = TinyHtml._preNodeElem(target, 'insertBefore');
1423
+ const childNode = TinyHtml._preNodeElemWithNull(child, 'insertBefore');
1424
+ if (!targ.parentNode) throw new Error('');
1425
+ targ.parentNode.insertBefore(elem, childNode || targ);
1426
+ }
1427
+
1428
+ /**
1429
+ * Inserts the element before a child of a given target, or before the target itself.
1430
+ *
1431
+ * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
1432
+ * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert before, defaults to target.
1433
+ */
1434
+ insertBefore(target, child) {
1435
+ return TinyHtml.insertBefore(this, target, child);
1436
+ }
1437
+
1438
+ /**
1439
+ * Inserts the element after a child of a given target, or after the target itself.
1440
+ *
1441
+ * @param {TinyNode | TinyNode[]} el - The element(s) to insert.
1442
+ * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
1443
+ * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert after, defaults to target.
1444
+ */
1445
+ static insertAfter(el, target, child = null) {
1446
+ const elem = TinyHtml._preNodeElem(el, 'insertAfter');
1447
+ const targ = TinyHtml._preNodeElem(target, 'insertBefore');
1448
+ const childNode = TinyHtml._preNodeElemWithNull(child, 'insertBefore');
1449
+ if (!targ.parentNode) throw new Error('');
1450
+ targ.parentNode.insertBefore(elem, childNode || targ.nextSibling);
1451
+ }
1452
+
1453
+ /**
1454
+ * Inserts the element after a child of a given target, or after the target itself.
1455
+ *
1456
+ * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
1457
+ * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert after, defaults to target.
1458
+ */
1459
+ insertAfter(target, child) {
1460
+ return TinyHtml.insertAfter(this, target, child);
1461
+ }
1462
+
1463
+ /**
1464
+ * Replaces all target elements with the provided element(s).
1465
+ * If multiple targets exist, the inserted elements are cloned accordingly.
1466
+ *
1467
+ * @param {TinyNode | TinyNode[]} el - The new element(s) to insert.
1468
+ * @param {TinyNode | TinyNode[]} targets - The elements to be replaced.
1469
+ */
1470
+ static replaceAll(el, targets) {
1471
+ const elems = TinyHtml._preNodeElems(el, 'replaceAll');
1472
+ const tars = TinyHtml._preNodeElems(targets, 'replaceAll');
1473
+ tars.forEach((target, i) => {
1474
+ const parent = target.parentNode;
1475
+ elems.forEach((elem) => {
1476
+ if (parent)
1477
+ parent.replaceChild(i === tars.length - 1 ? elem : elem.cloneNode(true), target);
1478
+ });
1479
+ });
1480
+ }
1481
+
1482
+ /**
1483
+ * Replaces all target elements with the provided element(s).
1484
+ * If multiple targets exist, the inserted elements are cloned accordingly.
1485
+ *
1486
+ * @param {TinyNode | TinyNode[]} targets - The elements to be replaced.
1487
+ */
1488
+ replaceAll(targets) {
1489
+ return TinyHtml.replaceAll(this, targets);
1490
+ }
1491
+
1492
+ //////////////////////////////////////////////////////
1493
+
1494
+ /**
1495
+ * The target HTML element for instance-level operations.
1496
+ * @type {ConstructorElValues}
1497
+ */
1498
+ #el;
1499
+
1500
+ /**
1501
+ * Creates an instance of TinyHtml for a specific Element.
1502
+ * Useful when you want to operate repeatedly on the same element using instance methods.
1503
+ * @param {ConstructorElValues} el - The element to wrap and manipulate.
1504
+ */
1505
+ constructor(el) {
1506
+ if (el instanceof TinyHtml)
1507
+ throw new Error(
1508
+ `[TinyHtml] You are trying to put a TinyHtml inside another TinyHtml in constructor.`,
1509
+ );
1510
+ if (!(el instanceof Element) && !(el instanceof Window) && !(el instanceof Document))
1511
+ throw new Error(`[TinyHtml] Invalid Target in constructor.`);
1512
+ this.#el = el;
1513
+ }
1514
+
1515
+ /**
1516
+ * Checks whether the given object is a window.
1517
+ * @param {*} obj - The object to test.
1518
+ * @returns {obj is Window} - True if it's a Window.
1519
+ */
1520
+ static isWindow(obj) {
1521
+ return obj != null && obj === obj.window;
1522
+ }
1523
+
1524
+ /////////////////////////////////////////////////////
1525
+
1526
+ /**
1527
+ * Returns the full computed CSS styles for the given element.
1528
+ *
1529
+ * @param {TinyElement} el - The element to retrieve styles from.
1530
+ * @returns {CSSStyleDeclaration} The computed style object for the element.
1531
+ */
1532
+ static css(el) {
1533
+ const elem = TinyHtml._preElem(el, 'css');
1534
+ return window.getComputedStyle(elem);
1535
+ }
1536
+
1537
+ /**
1538
+ * Returns the full computed CSS styles for the given element.
1539
+ *
1540
+ * @returns {CSSStyleDeclaration} The computed style object for the element.
1541
+ */
1542
+ css() {
1543
+ return TinyHtml.css(this);
1544
+ }
1545
+
1546
+ /**
1547
+ * Returns the value of a specific computed CSS property from the given element as a string.
1548
+ *
1549
+ * @param {TinyElement} el - The element to retrieve the style property from.
1550
+ * @param {string} prop - The name of the CSS property (camelCase or kebab-case).
1551
+ * @returns {string|null} The value of the CSS property as a string, or null if not found or invalid.
1552
+ */
1553
+ static cssString(el, prop) {
1554
+ const elem = TinyHtml._preElem(el, 'cssString');
1555
+ if (typeof prop !== 'string') throw new TypeError('The prop must be a string.');
1556
+ // @ts-ignore
1557
+ const val = window.getComputedStyle(elem)[prop];
1558
+ return typeof val === 'string' ? val : typeof val === 'number' ? val.toString() : null;
1559
+ }
1560
+
1561
+ /**
1562
+ * Returns the value of a specific computed CSS property from the given element as a string.
1563
+ *
1564
+ * @param {string} prop - The name of the CSS property (camelCase or kebab-case).
1565
+ * @returns {string|null} The value of the CSS property as a string, or null if not found or invalid.
1566
+ */
1567
+ cssString(prop) {
1568
+ return TinyHtml.cssString(this, prop);
1569
+ }
1570
+
1571
+ /**
1572
+ * Returns a subset of computed CSS styles based on the given list of properties.
1573
+ *
1574
+ * @param {TinyElement} el - The element to retrieve styles from.
1575
+ * @param {string[]} prop - An array of CSS property names to retrieve.
1576
+ * @returns {Partial<CSSStyleDeclaration>} An object containing the requested styles.
1577
+ */
1578
+ static cssList(el, prop) {
1579
+ const elem = TinyHtml._preElem(el, 'cssList');
1580
+ if (!Array.isArray(prop)) throw new TypeError('The prop must be an array of strings.');
1581
+
1582
+ const css = window.getComputedStyle(elem);
1583
+ /** @type {Partial<CSSStyleDeclaration>} */
1584
+ const result = {};
1585
+
1586
+ for (const p of prop) {
1587
+ if (typeof p !== 'undefined') {
1588
+ // @ts-ignore
1589
+ result[p] = css.getPropertyValue(p);
1590
+ }
1591
+ }
1592
+
1593
+ return result;
1594
+ }
1595
+
1596
+ /**
1597
+ * Returns a subset of computed CSS styles based on the given list of properties.
1598
+ *
1599
+ * @param {string[]} prop - An array of CSS property names to retrieve.
1600
+ * @returns {Partial<CSSStyleDeclaration>} An object containing the requested styles.
1601
+ */
1602
+ cssList(prop) {
1603
+ return TinyHtml.cssList(this, prop);
1604
+ }
1605
+
1606
+ /**
1607
+ * Returns the computed CSS float value of a property.
1608
+ * @param {TinyElement} el - The element to inspect.
1609
+ * @param {string} prop - The CSS property.
1610
+ * @returns {number} - The parsed float value.
1611
+ */
1612
+ static cssFloat(el, prop) {
1613
+ const elem = TinyHtml._preElem(el, 'cssFloat');
1614
+ if (typeof prop !== 'string') throw new TypeError('The prop must be a string.');
1615
+ // @ts-ignore
1616
+ const val = window.getComputedStyle(elem)[prop];
1617
+ return parseFloat(val) || 0;
1618
+ }
1619
+
1620
+ /**
1621
+ * Returns the computed CSS float value of a property.
1622
+ * @param {string} prop - The CSS property.
1623
+ * @returns {number} - The parsed float value.
1624
+ */
1625
+ cssFloat(prop) {
1626
+ return TinyHtml.cssFloat(this, prop);
1627
+ }
1628
+
1629
+ /**
1630
+ * Returns computed float values of multiple CSS properties.
1631
+ * @param {TinyElement} el - The element to inspect.
1632
+ * @param {string[]} prop - An array of CSS properties.
1633
+ * @returns {Record<string, number>} - Map of property to float value.
1634
+ */
1635
+ static cssFloats(el, prop) {
1636
+ const elem = TinyHtml._preElem(el, 'cssFloats');
1637
+ if (!Array.isArray(prop)) throw new TypeError('The prop must be an array of strings.');
1638
+ const css = window.getComputedStyle(elem);
1639
+ /** @type {Record<string, number>} */
1640
+ const result = {};
1641
+ for (const name of prop) {
1642
+ // @ts-ignore
1643
+ result[name] = parseFloat(css[name]) || 0;
1644
+ }
1645
+ return result;
1646
+ }
1647
+
1648
+ /**
1649
+ * Returns computed float values of multiple CSS properties.
1650
+ * @param {string[]} prop - An array of CSS properties.
1651
+ * @returns {Record<string, number>} - Map of property to float value.
1652
+ */
1653
+ cssFloats(prop) {
1654
+ return TinyHtml.cssFloats(this, prop);
1655
+ }
1656
+
1657
+ //////////////////////////////////////////////////////////////////////
1658
+
1659
+ /**
1660
+ * Focus the element.
1661
+ *
1662
+ * @param {TinyHtmlElement} el - The element or a selector string.
1663
+ */
1664
+ static focus(el) {
1665
+ const elem = TinyHtml._preHtmlElem(el, 'focus');
1666
+ elem.focus();
1667
+ }
1668
+
1669
+ /**
1670
+ * Focus the element.
1671
+ */
1672
+ focus() {
1673
+ return TinyHtml.focus(this);
1674
+ }
1675
+
1676
+ /**
1677
+ * Blur the element.
1678
+ *
1679
+ * @param {TinyHtmlElement} el - The element or a selector string.
1680
+ */
1681
+ static blur(el) {
1682
+ const elem = TinyHtml._preHtmlElem(el, 'blur');
1683
+ elem.blur();
1684
+ }
1685
+
1686
+ /**
1687
+ * Blur the element.
1688
+ */
1689
+ blur() {
1690
+ return TinyHtml.blur(this);
1691
+ }
1692
+
1693
+ //////////////////////////////////////////////////////////////////////
1694
+
1695
+ /**
1696
+ * Sets the vertical scroll position of the window.
1697
+ * @param {number} value - Sets the scroll position.
1698
+ */
1699
+ static setWinScrollTop(value) {
1700
+ if (typeof value !== 'number') throw new TypeError('The value must be a number.');
1701
+ window.scrollTo({ top: value });
1702
+ }
1703
+
1704
+ /**
1705
+ * Sets the horizontal scroll position of the window.
1706
+ * @param {number} value - Sets the scroll position.
1707
+ */
1708
+ static setWinScrollLeft(value) {
1709
+ if (typeof value !== 'number') throw new TypeError('The value must be a number.');
1710
+ window.scrollTo({ left: value });
1711
+ }
1712
+
1713
+ /**
1714
+ * Gets the vertical scroll position of the window.
1715
+ * @returns {number} - The current scroll top value.
1716
+ */
1717
+ static winScrollTop() {
1718
+ return window.scrollY || document.documentElement.scrollTop;
1719
+ }
1720
+
1721
+ /**
1722
+ * Gets the horizontal scroll position of the window.
1723
+ * @returns {number} - The current scroll left value.
1724
+ */
1725
+ static winScrollLeft() {
1726
+ return window.scrollX || document.documentElement.scrollLeft;
1727
+ }
1728
+
1729
+ /**
1730
+ * Returns the current height of the viewport.
1731
+ * @returns {number} - Viewport height in pixels.
1732
+ */
1733
+ static winInnerHeight() {
1734
+ return window.innerHeight || document.documentElement.clientHeight;
1735
+ }
1736
+
1737
+ /**
1738
+ * Returns the current width of the viewport.
1739
+ * @returns {number} - Viewport width in pixels.
1740
+ */
1741
+ static winInnerWidth() {
1742
+ return window.innerWidth || document.documentElement.clientWidth;
1743
+ }
1744
+
1745
+ /**
1746
+ * Gets the width or height of an element based on the box model.
1747
+ * @param {TinyElementAndWindow} el - The element or window.
1748
+ * @param {"width"|"height"} type - Dimension type.
1749
+ * @param {"content"|"padding"|"border"|"margin"} [extra='content'] - Box model context.
1750
+ * @returns {number} - Computed dimension.
1751
+ * @throws {TypeError} If `type` or `extra` is not a string.
1752
+ */
1753
+ static getDimension(el, type, extra = 'content') {
1754
+ const elem = TinyHtml._preElemAndWindow(el, 'getDimension');
1755
+ if (typeof type !== 'string') throw new TypeError('The type must be a string.');
1756
+ if (typeof extra !== 'string') throw new TypeError('The extra must be a string.');
1757
+
1758
+ const name = type === 'width' ? 'Width' : 'Height';
1759
+
1760
+ if (TinyHtml.isWindow(elem)) {
1761
+ return extra === 'margin'
1762
+ ? // @ts-ignore
1763
+ elem['inner' + name]
1764
+ : // @ts-ignore
1765
+ elem.document.documentElement['client' + name];
1766
+ }
1767
+ /** @type {Element} */
1768
+ const elHtml = elem;
1769
+
1770
+ if (elHtml.nodeType === 9) {
1771
+ // @ts-ignore
1772
+ const doc = elHtml.documentElement;
1773
+ return Math.max(
1774
+ // @ts-ignore
1775
+ elHtml.body['scroll' + name],
1776
+ doc['scroll' + name],
1777
+ // @ts-ignore
1778
+ elHtml.body['offset' + name],
1779
+ doc['offset' + name],
1780
+ doc['client' + name],
1781
+ );
1782
+ }
1783
+
1784
+ let size = elHtml.getBoundingClientRect()[type];
1785
+
1786
+ /**
1787
+ * Auxiliary function to add measures on one side and the other
1788
+ * @param {string} prefix
1789
+ */
1790
+ function sumSides(prefix) {
1791
+ if (type === 'width') {
1792
+ return (
1793
+ TinyHtml.cssFloat(elHtml, prefix + 'Left') + TinyHtml.cssFloat(elHtml, prefix + 'Right')
1794
+ );
1795
+ } else {
1796
+ return (
1797
+ TinyHtml.cssFloat(elHtml, prefix + 'Top') + TinyHtml.cssFloat(elHtml, prefix + 'Bottom')
1798
+ );
1799
+ }
1800
+ }
1801
+
1802
+ switch (extra) {
1803
+ case 'content':
1804
+ // remove padding + border
1805
+ size -= sumSides('padding');
1806
+ size -= sumSides('border');
1807
+ break;
1808
+
1809
+ case 'padding':
1810
+ // remove border only (padding included in the bounding rect)
1811
+ size -= sumSides('border');
1812
+ break;
1813
+
1814
+ case 'border':
1815
+ // bounding rect already includes border + padding, so do not move the size
1816
+ break;
1817
+
1818
+ case 'margin':
1819
+ // adds margin (margin is out of bounding rect)
1820
+ size += sumSides('margin');
1821
+ break;
1822
+ }
1823
+
1824
+ return size;
1825
+ }
1826
+
1827
+ /**
1828
+ * Gets the width or height of an element based on the box model.
1829
+ * @param {"width"|"height"} type - Dimension type.
1830
+ * @param {"content"|"padding"|"border"|"margin"} extra - Box model context.
1831
+ * @returns {number} - Computed dimension.
1832
+ */
1833
+ getDimension(type, extra) {
1834
+ return TinyHtml.getDimension(this, type, extra);
1835
+ }
1836
+
1837
+ /**
1838
+ * Sets the height of the element.
1839
+ * @param {TinyHtmlElement} el - Target element.
1840
+ * @param {string|number} value - Height value.
1841
+ * @throws {TypeError} If `value` is neither a string nor number.
1842
+ */
1843
+ static setHeight(el, value) {
1844
+ const elem = TinyHtml._preHtmlElem(el, 'setHeight');
1845
+ if (typeof value !== 'number' && typeof value !== 'string')
1846
+ throw new TypeError('The value must be a string or number.');
1847
+ elem.style.height = typeof value === 'number' ? `${value}px` : value;
1848
+ }
1849
+
1850
+ /**
1851
+ * Sets the height of the element.
1852
+ * @param {string|number} value - Height value.
1853
+ */
1854
+ setHeight(value) {
1855
+ return TinyHtml.setHeight(this, value);
1856
+ }
1857
+
1858
+ /**
1859
+ * Sets the width of the element.
1860
+ * @param {TinyHtmlElement} el - Target element.
1861
+ * @param {string|number} value - Width value.
1862
+ * @throws {TypeError} If `value` is neither a string nor number.
1863
+ */
1864
+ static setWidth(el, value) {
1865
+ const elem = TinyHtml._preHtmlElem(el, 'setWidth');
1866
+ if (typeof value !== 'number' && typeof value !== 'string')
1867
+ throw new TypeError('The value must be a string or number.');
1868
+ elem.style.width = typeof value === 'number' ? `${value}px` : value;
1869
+ }
1870
+
1871
+ /**
1872
+ * Sets the width of the element.
1873
+ * @param {string|number} value - Width value.
1874
+ */
1875
+ setWidth(value) {
1876
+ return TinyHtml.setWidth(this, value);
1877
+ }
1878
+
1879
+ /**
1880
+ * Returns content box height.
1881
+ * @param {TinyElementAndWindow} el - Target element.
1882
+ * @returns {number}
1883
+ */
1884
+ static height(el) {
1885
+ const elem = TinyHtml._preElemAndWindow(el, 'height');
1886
+ return TinyHtml.getDimension(elem, 'height', 'content');
1887
+ }
1888
+
1889
+ /**
1890
+ * Returns content box height.
1891
+ * @returns {number}
1892
+ */
1893
+ height() {
1894
+ return TinyHtml.height(this);
1895
+ }
1896
+
1897
+ /**
1898
+ * Returns content box width.
1899
+ * @param {TinyElementAndWindow} el - Target element.
1900
+ * @returns {number}
1901
+ */
1902
+ static width(el) {
1903
+ const elem = TinyHtml._preElemAndWindow(el, 'width');
1904
+ return TinyHtml.getDimension(elem, 'width', 'content');
1905
+ }
1906
+
1907
+ /**
1908
+ * Returns content box width.
1909
+ * @returns {number}
1910
+ */
1911
+ width() {
1912
+ return TinyHtml.width(this);
1913
+ }
1914
+
1915
+ /**
1916
+ * Returns padding box height.
1917
+ * @param {TinyElementAndWindow} el - Target element.
1918
+ * @returns {number}
1919
+ */
1920
+ static innerHeight(el) {
1921
+ const elem = TinyHtml._preElemAndWindow(el, 'innerHeight');
1922
+ return TinyHtml.getDimension(elem, 'height', 'padding');
1923
+ }
1924
+
1925
+ /**
1926
+ * Returns padding box height.
1927
+ * @returns {number}
1928
+ */
1929
+ innerHeight() {
1930
+ return TinyHtml.innerHeight(this);
1931
+ }
1932
+
1933
+ /**
1934
+ * Returns padding box width.
1935
+ * @param {TinyElementAndWindow} el - Target element.
1936
+ * @returns {number}
1937
+ */
1938
+ static innerWidth(el) {
1939
+ const elem = TinyHtml._preElemAndWindow(el, 'innerWidth');
1940
+ return TinyHtml.getDimension(elem, 'width', 'padding');
1941
+ }
1942
+
1943
+ /**
1944
+ * Returns padding box width.
1945
+ * @returns {number}
1946
+ */
1947
+ innerWidth() {
1948
+ return TinyHtml.innerWidth(this);
1949
+ }
1950
+
1951
+ /**
1952
+ * Returns outer height of the element, optionally including margin.
1953
+ * @param {TinyElementAndWindow} el - Target element.
1954
+ * @param {boolean} [includeMargin=false] - Whether to include margin.
1955
+ * @returns {number}
1956
+ */
1957
+ static outerHeight(el, includeMargin = false) {
1958
+ if (typeof includeMargin !== 'boolean')
1959
+ throw new TypeError('The "includeMargin" must be a boolean.');
1960
+ const elem = TinyHtml._preElemAndWindow(el, 'outerHeight');
1961
+ return TinyHtml.getDimension(elem, 'height', includeMargin ? 'margin' : 'border');
1962
+ }
1963
+
1964
+ /**
1965
+ * Returns outer height of the element, optionally including margin.
1966
+ * @param {boolean} [includeMargin=false] - Whether to include margin.
1967
+ * @returns {number}
1968
+ */
1969
+ outerHeight(includeMargin) {
1970
+ return TinyHtml.outerHeight(this, includeMargin);
1971
+ }
1972
+
1973
+ /**
1974
+ * Returns outer width of the element, optionally including margin.
1975
+ * @param {TinyElementAndWindow} el - Target element.
1976
+ * @param {boolean} [includeMargin=false] - Whether to include margin.
1977
+ * @returns {number}
1978
+ */
1979
+ static outerWidth(el, includeMargin = false) {
1980
+ if (typeof includeMargin !== 'boolean')
1981
+ throw new TypeError('The "includeMargin" must be a boolean.');
1982
+ const elem = TinyHtml._preElemAndWindow(el, 'outerWidth');
1983
+ return TinyHtml.getDimension(elem, 'width', includeMargin ? 'margin' : 'border');
1984
+ }
1985
+
1986
+ /**
1987
+ * Returns outer width of the element, optionally including margin.
1988
+ * @param {boolean} [includeMargin=false] - Whether to include margin.
1989
+ * @returns {number}
1990
+ */
1991
+ outerWidth(includeMargin) {
1992
+ return TinyHtml.outerWidth(this, includeMargin);
1993
+ }
1994
+
1995
+ //////////////////////////////////////////////////
1996
+
1997
+ /**
1998
+ * Gets the offset of the element relative to the document.
1999
+ * @param {TinyElement} el - Target element.
2000
+ * @returns {{top: number, left: number}}
2001
+ */
2002
+ static offset(el) {
2003
+ const elem = TinyHtml._preElem(el, 'offset');
2004
+ const rect = elem.getBoundingClientRect();
2005
+ const scrollTop = window.scrollY || document.documentElement.scrollTop;
2006
+ const scrollLeft = window.scrollX || document.documentElement.scrollLeft;
2007
+
2008
+ return {
2009
+ top: rect.top + scrollTop,
2010
+ left: rect.left + scrollLeft,
2011
+ };
2012
+ }
2013
+
2014
+ /**
2015
+ * Gets the offset of the element relative to the document.
2016
+ * @returns {{top: number, left: number}}
2017
+ */
2018
+ offset() {
2019
+ return TinyHtml.offset(this);
2020
+ }
2021
+
2022
+ /**
2023
+ * Gets the position of the element relative to its offset parent.
2024
+ * @param {TinyHtmlElement} el - Target element.
2025
+ * @returns {{top: number, left: number}}
2026
+ */
2027
+ static position(el) {
2028
+ const elem = TinyHtml._preHtmlElem(el, 'position');
2029
+
2030
+ let offsetParent;
2031
+ let offset;
2032
+ let parentOffset = { top: 0, left: 0 };
2033
+
2034
+ const computedStyle = window.getComputedStyle(elem);
2035
+
2036
+ if (computedStyle.position === 'fixed') {
2037
+ offset = elem.getBoundingClientRect();
2038
+ } else {
2039
+ offset = TinyHtml.offset(elem);
2040
+
2041
+ offsetParent = elem.offsetParent || document.documentElement;
2042
+ const { position } = window.getComputedStyle(offsetParent);
2043
+
2044
+ while (
2045
+ offsetParent instanceof HTMLElement &&
2046
+ (offsetParent === document.body || offsetParent === document.documentElement) &&
2047
+ position === 'static'
2048
+ ) {
2049
+ offsetParent = offsetParent.parentNode;
2050
+ }
2051
+
2052
+ if (
2053
+ offsetParent instanceof HTMLElement &&
2054
+ offsetParent !== elem &&
2055
+ offsetParent.nodeType === 1
2056
+ ) {
2057
+ const { borderTopWidth, borderLeftWidth } = TinyHtml.cssFloats(offsetParent, [
2058
+ 'borderTopWidth',
2059
+ 'borderLeftWidth',
2060
+ ]);
2061
+ parentOffset = TinyHtml.offset(offsetParent);
2062
+ parentOffset.top += borderTopWidth;
2063
+ parentOffset.left += borderLeftWidth;
2064
+ }
2065
+ }
2066
+
2067
+ return {
2068
+ top: offset.top - parentOffset.top - TinyHtml.cssFloat(elem, 'marginTop'),
2069
+ left: offset.left - parentOffset.left - TinyHtml.cssFloat(elem, 'marginLeft'),
2070
+ };
2071
+ }
2072
+
2073
+ /**
2074
+ * Gets the position of the element relative to its offset parent.
2075
+ * @returns {{top: number, left: number}}
2076
+ */
2077
+ position() {
2078
+ return TinyHtml.position(this);
2079
+ }
2080
+
2081
+ /**
2082
+ * Gets the closest positioned ancestor element.
2083
+ * @param {TinyHtmlElement} el - Target element.
2084
+ * @returns {HTMLElement} - Offset parent element.
2085
+ */
2086
+ static offsetParent(el) {
2087
+ const elem = TinyHtml._preHtmlElem(el, 'offsetParent');
2088
+ let offsetParent = elem.offsetParent;
2089
+
2090
+ while (
2091
+ offsetParent instanceof HTMLElement &&
2092
+ window.getComputedStyle(offsetParent).position === 'static'
2093
+ ) {
2094
+ offsetParent = offsetParent.offsetParent;
2095
+ }
2096
+
2097
+ // Fallback to document.documentElement
2098
+ return offsetParent instanceof HTMLElement ? offsetParent : document.documentElement;
2099
+ }
2100
+
2101
+ /**
2102
+ * Gets the closest positioned ancestor element.
2103
+ * @returns {HTMLElement} - Offset parent element.
2104
+ */
2105
+ offsetParent() {
2106
+ return TinyHtml.offsetParent(this);
2107
+ }
2108
+
2109
+ /**
2110
+ * Gets the vertical scroll position.
2111
+ * @param {TinyElementAndWindow} el - Element or window.
2112
+ * @returns {number}
2113
+ */
2114
+ static scrollTop(el) {
2115
+ const elem = TinyHtml._preElemAndWindow(el, 'scrollTop');
2116
+ if (TinyHtml.isWindow(elem)) return elem.pageYOffset;
2117
+ // @ts-ignore
2118
+ if (elem.nodeType === 9) return elem.defaultView.pageYOffset;
2119
+ return elem.scrollTop;
2120
+ }
2121
+
2122
+ /**
2123
+ * Gets the vertical scroll position.
2124
+ * @returns {number}
2125
+ */
2126
+ scrollTop() {
2127
+ return TinyHtml.scrollTop(this);
2128
+ }
2129
+
2130
+ /**
2131
+ * Gets the horizontal scroll position.
2132
+ * @param {TinyElementAndWindow} el - Element or window.
2133
+ * @returns {number}
2134
+ */
2135
+ static scrollLeft(el) {
2136
+ const elem = TinyHtml._preElemAndWindow(el, 'scrollLeft');
2137
+ if (TinyHtml.isWindow(elem)) return elem.pageXOffset;
2138
+ // @ts-ignore
2139
+ if (elem.nodeType === 9) return elem.defaultView.pageXOffset;
2140
+ return elem.scrollLeft;
2141
+ }
2142
+
2143
+ /**
2144
+ * Gets the horizontal scroll position.
2145
+ * @returns {number}
2146
+ */
2147
+ scrollLeft() {
2148
+ return TinyHtml.scrollLeft(this);
2149
+ }
2150
+
2151
+ /**
2152
+ * Sets the vertical scroll position.
2153
+ * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
2154
+ * @param {number} value - Scroll top value.
2155
+ */
2156
+ static setScrollTop(el, value) {
2157
+ if (typeof value !== 'number') throw new TypeError('ScrollTop value must be a number.');
2158
+ TinyHtml._preElemsAndWindow(el, 'setScrollTop').forEach((elem) => {
2159
+ if (TinyHtml.isWindow(elem)) {
2160
+ elem.scrollTo(elem.pageXOffset, value);
2161
+ } else if (elem.nodeType === 9) {
2162
+ // @ts-ignore
2163
+ elem.defaultView.scrollTo(elem.defaultView.pageXOffset, value);
2164
+ } else {
2165
+ elem.scrollTop = value;
2166
+ }
2167
+ });
2168
+ }
2169
+
2170
+ /**
2171
+ * Sets the vertical scroll position.
2172
+ * @param {number} value - Scroll top value.
2173
+ */
2174
+ setScrollTop(value) {
2175
+ return TinyHtml.setScrollTop(this, value);
2176
+ }
2177
+
2178
+ /**
2179
+ * Sets the horizontal scroll position.
2180
+ * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
2181
+ * @param {number} value - Scroll left value.
2182
+ */
2183
+ static setScrollLeft(el, value) {
2184
+ if (typeof value !== 'number') throw new TypeError('ScrollLeft value must be a number.');
2185
+ TinyHtml._preElemsAndWindow(el, 'setScrollLeft').forEach((elem) => {
2186
+ if (TinyHtml.isWindow(elem)) {
2187
+ elem.scrollTo(value, elem.pageYOffset);
2188
+ } else if (elem.nodeType === 9) {
2189
+ // @ts-ignore
2190
+ elem.defaultView.scrollTo(value, elem.defaultView.pageYOffset);
2191
+ } else {
2192
+ elem.scrollLeft = value;
2193
+ }
2194
+ });
2195
+ }
2196
+
2197
+ /**
2198
+ * Sets the horizontal scroll position.
2199
+ * @param {number} value - Scroll left value.
2200
+ */
2201
+ setScrollLeft(value) {
2202
+ return TinyHtml.setScrollLeft(this, value);
2203
+ }
2204
+
2205
+ /**
2206
+ * Returns the total border width and individual sides from `border{Side}Width` CSS properties.
2207
+ *
2208
+ * @param {TinyElement} el - The target DOM element.
2209
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border widths, and each side individually.
2210
+ */
2211
+ static borderWidth(el) {
2212
+ const elem = TinyHtml._preElem(el, 'borderWidth');
2213
+ const {
2214
+ borderLeftWidth: left,
2215
+ borderRightWidth: right,
2216
+ borderTopWidth: top,
2217
+ borderBottomWidth: bottom,
2218
+ } = TinyHtml.cssFloats(elem, [
2219
+ 'borderLeftWidth',
2220
+ 'borderRightWidth',
2221
+ 'borderTopWidth',
2222
+ 'borderBottomWidth',
2223
+ ]);
2224
+ const x = left + right;
2225
+ const y = top + bottom;
2226
+
2227
+ return { x, y, left, right, top, bottom };
2228
+ }
2229
+
2230
+ /**
2231
+ * Returns the total border width and individual sides from `border{Side}Width` CSS properties.
2232
+ *
2233
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border widths, and each side individually.
2234
+ */
2235
+ borderWidth() {
2236
+ return TinyHtml.borderWidth(this);
2237
+ }
2238
+
2239
+ /**
2240
+ * Returns the total border size and individual sides from `border{Side}` CSS properties.
2241
+ *
2242
+ * @param {TinyElement} el - The target DOM element.
2243
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border sizes, and each side individually.
2244
+ */
2245
+ static border(el) {
2246
+ const elem = TinyHtml._preElem(el, 'border');
2247
+ const {
2248
+ borderLeft: left,
2249
+ borderRight: right,
2250
+ borderTop: top,
2251
+ borderBottom: bottom,
2252
+ } = TinyHtml.cssFloats(elem, ['borderLeft', 'borderRight', 'borderTop', 'borderBottom']);
2253
+ const x = left + right;
2254
+ const y = top + bottom;
2255
+
2256
+ return { x, y, left, right, top, bottom };
2257
+ }
2258
+
2259
+ /**
2260
+ * Returns the total border size and individual sides from `border{Side}` CSS properties.
2261
+ *
2262
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border sizes, and each side individually.
2263
+ */
2264
+ border() {
2265
+ return TinyHtml.border(this);
2266
+ }
2267
+
2268
+ /**
2269
+ * Returns the total margin and individual sides from `margin{Side}` CSS properties.
2270
+ *
2271
+ * @param {TinyElement} el - The target DOM element.
2272
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) margins, and each side individually.
2273
+ */
2274
+ static margin(el) {
2275
+ const elem = TinyHtml._preElem(el, 'margin');
2276
+ const {
2277
+ marginLeft: left,
2278
+ marginRight: right,
2279
+ marginTop: top,
2280
+ marginBottom: bottom,
2281
+ } = TinyHtml.cssFloats(elem, ['marginLeft', 'marginRight', 'marginTop', 'marginBottom']);
2282
+ const x = left + right;
2283
+ const y = top + bottom;
2284
+
2285
+ return { x, y, left, right, top, bottom };
2286
+ }
2287
+
2288
+ /**
2289
+ * Returns the total margin and individual sides from `margin{Side}` CSS properties.
2290
+ *
2291
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) margins, and each side individually.
2292
+ */
2293
+ margin() {
2294
+ return TinyHtml.margin(this);
2295
+ }
2296
+
2297
+ /**
2298
+ * Returns the total padding and individual sides from `padding{Side}` CSS properties.
2299
+ *
2300
+ * @param {TinyElement} el - The target DOM element.
2301
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) paddings, and each side individually.
2302
+ */
2303
+ static padding(el) {
2304
+ const elem = TinyHtml._preElem(el, 'padding');
2305
+ const {
2306
+ paddingLeft: left,
2307
+ paddingRight: right,
2308
+ paddingTop: top,
2309
+ paddingBottom: bottom,
2310
+ } = TinyHtml.cssFloats(elem, ['paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom']);
2311
+ const x = left + right;
2312
+ const y = top + bottom;
2313
+
2314
+ return { x, y, left, right, top, bottom };
2315
+ }
2316
+
2317
+ /**
2318
+ * Returns the total padding and individual sides from `padding{Side}` CSS properties.
2319
+ *
2320
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) paddings, and each side individually.
2321
+ */
2322
+ padding() {
2323
+ return TinyHtml.padding(this);
2324
+ }
2325
+
2326
+ /////////////////////////////////////////////
2327
+
2328
+ /**
2329
+ * Adds one or more CSS class names to the element.
2330
+ * @type {(el: TinyElement|TinyElement[], ...tokens: string[]) => void} - One or more class names to add.
2331
+ */
2332
+ static addClass(el, ...args) {
2333
+ TinyHtml._preElems(el, 'addClass').forEach((elem) => elem.classList.add(...args));
2334
+ }
2335
+
2336
+ /**
2337
+ * Adds one or more CSS class names to the element.
2338
+ * @type {(...tokens: string[]) => void} - One or more class names to add.
2339
+ */
2340
+ addClass(...args) {
2341
+ return TinyHtml.addClass(this, ...args);
2342
+ }
2343
+
2344
+ /**
2345
+ * Removes one or more CSS class names from the element.
2346
+ * @type {(el: TinyElement|TinyElement[], ...tokens: string[]) => void} - One or more class names to remove.
2347
+ */
2348
+ static removeClass(el, ...args) {
2349
+ TinyHtml._preElems(el, 'removeClass').forEach((elem) => elem.classList.remove(...args));
2350
+ }
2351
+
2352
+ /**
2353
+ * Removes one or more CSS class names from the element.
2354
+ * @type {(...tokens: string[]) => void} - One or more class names to remove.
2355
+ */
2356
+ removeClass(...args) {
2357
+ return TinyHtml.removeClass(this, ...args);
2358
+ }
2359
+
2360
+ /**
2361
+ * Replaces an existing class name with a new one.
2362
+ * @param {TinyElement|TinyElement[]} el - Target element.
2363
+ * @param {string} token - The class name to be replaced.
2364
+ * @param {string} newToken - The new class name to apply.
2365
+ * @returns {boolean[]} Whether the replacement was successful.
2366
+ * @throws {TypeError} If either argument is not a string.
2367
+ */
2368
+ static replaceClass(el, token, newToken) {
2369
+ if (typeof token !== 'string') throw new TypeError('The "token" parameter must be a string.');
2370
+ if (typeof newToken !== 'string')
2371
+ throw new TypeError('The "newToken" parameter must be a string.');
2372
+ /** @type {boolean[]} */
2373
+ const result = [];
2374
+ TinyHtml._preElems(el, 'replaceClass').forEach((elem) =>
2375
+ result.push(elem.classList.replace(token, newToken)),
2376
+ );
2377
+ return result;
2378
+ }
2379
+
2380
+ /**
2381
+ * Replaces an existing class name with a new one.
2382
+ * @param {string} token - The class name to be replaced.
2383
+ * @param {string} newToken - The new class name to apply.
2384
+ * @returns {boolean} Whether the replacement was successful.
2385
+ * @throws {TypeError} If either argument is not a string.
2386
+ */
2387
+ replaceClass(token, newToken) {
2388
+ return TinyHtml.replaceClass(this, token, newToken)[0];
2389
+ }
2390
+
2391
+ /**
2392
+ * Returns the class name at the specified index.
2393
+ * @param {TinyElement} el - Target element.
2394
+ * @param {number} index - The index of the class name.
2395
+ * @returns {string|null} The class name at the index or null if not found.
2396
+ * @throws {TypeError} If the index is not a number.
2397
+ */
2398
+ static classItem(el, index) {
2399
+ const elem = TinyHtml._preElem(el, 'classItem');
2400
+ if (typeof index !== 'number') throw new TypeError('The "index" parameter must be a number.');
2401
+ return elem.classList.item(index);
2402
+ }
2403
+
2404
+ /**
2405
+ * Returns the class name at the specified index.
2406
+ * @param {number} index - The index of the class name.
2407
+ * @returns {string|null} The class name at the index or null if not found.
2408
+ * @throws {TypeError} If the index is not a number.
2409
+ */
2410
+ classItem(index) {
2411
+ return TinyHtml.classItem(this, index);
2412
+ }
2413
+
2414
+ /**
2415
+ * Toggles a class name on the element with an optional force boolean.
2416
+ * @param {TinyElement|TinyElement[]} el - Target element.
2417
+ * @param {string} token - The class name to toggle.
2418
+ * @param {boolean} [force] - If true, adds the class; if false, removes it.
2419
+ * @returns {boolean[]} Whether the class is present after the toggle.
2420
+ * @throws {TypeError} If token is not a string or force is not a boolean.
2421
+ */
2422
+ static toggleClass(el, token, force) {
2423
+ if (typeof token !== 'string') throw new TypeError('The "token" parameter must be a string.');
2424
+ if (typeof force !== 'undefined' && typeof force !== 'boolean')
2425
+ throw new TypeError('The "force" parameter must be a boolean.');
2426
+ /** @type {boolean[]} */
2427
+ const result = [];
2428
+ TinyHtml._preElems(el, 'toggleClass').forEach((elem) =>
2429
+ result.push(elem.classList.toggle(token, force)),
2430
+ );
2431
+ return result;
2432
+ }
2433
+
2434
+ /**
2435
+ * Toggles a class name on the element with an optional force boolean.
2436
+ * @param {string} token - The class name to toggle.
2437
+ * @param {boolean} force - If true, adds the class; if false, removes it.
2438
+ * @returns {boolean} Whether the class is present after the toggle.
2439
+ * @throws {TypeError} If token is not a string or force is not a boolean.
2440
+ */
2441
+ toggleClass(token, force) {
2442
+ return TinyHtml.toggleClass(this, token, force)[0];
2443
+ }
2444
+
2445
+ /**
2446
+ * Checks if the element contains the given class name.
2447
+ * @param {TinyElement} el - Target element.
2448
+ * @param {string} token - The class name to check.
2449
+ * @returns {boolean} True if the class is present, false otherwise.
2450
+ * @throws {TypeError} If token is not a string.
2451
+ */
2452
+ static hasClass(el, token) {
2453
+ const elem = TinyHtml._preElem(el, 'hasClass');
2454
+ if (typeof token !== 'string') throw new TypeError('The "token" parameter must be a string.');
2455
+ return elem.classList.contains(token);
2456
+ }
2457
+
2458
+ /**
2459
+ * Checks if the element contains the given class name.
2460
+ * @param {string} token - The class name to check.
2461
+ * @returns {boolean} True if the class is present, false otherwise.
2462
+ * @throws {TypeError} If token is not a string.
2463
+ */
2464
+ hasClass(token) {
2465
+ return TinyHtml.hasClass(this, token);
2466
+ }
2467
+
2468
+ /**
2469
+ * Returns the number of classes applied to the element.
2470
+ * @param {TinyElement} el - Target element.
2471
+ * @returns {number} The number of classes.
2472
+ */
2473
+ static classLength(el) {
2474
+ const elem = TinyHtml._preElem(el, 'classLength');
2475
+ return elem.classList.length;
2476
+ }
2477
+
2478
+ /**
2479
+ * Returns the number of classes applied to the element.
2480
+ * @returns {number} The number of classes.
2481
+ */
2482
+ classLength() {
2483
+ return TinyHtml.classLength(this);
2484
+ }
2485
+
2486
+ /**
2487
+ * Returns all class names as an array of strings.
2488
+ * @param {TinyElement} el - Target element.
2489
+ * @returns {string[]} An array of class names.
2490
+ */
2491
+ static classList(el) {
2492
+ const elem = TinyHtml._preElem(el, 'classList');
2493
+ return elem.classList.values().toArray();
2494
+ }
2495
+
2496
+ /**
2497
+ * Returns all class names as an array of strings.
2498
+ * @returns {string[]} An array of class names.
2499
+ */
2500
+ classList() {
2501
+ return TinyHtml.classList(this);
2502
+ }
2503
+
2504
+ /////////////////////////////////////////
2505
+
2506
+ /**
2507
+ * Returns the tag name of the element.
2508
+ * @param {TinyElement} el - Target element.
2509
+ * @returns {string} The tag name in uppercase.
2510
+ */
2511
+ static tagName(el) {
2512
+ const elem = TinyHtml._preElem(el, 'tagName');
2513
+ return elem.tagName;
2514
+ }
2515
+
2516
+ /**
2517
+ * Returns the tag name of the element.
2518
+ * @returns {string} The tag name in uppercase.
2519
+ */
2520
+ tagName() {
2521
+ return TinyHtml.tagName(this);
2522
+ }
2523
+
2524
+ /**
2525
+ * Returns the ID of the element.
2526
+ * @param {TinyElement} el - Target element.
2527
+ * @returns {string} The element's ID.
2528
+ */
2529
+ static id(el) {
2530
+ const elem = TinyHtml._preElem(el, 'id');
2531
+ return elem.id;
2532
+ }
2533
+
2534
+ /**
2535
+ * Returns the ID of the element.
2536
+ * @returns {string} The element's ID.
2537
+ */
2538
+ id() {
2539
+ return TinyHtml.id(this);
2540
+ }
2541
+
2542
+ /**
2543
+ * Returns the text content of the element.
2544
+ * @param {TinyElement} el - Target element.
2545
+ * @returns {string|null} The text content or null if none.
2546
+ */
2547
+ static text(el) {
2548
+ const elem = TinyHtml._preElem(el, 'text');
2549
+ return elem.textContent;
2550
+ }
2551
+
2552
+ /**
2553
+ * Returns the text content of the element.
2554
+ * @returns {string|null} The text content or null if none.
2555
+ */
2556
+ text() {
2557
+ return TinyHtml.text(this);
2558
+ }
2559
+
2560
+ /**
2561
+ * Set text content of elements.
2562
+ * @param {TinyElement|TinyElement[]} el
2563
+ * @param {string} value
2564
+ */
2565
+ static setText(el, value) {
2566
+ if (typeof value !== 'string') throw new Error('Value is not a valid string.');
2567
+ TinyHtml._preElems(el, 'setText').forEach((el) => (el.textContent = value));
2568
+ }
2569
+
2570
+ /**
2571
+ * Set text content of the element.
2572
+ * @param {string} value
2573
+ */
2574
+ setText(value) {
2575
+ return TinyHtml.setText(this, value);
2576
+ }
2577
+
2578
+ /**
2579
+ * Remove all child nodes from each element.
2580
+ * @param {TinyElement|TinyElement[]} el
2581
+ */
2582
+ static empty(el) {
2583
+ TinyHtml._preElems(el, 'empty').forEach((el) => (el.textContent = ''));
2584
+ }
2585
+
2586
+ /**
2587
+ * Remove all child nodes of the element.
2588
+ */
2589
+ empty() {
2590
+ return TinyHtml.empty(this);
2591
+ }
2592
+
2593
+ /**
2594
+ * Get the innerHTML of the element.
2595
+ * @param {TinyElement|TinyElement[]} el
2596
+ * @returns {string}
2597
+ */
2598
+ static html(el) {
2599
+ const elem = TinyHtml._preElem(el, 'html');
2600
+ return elem.innerHTML;
2601
+ }
2602
+
2603
+ /**
2604
+ * Get the innerHTML of the element.
2605
+ * @returns {string}
2606
+ */
2607
+ html() {
2608
+ return TinyHtml.html(this);
2609
+ }
2610
+
2611
+ /**
2612
+ * Set the innerHTML of each element.
2613
+ * @param {TinyElement|TinyElement[]} el
2614
+ * @param {string} value
2615
+ */
2616
+ static setHtml(el, value) {
2617
+ if (typeof value !== 'string') throw new Error('Value is not a valid string.');
2618
+ TinyHtml._preElems(el, 'setHtml').forEach((el) => (el.innerHTML = value));
2619
+ }
2620
+
2621
+ /**
2622
+ * Set the innerHTML of the element.
2623
+ * @param {string} value
2624
+ */
2625
+ setHtml(value) {
2626
+ return TinyHtml.setHtml(this, value);
2627
+ }
2628
+
2629
+ /** @readonly */
2630
+ static _valHooks = {
2631
+ option: {
2632
+ /**
2633
+ * @param {HTMLOptionElement} elem
2634
+ * @returns {string|null}
2635
+ */
2636
+ get: (elem) => {
2637
+ const val = elem.getAttribute('value');
2638
+ return val != null ? val : elem.textContent;
2639
+ },
2640
+ },
2641
+
2642
+ select: {
2643
+ /**
2644
+ * @param {HTMLSelectElement} elem
2645
+ * @returns {(string | null)[] | string | null}
2646
+ */
2647
+ get: (elem) => {
2648
+ const options = elem.options;
2649
+ const index = elem.selectedIndex;
2650
+ const isSingle = elem.type === 'select-one';
2651
+ const max = isSingle ? index + 1 : options.length;
2652
+
2653
+ /** @type {(string | null)[] | null} */
2654
+ const values = [];
2655
+ let i = index < 0 ? max : isSingle ? index : 0;
2656
+
2657
+ for (; i < max; i++) {
2658
+ const option = options[i];
2659
+ /** @type {HTMLSelectElement|null} */
2660
+ // @ts-ignore
2661
+ const parentNode = option.parentNode;
2662
+ if (
2663
+ (option.selected || i === index) &&
2664
+ !option.disabled &&
2665
+ (!parentNode || !parentNode.disabled || parentNode.tagName !== 'OPTGROUP')
2666
+ ) {
2667
+ const val = TinyHtml._valHooks.option.get(option);
2668
+ if (isSingle) return val;
2669
+ values.push(val);
2670
+ }
2671
+ }
2672
+
2673
+ return values;
2674
+ },
2675
+
2676
+ /**
2677
+ * @param {HTMLSelectElement} elem
2678
+ * @param {string[]|string} value
2679
+ */
2680
+ set: (elem, value) => {
2681
+ const options = elem.options;
2682
+ const values = Array.isArray(value) ? value.map(String) : [String(value)];
2683
+ let optionSet = false;
2684
+
2685
+ for (let i = 0; i < options.length; i++) {
2686
+ const option = options[i];
2687
+ const optionVal = TinyHtml._valHooks.option.get(option);
2688
+ if (typeof optionVal === 'string' && (option.selected = values.includes(optionVal))) {
2689
+ optionSet = true;
2690
+ }
2691
+ }
2692
+
2693
+ if (!optionSet) {
2694
+ elem.selectedIndex = -1;
2695
+ }
2696
+
2697
+ return values;
2698
+ },
2699
+ },
2700
+
2701
+ radio: {
2702
+ /**
2703
+ * @param {HTMLInputElement} elem
2704
+ * @returns {string}
2705
+ */
2706
+ get(elem) {
2707
+ return elem.checked ? 'on' : 'off';
2708
+ },
2709
+ /**
2710
+ * @param {HTMLInputElement} elem
2711
+ * @param {string[]} value
2712
+ */
2713
+ set(elem, value) {
2714
+ if (typeof value === 'boolean') {
2715
+ const label = elem.closest('label');
2716
+ if (value && label) {
2717
+ const otherRadios = label.querySelectorAll('input[type="radio"]');
2718
+ otherRadios.forEach((otherRadio) => {
2719
+ if (otherRadio instanceof HTMLInputElement && otherRadio !== elem)
2720
+ otherRadio.checked = false;
2721
+ });
2722
+ }
2723
+ elem.checked = value;
2724
+ return value;
2725
+ }
2726
+ },
2727
+ },
2728
+
2729
+ checkbox: {
2730
+ /**
2731
+ * @param {HTMLInputElement} elem
2732
+ * @returns {string}
2733
+ */
2734
+ get(elem) {
2735
+ return elem.checked ? 'on' : 'off';
2736
+ },
2737
+ /**
2738
+ * @param {HTMLInputElement} elem
2739
+ * @param {boolean} value
2740
+ */
2741
+ set(elem, value) {
2742
+ if (typeof value === 'boolean') {
2743
+ elem.checked = value;
2744
+ return value;
2745
+ }
2746
+ },
2747
+ },
2748
+ };
2749
+
2750
+ /**
2751
+ * Sets the value of the current HTML value element (input, select, textarea, etc.).
2752
+ * Accepts strings, numbers, booleans or arrays of these values, or a callback function that computes them.
2753
+ *
2754
+ * @param {TinyInputElement|TinyInputElement[]} el - Target element.
2755
+ * @param {SetValueList|((el: InputElement, val: SetValueList) => SetValueList)} value - The value to assign or a function that returns it.
2756
+ * @throws {Error} If the computed value is not a valid string or boolean.
2757
+ */
2758
+ static setVal(el, value) {
2759
+ TinyHtml._preInputElems(el, 'setVal').forEach((elem) => {
2760
+ /**
2761
+ * @param {SetValueBase[]} array
2762
+ * @param {(v: SetValueBase, i: number) => SetValueBase} callback
2763
+ */
2764
+ const mapArray = (array, callback) => {
2765
+ const result = [];
2766
+ for (let i = 0; i < array.length; i++) {
2767
+ result.push(callback(array[i], i));
2768
+ }
2769
+ return result;
2770
+ };
2771
+
2772
+ if (elem.nodeType !== 1) return;
2773
+ /** @type {SetValueList} */
2774
+ let valToSet = typeof value === 'function' ? value(elem, TinyHtml.val(elem)) : value;
2775
+
2776
+ if (valToSet == null) {
2777
+ valToSet = '';
2778
+ } else if (typeof valToSet === 'number') {
2779
+ valToSet = String(valToSet);
2780
+ } else if (Array.isArray(valToSet)) {
2781
+ valToSet = mapArray(valToSet, (v) => (v == null ? '' : String(v)));
2782
+ }
2783
+
2784
+ // @ts-ignore
2785
+ const hook = TinyHtml._valHooks[elem.type] || TinyHtml._valHooks[elem.nodeName.toLowerCase()];
2786
+ if (
2787
+ !hook ||
2788
+ typeof hook.set !== 'function' ||
2789
+ hook.set(elem, valToSet, 'value') === undefined
2790
+ ) {
2791
+ if (typeof valToSet !== 'string' && typeof valToSet !== 'boolean')
2792
+ throw new Error(`Invalid setValue "${typeof valToSet}" value.`);
2793
+ if (typeof valToSet === 'string') elem.value = valToSet;
2794
+ }
2795
+ });
2796
+ }
2797
+
2798
+ /**
2799
+ * Sets the value of the current HTML value element (input, select, textarea, etc.).
2800
+ * Accepts strings, numbers, booleans or arrays of these values, or a callback function that computes them.
2801
+ *
2802
+ * @param {SetValueList|((el: InputElement, val: SetValueList) => SetValueList)} value - The value to assign or a function that returns it.
2803
+ * @throws {Error} If the computed value is not a valid string or boolean.
2804
+ */
2805
+ setVal(value) {
2806
+ return TinyHtml.setVal(this, value);
2807
+ }
2808
+
2809
+ /**
2810
+ * Maps value types to their corresponding getter functions.
2811
+ * Each function extracts a value of a specific type from a compatible HTMLInputElement.
2812
+ * @readonly
2813
+ */
2814
+ static _valTypes = {
2815
+ /**
2816
+ * Gets the string value from any HTMLInputElement.
2817
+ * @type {(elem: HTMLInputElement) => string}
2818
+ */
2819
+ string: (elem) => elem.value,
2820
+
2821
+ /**
2822
+ * Gets the value as a Date object from supported input types.
2823
+ * Valid only for types: "date", "datetime-local", "month", "time", "week".
2824
+ * Returns `null` if the field is empty or invalid.
2825
+ * @type {(elem: HTMLInputElement & { type: "date" | "datetime-local" | "month" | "time" | "week" }) => Date | null}
2826
+ */
2827
+ date: (elem) => elem.valueAsDate,
2828
+
2829
+ /**
2830
+ * Gets the numeric value from supported input types.
2831
+ * Valid for types: "number", "range", "date", "time".
2832
+ * Returns `NaN` if the value is invalid or empty.
2833
+ * @type {(elem: HTMLInputElement & { type: "number" | "range" | "date" | "time" }) => number}
2834
+ */
2835
+ number: (elem) => elem.valueAsNumber,
2836
+ };
2837
+
2838
+ /**
2839
+ * Gets the value of an input element according to the specified type.
2840
+ *
2841
+ * @param {InputElement} elem - The input element to extract the value from.
2842
+ * @param {GetValueTypes} type - The type of value to retrieve ("string", "date", or "number").
2843
+ * @param {string} where - The context/method name using this validation.
2844
+ * @returns {any} The extracted value, depending on the type.
2845
+ * @throws {Error} If the element is not an HTMLInputElement or if the type handler is invalid.
2846
+ * @readonly
2847
+ */
2848
+ static _getValByType(elem, type, where) {
2849
+ if (typeof type !== 'string') throw new TypeError('The "type" must be a string.');
2850
+ if (typeof where !== 'string') throw new TypeError('The "where" must be a string.');
2851
+ if (!(elem instanceof HTMLInputElement))
2852
+ throw new Error(`Provided element is not an HTMLInputElement in ${where}().`);
2853
+ if (typeof TinyHtml._valTypes[type] !== 'function')
2854
+ throw new Error(`No handler found for type "${type}" in ${where}().`);
2855
+ // @ts-ignore
2856
+ return TinyHtml._valTypes[type](elem);
2857
+ }
2858
+
2859
+ /**
2860
+ * Retrieves the raw value from the HTML input element.
2861
+ * If a custom value hook exists, it will be used first.
2862
+ *
2863
+ * @param {TinyInputElement} el - Target element.
2864
+ * @param {GetValueTypes} type - The type of value to retrieve ("string", "date", or "number").
2865
+ * @param {string} where - The context/method name using this validation.
2866
+ * @returns {any} The raw value retrieved from the element or hook.
2867
+ * @readonly
2868
+ */
2869
+ static _val(el, where, type) {
2870
+ const elem = TinyHtml._preInputElem(el, where);
2871
+ // @ts-ignore
2872
+ const hook = TinyHtml._valHooks[elem.type] || TinyHtml._valHooks[elem.nodeName.toLowerCase()];
2873
+ if (hook && typeof hook.get === 'function') {
2874
+ const ret = hook.get(elem, 'value', type);
2875
+ if (ret !== undefined) return typeof ret === 'string' ? ret.replace(/\r/g, '') : ret;
2876
+ }
2877
+
2878
+ return TinyHtml._getValByType(elem, type, where);
2879
+ }
2880
+
2881
+ /**
2882
+ * Retrieves the raw value from the HTML input element.
2883
+ * If a custom value hook exists, it will be used first.
2884
+ *
2885
+ * @param {GetValueTypes} type - The type of value to retrieve ("string", "date", or "number").
2886
+ * @param {string} where - The context/method name using this validation.
2887
+ * @returns {any} The raw value retrieved from the element or hook.
2888
+ * @readonly
2889
+ */
2890
+ _val(where, type) {
2891
+ return TinyHtml._val(this, where, type);
2892
+ }
2893
+
2894
+ /**
2895
+ * Gets the value of the current HTML value element.
2896
+ *
2897
+ * @param {TinyInputElement} el - Target element.
2898
+ * @returns {SetValueList} The normalized value, with carriage returns removed.
2899
+ */
2900
+ static val(el) {
2901
+ return /** @type {SetValueList} */ (TinyHtml._val(el, 'val', 'string'));
2902
+ }
2903
+
2904
+ /**
2905
+ * Gets the value of the current HTML value element.
2906
+ *
2907
+ * @returns {SetValueList} The normalized value, with carriage returns removed.
2908
+ */
2909
+ val() {
2910
+ return TinyHtml.val(this);
2911
+ }
2912
+
2913
+ /**
2914
+ * Gets the text of the current HTML value element (for text).
2915
+ *
2916
+ * @param {TinyInputElement} el - Target element.
2917
+ * @returns {string} The text value.
2918
+ * @throws {Error} If the element is not a string value.
2919
+ */
2920
+ static valTxt(el) {
2921
+ /** @type {string} */
2922
+ const ret = TinyHtml._val(el, 'valTxt', 'string');
2923
+ if (typeof ret !== 'string' && ret !== null) throw new Error('Value is not a valid string.');
2924
+ return ret == null ? '' : typeof ret === 'string' ? ret.replace(/\r/g, '') : ret;
2925
+ }
2926
+
2927
+ /**
2928
+ * Gets the text of the current HTML value element (for text).
2929
+ *
2930
+ * @returns {string} The text value.
2931
+ * @throws {Error} If the element is not a string value.
2932
+ */
2933
+ valTxt() {
2934
+ return TinyHtml.valTxt(this);
2935
+ }
2936
+
2937
+ /**
2938
+ * Internal helper to get a value from an input expected to return an array.
2939
+ *
2940
+ * @param {TinyInputElement} el - Target element.
2941
+ * @param {string} where - The method name or context using this validation (for error reporting).
2942
+ * @param {GetValueTypes} type - The type of value to retrieve ("string", "date", or "number").
2943
+ * @returns {SetValueBase[]} - The validated value as an array.
2944
+ * @throws {Error} If the returned value is not an array.
2945
+ * @readonly
2946
+ */
2947
+ static _valArr(el, where, type) {
2948
+ /** @type {SetValueBase[]} */
2949
+ const ret = TinyHtml._val(el, where, type);
2950
+ if (!Array.isArray(ret)) throw new Error(`Value expected an array but got ${typeof ret}.`);
2951
+ return ret;
2952
+ }
2953
+
2954
+ /**
2955
+ * Internal helper to get a value from an input expected to return an array.
2956
+ *
2957
+ * @param {string} where - The method name or context using this validation (for error reporting).
2958
+ * @param {GetValueTypes} type - The type of value to retrieve ("string", "date", or "number").
2959
+ * @returns {SetValueBase[]} - The validated value as an array.
2960
+ * @throws {Error} If the returned value is not an array.
2961
+ * @readonly
2962
+ */
2963
+ _valArr(where, type) {
2964
+ return TinyHtml._valArr(this, where, type);
2965
+ }
2966
+
2967
+ /**
2968
+ * Gets the raw value as a generic array of the current HTML value element (for select).
2969
+ *
2970
+ * @param {TinyInputElement} el - Target element.
2971
+ * @returns {SetValueBase[]} - The value cast as a generic array.
2972
+ * @throws {Error} If the value is not a valid array.
2973
+ */
2974
+ static valArr(el) {
2975
+ return TinyHtml._valArr(el, 'valArr', 'string');
2976
+ }
2977
+
2978
+ /**
2979
+ * Gets the raw value as a generic array of the current HTML value element (for select).
2980
+ *
2981
+ * @returns {SetValueBase[]} - The value cast as a generic array.
2982
+ * @throws {Error} If the value is not a valid array.
2983
+ */
2984
+ valArr() {
2985
+ return TinyHtml.valArr(this);
2986
+ }
2987
+
2988
+ /**
2989
+ * Gets the current value parsed as a number (for number/text).
2990
+ *
2991
+ * @param {TinyInputElement} el - Target element.
2992
+ * @returns {number} The numeric value.
2993
+ * @throws {Error} If the element is not a number-compatible input or value is NaN.
2994
+ */
2995
+ static valNb(el) {
2996
+ const elem = TinyHtml._preInputElem(el, 'valNb');
2997
+ if (!(elem instanceof HTMLInputElement)) throw new Error('Element must be an input element.');
2998
+ /** @type {number} */
2999
+ const result = TinyHtml._val(el, 'valNb', 'number');
3000
+ if (Number.isNaN(result)) throw new Error('Value is not a valid number.');
3001
+ return result;
3002
+ }
3003
+
3004
+ /**
3005
+ * Gets the current value parsed as a number (for number/text).
3006
+ *
3007
+ * @returns {number} The numeric value.
3008
+ * @throws {Error} If the element is not a number-compatible input or value is NaN.
3009
+ */
3010
+ valNb() {
3011
+ return TinyHtml.valNb(this);
3012
+ }
3013
+
3014
+ /**
3015
+ * Gets the current value parsed as a Date (for time/date).
3016
+ *
3017
+ * @param {TinyInputElement} el - Target element.
3018
+ * @returns {Date} The date value.
3019
+ * @throws {Error} If the element is not a date-compatible input.
3020
+ */
3021
+ static valDate(el) {
3022
+ const elem = TinyHtml._preInputElem(el, 'valDate');
3023
+ if (!(elem instanceof HTMLInputElement)) throw new Error('Element must be an input element.');
3024
+ /** @type {Date} */
3025
+ const result = TinyHtml._val(el, 'valDate', 'date');
3026
+ if (!(result instanceof Date)) throw new Error('Value is not a valid date.');
3027
+ return result;
3028
+ }
3029
+
3030
+ /**
3031
+ * Gets the current value parsed as a Date (for time/date).
3032
+ *
3033
+ * @returns {Date} The date value.
3034
+ * @throws {Error} If the element is not a date-compatible input.
3035
+ */
3036
+ valDate() {
3037
+ return TinyHtml.valDate(this);
3038
+ }
3039
+
3040
+ /**
3041
+ * Checks if the input element is boolean (for checkboxes/radios).
3042
+ *
3043
+ * @param {TinyInputElement} el - Target element.
3044
+ * @returns {boolean} True if the input is considered checked (value === "on"), false otherwise.
3045
+ * @throws {Error} If the element is not a checkbox/radio input.
3046
+ */
3047
+ static valBool(el) {
3048
+ const elem = TinyHtml._preInputElem(el, 'valBool');
3049
+ if (!(elem instanceof HTMLInputElement)) throw new Error('Element must be an input element.');
3050
+ return TinyHtml.val(elem) === 'on' ? true : false;
3051
+ }
3052
+
3053
+ /**
3054
+ * Checks if the input element is boolean (for checkboxes/radios).
3055
+ *
3056
+ * @returns {boolean} True if the input is considered checked (value === "on"), false otherwise.
3057
+ * @throws {Error} If the element is not a checkbox/radio input.
3058
+ */
3059
+ valBool() {
3060
+ return TinyHtml.valBool(this);
3061
+ }
3062
+
3063
+ ////////////////////////////////////////////
3064
+
3065
+ /**
3066
+ * Registers an event listener on the specified element.
3067
+ *
3068
+ * @param {TinyEventTarget|TinyEventTarget[]} el - The target to listen on.
3069
+ * @param {string} event - The event type (e.g. 'click', 'keydown').
3070
+ * @param {EventRegistryHandle} handler - The callback function to run on event.
3071
+ * @param {EventRegistryOptions} [options] - Optional event listener options.
3072
+ */
3073
+ static on(el, event, handler, options) {
3074
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
3075
+ TinyHtml._preEventTargetElems(el, 'on').forEach((elem) => {
3076
+ elem.addEventListener(event, handler, options);
3077
+
3078
+ if (!__eventRegistry.has(elem)) __eventRegistry.set(elem, {});
3079
+ const events = __eventRegistry.get(elem);
3080
+ if (!events) return;
3081
+ if (!Array.isArray(events[event])) events[event] = [];
3082
+ events[event].push({ handler, options });
3083
+ });
3084
+ }
3085
+
3086
+ /**
3087
+ * Registers an event listener on the specified element.
3088
+ *
3089
+ * @param {string} event - The event type (e.g. 'click', 'keydown').
3090
+ * @param {EventRegistryHandle} handler - The callback function to run on event.
3091
+ * @param {EventRegistryOptions} [options] - Optional event listener options.
3092
+ */
3093
+ on(event, handler, options) {
3094
+ return TinyHtml.on(this, event, handler, options);
3095
+ }
3096
+
3097
+ /**
3098
+ * Registers an event listener that runs only once, then is removed.
3099
+ *
3100
+ * @param {TinyEventTarget|TinyEventTarget[]} el - The target to listen on.
3101
+ * @param {string} event - The event type (e.g. 'click', 'keydown').
3102
+ * @param {EventRegistryHandle} handler - The callback function to run on event.
3103
+ * @param {EventRegistryOptions} [options={}] - Optional event listener options.
3104
+ */
3105
+ static once(el, event, handler, options = {}) {
3106
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
3107
+ TinyHtml._preEventTargetElems(el, 'once').forEach((elem) => {
3108
+ /** @type {EventRegistryHandle} e */
3109
+ const wrapped = (e) => {
3110
+ TinyHtml.off(elem, event, wrapped);
3111
+ handler(e);
3112
+ };
3113
+
3114
+ TinyHtml.on(
3115
+ elem,
3116
+ event,
3117
+ wrapped,
3118
+ typeof options === 'boolean' ? options : { ...options, once: true },
3119
+ );
3120
+ });
3121
+ }
3122
+
3123
+ /**
3124
+ * Registers an event listener that runs only once, then is removed.
3125
+ *
3126
+ * @param {string} event - The event type (e.g. 'click', 'keydown').
3127
+ * @param {EventRegistryHandle} handler - The callback function to run on event.
3128
+ * @param {EventRegistryOptions} [options={}] - Optional event listener options.
3129
+ */
3130
+ once(event, handler, options = {}) {
3131
+ return TinyHtml.once(this, event, handler, options);
3132
+ }
3133
+
3134
+ /**
3135
+ * Removes a specific event listener from an element.
3136
+ *
3137
+ * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
3138
+ * @param {string} event - The event type.
3139
+ * @param {EventRegistryHandle} handler - The function originally bound to the event.
3140
+ * @param {boolean|EventListenerOptions} [options] - Optional listener options.
3141
+ */
3142
+ static off(el, event, handler, options) {
3143
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
3144
+ TinyHtml._preEventTargetElems(el, 'off').forEach((elem) => {
3145
+ elem.removeEventListener(event, handler, options);
3146
+
3147
+ const events = __eventRegistry.get(elem);
3148
+ if (events && events[event]) {
3149
+ events[event] = events[event].filter((entry) => entry.handler !== handler);
3150
+ if (events[event].length === 0) delete events[event];
3151
+ }
3152
+ });
3153
+ }
3154
+
3155
+ /**
3156
+ * Removes a specific event listener from an element.
3157
+ *
3158
+ * @param {string} event - The event type.
3159
+ * @param {EventRegistryHandle} handler - The function originally bound to the event.
3160
+ * @param {boolean|EventListenerOptions} [options] - Optional listener options.
3161
+ */
3162
+ off(event, handler, options) {
3163
+ return TinyHtml.off(this, event, handler, options);
3164
+ }
3165
+
3166
+ /**
3167
+ * Removes all event listeners of a specific type from the element.
3168
+ *
3169
+ * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
3170
+ * @param {string} event - The event type to remove (e.g. 'click').
3171
+ */
3172
+ static offAll(el, event) {
3173
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
3174
+ TinyHtml._preEventTargetElems(el, 'offAll').forEach((elem) => {
3175
+ const events = __eventRegistry.get(elem);
3176
+ if (events && events[event]) {
3177
+ for (const entry of events[event]) {
3178
+ elem.removeEventListener(event, entry.handler, entry.options);
3179
+ }
3180
+ delete events[event];
3181
+ }
3182
+ });
3183
+ }
3184
+
3185
+ /**
3186
+ * Removes all event listeners of a specific type from the element.
3187
+ *
3188
+ * @param {string} event - The event type to remove (e.g. 'click').
3189
+ */
3190
+ offAll(event) {
3191
+ return TinyHtml.offAll(this, event);
3192
+ }
3193
+
3194
+ /**
3195
+ * Removes all event listeners of all types from the element.
3196
+ *
3197
+ * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
3198
+ * @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
3199
+ * Optional filter function to selectively remove specific handlers.
3200
+ */
3201
+ static offAllTypes(el, filterFn = null) {
3202
+ if (filterFn !== null && typeof filterFn !== 'function')
3203
+ throw new TypeError('The "filterFn" must be a function.');
3204
+ TinyHtml._preEventTargetElems(el, 'offAllTypes').forEach((elem) => {
3205
+ const events = __eventRegistry.get(elem);
3206
+ if (!events) return;
3207
+
3208
+ for (const event in events) {
3209
+ for (const entry of events[event]) {
3210
+ if (typeof filterFn !== 'function' || filterFn(entry.handler, event)) {
3211
+ elem.removeEventListener(event, entry.handler, entry.options);
3212
+ }
3213
+ }
3214
+ }
3215
+
3216
+ __eventRegistry.delete(elem);
3217
+ });
3218
+ }
3219
+
3220
+ /**
3221
+ * Removes all event listeners of all types from the element.
3222
+ *
3223
+ * @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
3224
+ * Optional filter function to selectively remove specific handlers.
3225
+ */
3226
+ offAllTypes(filterFn = null) {
3227
+ return TinyHtml.offAllTypes(this, filterFn);
3228
+ }
3229
+
3230
+ /**
3231
+ * Triggers all handlers associated with a specific event on the given element.
3232
+ *
3233
+ * @param {TinyEventTarget|TinyEventTarget[]} el - Target element where the event should be triggered.
3234
+ * @param {string} event - Name of the event to trigger.
3235
+ * @param {Event|CustomEvent|CustomEventInit} [payload] - Optional event object or data to pass.
3236
+ */
3237
+ static trigger(el, event, payload = {}) {
3238
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
3239
+ TinyHtml._preEventTargetElems(el, 'trigger').forEach((elem) => {
3240
+ const evt =
3241
+ payload instanceof Event || payload instanceof CustomEvent
3242
+ ? payload
3243
+ : new CustomEvent(event, {
3244
+ bubbles: true,
3245
+ cancelable: true,
3246
+ detail: payload,
3247
+ });
3248
+
3249
+ elem.dispatchEvent(evt);
3250
+ });
3251
+ }
3252
+
3253
+ /**
3254
+ * Triggers all handlers associated with a specific event on the given element.
3255
+ *
3256
+ * @param {string} event - Name of the event to trigger.
3257
+ * @param {Event|CustomEvent|CustomEventInit} [payload] - Optional event object or data to pass.
3258
+ */
3259
+ trigger(event, payload = {}) {
3260
+ return TinyHtml.trigger(this, event, payload);
3261
+ }
3262
+
3263
+ ///////////////////////////////////////////////////////////////
3264
+
3265
+ /**
3266
+ * Property name normalization similar to jQuery's propFix.
3267
+ * @readonly
3268
+ */
3269
+ static _propFix = {
3270
+ for: 'htmlFor',
3271
+ class: 'className',
3272
+ };
3273
+
3274
+ /**
3275
+ * Get an attribute on an element.
3276
+ * @param {TinyElement} el
3277
+ * @param {string} name
3278
+ * @returns {string|null}
3279
+ */
3280
+ static attr(el, name) {
3281
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
3282
+ const elem = TinyHtml._preElem(el, 'attr');
3283
+ return elem.getAttribute(name);
3284
+ }
3285
+
3286
+ /**
3287
+ * Get an attribute on an element.
3288
+ * @param {string} name
3289
+ * @returns {string|null}
3290
+ */
3291
+ attr(name) {
3292
+ return TinyHtml.attr(this, name);
3293
+ }
3294
+
3295
+ /**
3296
+ * Set an attribute on an element.
3297
+ * @param {TinyElement|TinyElement[]} el
3298
+ * @param {string} name
3299
+ * @param {string|null} [value=null]
3300
+ */
3301
+ static setAttr(el, name, value = null) {
3302
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
3303
+ if (value !== null && typeof value !== 'string')
3304
+ throw new TypeError('The "value" must be a string.');
3305
+ TinyHtml._preElems(el, 'setAttr').forEach((elem) => {
3306
+ if (value === null) elem.removeAttribute(name);
3307
+ else elem.setAttribute(name, value);
3308
+ });
3309
+ }
3310
+
3311
+ /**
3312
+ * Set an attribute on an element.
3313
+ * @param {string} name
3314
+ * @param {string|null} [value=null]
3315
+ */
3316
+ setAttr(name, value) {
3317
+ return TinyHtml.setAttr(this, name, value);
3318
+ }
3319
+
3320
+ /**
3321
+ * Remove attribute(s) from an element.
3322
+ * @param {TinyElement|TinyElement[]} el
3323
+ * @param {string} name Space-separated list of attributes.
3324
+ */
3325
+ static removeAttr(el, name) {
3326
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
3327
+ TinyHtml._preElems(el, 'removeAttr').forEach((elem) => elem.removeAttribute(name));
3328
+ }
3329
+
3330
+ /**
3331
+ * Remove attribute(s) from an element.
3332
+ * @param {string} name Space-separated list of attributes.
3333
+ */
3334
+ removeAttr(name) {
3335
+ return TinyHtml.removeAttr(this, name);
3336
+ }
3337
+
3338
+ /**
3339
+ * Check if an attribute exists on an element.
3340
+ * @param {TinyElement} el
3341
+ * @param {string} name
3342
+ * @returns {boolean}
3343
+ */
3344
+ static hasAttr(el, name) {
3345
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
3346
+ const elem = TinyHtml._preElem(el, 'hasAttr');
3347
+ return elem.hasAttribute(name);
3348
+ }
3349
+
3350
+ /**
3351
+ * Check if an attribute exists on an element.
3352
+ * @param {string} name
3353
+ * @returns {boolean}
3354
+ */
3355
+ hasAttr(name) {
3356
+ return TinyHtml.hasAttr(this, name);
3357
+ }
3358
+
3359
+ /**
3360
+ * Check if a property exists.
3361
+ * @param {TinyElement} el
3362
+ * @param {string} name
3363
+ * @returns {boolean}
3364
+ */
3365
+ static hasProp(el, name) {
3366
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
3367
+ const elem = TinyHtml._preElem(el, 'hasProp');
3368
+ // @ts-ignore
3369
+ const propName = TinyHtml._propFix[name] || name;
3370
+ // @ts-ignore
3371
+ return !!elem[propName];
3372
+ }
3373
+
3374
+ /**
3375
+ * Check if a property exists.
3376
+ * @param {string} name
3377
+ * @returns {boolean}
3378
+ */
3379
+ hasProp(name) {
3380
+ return TinyHtml.hasProp(this, name);
3381
+ }
3382
+
3383
+ /**
3384
+ * Set a property on an element.
3385
+ * @param {TinyElement|TinyElement[]} el
3386
+ * @param {string} name
3387
+ */
3388
+ static addProp(el, name) {
3389
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
3390
+ TinyHtml._preElems(el, 'addProp').forEach((elem) => {
3391
+ // @ts-ignore
3392
+ name = TinyHtml._propFix[name] || name;
3393
+ // @ts-ignore
3394
+ elem[name] = true;
3395
+ });
3396
+ }
3397
+
3398
+ /**
3399
+ * Set a property on an element.
3400
+ * @param {string} name
3401
+ */
3402
+ addProp(name) {
3403
+ return TinyHtml.addProp(this, name);
3404
+ }
3405
+
3406
+ /**
3407
+ * Remove a property from an element.
3408
+ * @param {TinyElement|TinyElement[]} el
3409
+ * @param {string} name
3410
+ */
3411
+ static removeProp(el, name) {
3412
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
3413
+ TinyHtml._preElems(el, 'removeProp').forEach((elem) => {
3414
+ // @ts-ignore
3415
+ name = TinyHtml._propFix[name] || name;
3416
+ // @ts-ignore
3417
+ elem[name] = false;
3418
+ });
3419
+ }
3420
+
3421
+ /**
3422
+ * Remove a property from an element.
3423
+ * @param {string} name
3424
+ */
3425
+ removeProp(name) {
3426
+ return TinyHtml.removeProp(this, name);
3427
+ }
3428
+
3429
+ /**
3430
+ * Toggle a boolean property.
3431
+ * @param {TinyElement|TinyElement[]} el
3432
+ * @param {string} name
3433
+ * @param {boolean} [force]
3434
+ */
3435
+ static toggleProp(el, name, force) {
3436
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
3437
+ if (typeof force !== 'undefined' && typeof force !== 'boolean')
3438
+ throw new TypeError('The "force" must be a boolean.');
3439
+ TinyHtml._preElems(el, 'toggleProp').forEach((elem) => {
3440
+ // @ts-ignore
3441
+ const propName = TinyHtml._propFix[name] || name;
3442
+ // @ts-ignore
3443
+ const shouldEnable = force === undefined ? !elem[propName] : force;
3444
+ // @ts-ignore
3445
+ if (shouldEnable) TinyHtml.addProp(elem, name);
3446
+ else TinyHtml.removeProp(elem, name);
3447
+ });
3448
+ }
3449
+
3450
+ /**
3451
+ * Toggle a boolean property.
3452
+ * @param {string} name
3453
+ * @param {boolean} [force]
3454
+ */
3455
+ toggleProp(name, force) {
3456
+ return TinyHtml.toggleProp(this, name, force);
3457
+ }
3458
+
3459
+ /////////////////////////////////////////////////////
3460
+
3461
+ /**
3462
+ * Removes an element from the DOM.
3463
+ * @param {TinyElement|TinyElement[]} el - The DOM element or selector to remove.
3464
+ */
3465
+ static remove(el) {
3466
+ TinyHtml._preElems(el, 'remove').forEach((elem) => elem.remove());
3467
+ }
3468
+
3469
+ /**
3470
+ * Removes the element from the DOM.
3471
+ */
3472
+ remove() {
3473
+ return TinyHtml.remove(this);
3474
+ }
3475
+
3476
+ /**
3477
+ * Returns the index of the first element within its parent or relative to a selector/element.
3478
+ *
3479
+ * @param {TinyElement} el - The element target
3480
+ * @param {string|TinyElement|null} [el2] - Optional target to compare index against.
3481
+ * @returns {number}
3482
+ */
3483
+ static index(el, el2 = null) {
3484
+ const elem = TinyHtml._preElem(el, 'index');
3485
+ if (!elem) return -1;
3486
+
3487
+ if (!el2) {
3488
+ return Array.prototype.indexOf.call(elem.parentNode?.children || [], elem);
3489
+ }
3490
+
3491
+ if (el2) {
3492
+ const matchEls =
3493
+ typeof el2 === 'string' ? document.querySelectorAll(el2) : TinyHtml._preElems(el2, 'index');
3494
+ return Array.prototype.indexOf.call(matchEls, elem);
3495
+ }
3496
+
3497
+ return -1;
3498
+ }
3499
+
3500
+ /**
3501
+ * Returns the index of the first element within its parent or relative to a selector/element.
3502
+ *
3503
+ * @param {string|TinyElement|null} [elem] - Optional target to compare index against.
3504
+ * @returns {number}
3505
+ */
3506
+ index(elem) {
3507
+ return TinyHtml.index(this, elem);
3508
+ }
3509
+
3510
+ ////////////////////////////////////////////////////////////////////
3511
+
3512
+ /**
3513
+ * Creates a new DOMRect object by copying the base rect and applying optional additional dimensions.
3514
+ *
3515
+ * @param {DOMRect} rect - The base rectangle to be cloned and extended.
3516
+ * @param {Partial<DOMRect>} extraRect - Additional dimensions to apply to the base rect (e.g., extra padding or offset).
3517
+ * @returns {DOMRect} - A new DOMRect object with the combined dimensions.
3518
+ * @readonly
3519
+ */
3520
+ static _getCustomRect(rect, extraRect) {
3521
+ /** @type {DOMRect} */
3522
+ const result = {
3523
+ height: 0,
3524
+ width: 0,
3525
+ x: 0,
3526
+ y: 0,
3527
+ bottom: 0,
3528
+ left: 0,
3529
+ right: 0,
3530
+ top: 0,
3531
+ toJSON: function () {
3532
+ throw new Error('Function not implemented.');
3533
+ },
3534
+ };
3535
+ for (const name in rect) {
3536
+ // @ts-ignore
3537
+ if (typeof rect[name] === 'number')
3538
+ // @ts-ignore
3539
+ result[name] = rect[name];
3540
+ }
3541
+
3542
+ if (typeof extraRect !== 'object' || extraRect === null || Array.isArray(extraRect))
3543
+ throw new Error('');
3544
+ const { height = 0, width = 0, top = 0, bottom = 0, left = 0, right = 0 } = extraRect;
3545
+ if (typeof height !== 'number') throw new Error('');
3546
+ if (typeof width !== 'number') throw new Error('');
3547
+ if (typeof top !== 'number') throw new Error('');
3548
+ if (typeof bottom !== 'number') throw new Error('');
3549
+ if (typeof left !== 'number') throw new Error('');
3550
+ if (typeof right !== 'number') throw new Error('');
3551
+
3552
+ // @ts-ignore
3553
+ result.height += height;
3554
+ // @ts-ignore
3555
+ result.width += width;
3556
+ // @ts-ignore
3557
+ result.top += top;
3558
+ // @ts-ignore
3559
+ result.bottom += bottom;
3560
+ // @ts-ignore
3561
+ result.left += left;
3562
+ // @ts-ignore
3563
+ result.right += right;
3564
+ return result;
3565
+ }
3566
+
3567
+ /**
3568
+ * Determines if two HTML elements are colliding, using a simple bounding box comparison.
3569
+ *
3570
+ * @param {TinyElement} el1 - The first element to compare.
3571
+ * @param {TinyElement} el2 - The second element to compare.
3572
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
3573
+ * @returns {boolean} - `true` if the elements are colliding, `false` otherwise.
3574
+ */
3575
+ static isCollWith(el1, el2, extraRect = {}) {
3576
+ const rect1 = TinyHtml._getCustomRect(
3577
+ TinyHtml._preElem(el1, 'isCollWith').getBoundingClientRect(),
3578
+ extraRect,
3579
+ );
3580
+ const rect2 = TinyHtml._preElem(el2, 'isCollWith').getBoundingClientRect();
3581
+ return areElsColliding(rect1, rect2);
3582
+ }
3583
+
3584
+ /**
3585
+ * Determines if two HTML elements are colliding, using a simple bounding box comparison.
3586
+ *
3587
+ * @param {TinyElement} el2 - The second element to compare.
3588
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
3589
+ * @returns {boolean} - `true` if the elements are colliding, `false` otherwise.
3590
+ */
3591
+ isCollWith(el2, extraRect) {
3592
+ return TinyHtml.isCollWith(this, el2, extraRect);
3593
+ }
3594
+
3595
+ /**
3596
+ * Determines if two HTML elements are colliding using a pixel-perfect collision algorithm.
3597
+ *
3598
+ * @param {TinyElement} el1 - The first element to compare.
3599
+ * @param {TinyElement} el2 - The second element to compare.
3600
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
3601
+ * @returns {boolean} - `true` if the elements are colliding with higher precision, `false` otherwise.
3602
+ */
3603
+ static isCollPerfWith(el1, el2, extraRect = {}) {
3604
+ const rect1 = TinyHtml._getCustomRect(
3605
+ TinyHtml._preElem(el1, 'isCollPerfWith').getBoundingClientRect(),
3606
+ extraRect,
3607
+ );
3608
+ const rect2 = TinyHtml._preElem(el2, 'isCollPerfWith').getBoundingClientRect();
3609
+ return areElsPerfColliding(rect1, rect2);
3610
+ }
3611
+
3612
+ /**
3613
+ * Determines if two HTML elements are colliding using a pixel-perfect collision algorithm.
3614
+ *
3615
+ * @param {TinyElement} el2 - The second element to compare.
3616
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
3617
+ * @returns {boolean} - `true` if the elements are colliding with higher precision, `false` otherwise.
3618
+ */
3619
+ isCollPerfWith(el2, extraRect) {
3620
+ return TinyHtml.isCollPerfWith(this, el2, extraRect);
3621
+ }
3622
+
3623
+ /**
3624
+ * Determines whether two elements are colliding with a directional lock mechanism.
3625
+ *
3626
+ * This function tracks the direction from which an element (`elem1`) initially collided with another,
3627
+ * and keeps the collision "locked" until the element exits the collision from the same direction.
3628
+ *
3629
+ * - If `isColliding` is true and no lock is stored yet, it saves the direction of entry.
3630
+ * - If `isColliding` is false but a previous lock exists, it checks if the element has exited
3631
+ * in the same direction it entered to remove the lock.
3632
+ *
3633
+ * @param {boolean} isColliding - Indicates whether `rect1` and `rect2` are currently colliding.
3634
+ * @param {DOMRect} rect1 - The bounding box of the first element.
3635
+ * @param {DOMRect} rect2 - The bounding box of the second element.
3636
+ * @param {Element} elem1 - The element to track collision state for.
3637
+ * @param {CollisionDirLock} lockDirection - The direction from which the collision was first detected.
3638
+ * @returns {boolean} Returns `true` if the element is still considered colliding (locked), otherwise `false`.
3639
+ * @readonly
3640
+ */
3641
+ static _isCollWithLock(isColliding, rect1, rect2, elem1, lockDirection) {
3642
+ const lockMap = __elemCollision[lockDirection];
3643
+
3644
+ if (isColliding) {
3645
+ // Save entry direction
3646
+ if (!lockMap.has(elem1)) {
3647
+ lockMap.set(elem1, true);
3648
+ }
3649
+ return true;
3650
+ }
3651
+
3652
+ // Handle unlock logic
3653
+ if (lockMap.has(elem1)) {
3654
+ let shouldUnlock = false;
3655
+
3656
+ switch (lockDirection) {
3657
+ case 'top':
3658
+ shouldUnlock = areElsCollTop(rect1, rect2);
3659
+ break;
3660
+ case 'bottom':
3661
+ shouldUnlock = areElsCollBottom(rect1, rect2);
3662
+ break;
3663
+ case 'left':
3664
+ shouldUnlock = areElsCollLeft(rect1, rect2);
3665
+ break;
3666
+ case 'right':
3667
+ shouldUnlock = areElsCollRight(rect1, rect2);
3668
+ break;
3669
+ }
3670
+
3671
+ if (shouldUnlock) lockMap.delete(elem1);
3672
+
3673
+ return lockMap.has(elem1); // still colliding (locked)
3674
+ }
3675
+
3676
+ return false;
3677
+ }
3678
+
3679
+ /**
3680
+ * Checks if two DOM elements are colliding on the screen, and locks the collision
3681
+ * until the element exits through the same side it entered.
3682
+ *
3683
+ * @param {TinyElement} el1 - First DOM element (e.g. draggable or moving element).
3684
+ * @param {TinyElement} el2 - Second DOM element (e.g. a container or boundary element).
3685
+ * @param {CollisionDirLock} lockDirection - Direction that must be respected to unlock the collision.
3686
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
3687
+ * @returns {boolean} True if collision is still active.
3688
+ */
3689
+ static isCollWithLock(el1, el2, lockDirection, extraRect = {}) {
3690
+ const elem1 = TinyHtml._preElem(el1, 'isCollWithLock');
3691
+ const elem2 = TinyHtml._preElem(el2, 'isCollWithLock');
3692
+ const rect1 = TinyHtml._getCustomRect(elem1.getBoundingClientRect(), extraRect);
3693
+ const rect2 = elem2.getBoundingClientRect();
3694
+ const isColliding = areElsColliding(rect1, rect2);
3695
+ return TinyHtml._isCollWithLock(isColliding, rect1, rect2, elem1, lockDirection);
3696
+ }
3697
+
3698
+ /**
3699
+ * Checks if two DOM elements are colliding on the screen, and locks the collision
3700
+ * until the element exits through the same side it entered.
3701
+ *
3702
+ * @param {TinyElement} el2 - Second DOM element (e.g. a container or boundary element).
3703
+ * @param {CollisionDirLock} lockDirection - Direction that must be respected to unlock the collision.
3704
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
3705
+ * @returns {boolean} True if collision is still active.
3706
+ */
3707
+ isCollWithLock(el2, lockDirection, extraRect) {
3708
+ return TinyHtml.isCollWithLock(this, el2, lockDirection, extraRect);
3709
+ }
3710
+
3711
+ /**
3712
+ * Checks if two DOM elements are colliding on the screen, and locks the collision
3713
+ * until the element exits through the same side it entered.
3714
+ *
3715
+ * @param {TinyElement} el1 - First DOM element (e.g. draggable or moving element).
3716
+ * @param {TinyElement} el2 - Second DOM element (e.g. a container or boundary element).
3717
+ * @param {CollisionDirLock} lockDirection - Direction that must be respected to unlock the collision.
3718
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
3719
+ * @returns {boolean} True if collision is still active.
3720
+ */
3721
+ static isCollPerfWithLock(el1, el2, lockDirection, extraRect = {}) {
3722
+ const elem1 = TinyHtml._preElem(el1, 'isCollPerfWithLock');
3723
+ const elem2 = TinyHtml._preElem(el2, 'isCollPerfWithLock');
3724
+ const rect1 = TinyHtml._getCustomRect(elem1.getBoundingClientRect(), extraRect);
3725
+ const rect2 = elem2.getBoundingClientRect();
3726
+ const isColliding = areElsPerfColliding(rect1, rect2);
3727
+ return TinyHtml._isCollWithLock(isColliding, rect1, rect2, elem1, lockDirection);
3728
+ }
3729
+
3730
+ /**
3731
+ * Checks if two DOM elements are colliding on the screen, and locks the collision
3732
+ * until the element exits through the same side it entered.
3733
+ *
3734
+ * @param {TinyElement} el2 - Second DOM element (e.g. a container or boundary element).
3735
+ * @param {CollisionDirLock} lockDirection - Direction that must be respected to unlock the collision.
3736
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
3737
+ * @returns {boolean} True if collision is still active.
3738
+ */
3739
+ isCollPerfWithLock(el2, lockDirection, extraRect) {
3740
+ return TinyHtml.isCollPerfWithLock(this, el2, lockDirection, extraRect);
3741
+ }
3742
+
3743
+ /**
3744
+ * Resets all collision locks for a specific element (for all directions).
3745
+ *
3746
+ * @param {TinyElement} el - The element whose locks should be removed.
3747
+ * @returns {boolean} True if at least one lock was removed.
3748
+ */
3749
+ static resetCollLock(el) {
3750
+ const elem = TinyHtml._preElem(el, 'resetCollLock');
3751
+ let removed = false;
3752
+
3753
+ for (const dir of /** @type {CollisionDirLock[]} */ (['top', 'bottom', 'left', 'right'])) {
3754
+ if (__elemCollision[dir].has(elem)) {
3755
+ __elemCollision[dir].delete(elem);
3756
+ removed = true;
3757
+ }
3758
+ }
3759
+
3760
+ return removed;
3761
+ }
3762
+
3763
+ /**
3764
+ * Resets the collision lock for a specific element.
3765
+ *
3766
+ * This removes any previously stored collision direction for the given element,
3767
+ * effectively unlocking its collision state.
3768
+ *
3769
+ * @returns {boolean} Returns `true` if a lock was removed, `false` if no lock was present.
3770
+ */
3771
+ resetCollLock() {
3772
+ return TinyHtml.resetCollLock(this);
3773
+ }
3774
+
3775
+ /**
3776
+ * Resets the collision lock for a specific element and direction.
3777
+ *
3778
+ * @param {TinyElement} el - The element whose lock should be removed.
3779
+ * @param {CollisionDirLock} direction - The direction to clear the lock from.
3780
+ * @returns {boolean} True if the lock was removed.
3781
+ */
3782
+ static resetCollLockDir(el, direction) {
3783
+ const elem = TinyHtml._preElem(el, 'resetCollLockDir');
3784
+ const lockMap = __elemCollision[direction];
3785
+
3786
+ if (lockMap.has(elem)) {
3787
+ lockMap.delete(elem);
3788
+ return true;
3789
+ }
3790
+
3791
+ return false;
3792
+ }
3793
+
3794
+ /**
3795
+ * Resets the collision lock for a specific element and direction.
3796
+ *
3797
+ * @param {CollisionDirLock} direction - The direction to clear the lock from.
3798
+ * @returns {boolean} True if the lock was removed.
3799
+ */
3800
+ resetCollLockDir(direction) {
3801
+ return TinyHtml.resetCollLockDir(this, direction);
3802
+ }
3803
+
3804
+ //////////////////////////////////////////////////////////////////////////////
3805
+
3806
+ /**
3807
+ * Checks if the given element is at least partially visible in the viewport.
3808
+ *
3809
+ * @param {TinyElement} el - The DOM element to check.
3810
+ * @returns {boolean} True if the element is partially in the viewport, false otherwise.
3811
+ */
3812
+ static isInViewport(el) {
3813
+ const elem = TinyHtml._preElem(el, 'isInViewport');
3814
+ const elementTop = TinyHtml.offset(elem).top;
3815
+ const elementBottom = elementTop + TinyHtml.outerHeight(elem);
3816
+
3817
+ const viewportTop = TinyHtml.scrollTop(window);
3818
+ const viewportBottom = viewportTop + TinyHtml.height(window);
3819
+
3820
+ return elementBottom > viewportTop && elementTop < viewportBottom;
3821
+ }
3822
+
3823
+ /**
3824
+ * Checks if the given element is at least partially visible in the viewport.
3825
+ *
3826
+ * @returns {boolean} True if the element is partially in the viewport, false otherwise.
3827
+ */
3828
+ isInViewport() {
3829
+ return TinyHtml.isInViewport(this);
3830
+ }
3831
+
3832
+ /**
3833
+ * Checks if the given element is fully visible in the viewport (top and bottom).
3834
+ *
3835
+ * @param {TinyElement} el - The DOM element to check.
3836
+ * @returns {boolean} True if the element is fully visible in the viewport, false otherwise.
3837
+ */
3838
+ static isScrolledIntoView(el) {
3839
+ const elem = TinyHtml._preElem(el, 'isScrolledIntoView');
3840
+ const docViewTop = TinyHtml.scrollTop(window);
3841
+ const docViewBottom = docViewTop + TinyHtml.height(window);
3842
+
3843
+ const elemTop = TinyHtml.offset(elem).top;
3844
+ const elemBottom = elemTop + TinyHtml.height(elem);
3845
+
3846
+ return elemBottom <= docViewBottom && elemTop >= docViewTop;
3847
+ }
3848
+
3849
+ /**
3850
+ * Checks if the given element is fully visible in the viewport (top and bottom).
3851
+ *
3852
+ * @returns {boolean} True if the element is fully visible in the viewport, false otherwise.
3853
+ */
3854
+ isScrolledIntoView() {
3855
+ return TinyHtml.isScrolledIntoView(this);
3856
+ }
3857
+ }
3858
+
3859
+ module.exports = TinyHtml;