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