tiny-essentials 1.21.10 → 1.22.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/.vscode/extensions.json +30 -0
- package/.vscode/settings.json +53 -0
- package/LICENSE +160 -669
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyDragger.min.js +1 -1
- package/dist/v1/TinyElementObserver.min.js +1 -0
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyHtml.min.js +1 -1
- package/dist/v1/TinySmartScroller.min.js +1 -1
- package/dist/v1/TinyUploadClicker.min.js +1 -1
- package/dist/v1/basics/array.cjs +12 -0
- package/dist/v1/basics/array.d.mts +9 -0
- package/dist/v1/basics/array.mjs +10 -0
- package/dist/v1/basics/index.cjs +2 -0
- package/dist/v1/basics/index.d.mts +3 -1
- package/dist/v1/basics/index.mjs +3 -3
- package/dist/v1/basics/text.cjs +44 -9
- package/dist/v1/basics/text.d.mts +14 -2
- package/dist/v1/basics/text.mjs +38 -9
- package/dist/v1/build/TinyElementObserver.cjs +7 -0
- package/dist/v1/build/TinyElementObserver.d.mts +3 -0
- package/dist/v1/build/TinyElementObserver.mjs +2 -0
- package/dist/v1/index.cjs +4 -0
- package/dist/v1/index.d.mts +4 -1
- package/dist/v1/index.mjs +4 -3
- package/dist/v1/libs/TinyElementObserver.cjs +292 -0
- package/dist/v1/libs/TinyElementObserver.d.mts +154 -0
- package/dist/v1/libs/TinyElementObserver.mjs +266 -0
- package/dist/v1/libs/TinyGamepad.d.mts +1 -1
- package/dist/v1/libs/TinyHtml.cjs +1418 -131
- package/dist/v1/libs/TinyHtml.d.mts +513 -12
- package/dist/v1/libs/TinyHtml.mjs +1129 -14
- package/dist/v1/libs/TinyInventory.d.mts +1 -1
- package/dist/v1/libs/UltraRandomMsgGen.d.mts +7 -7
- package/docs/v1/README.md +1 -0
- package/docs/v1/basics/array.md +20 -0
- package/docs/v1/basics/text.md +38 -7
- package/docs/v1/libs/TinyElementObserver.md +107 -0
- package/docs/v1/libs/TinyHtml.md +797 -3
- package/package.json +2 -2
|
@@ -1,9 +1,56 @@
|
|
|
1
|
+
import { diffArrayList } from '../basics/array.mjs';
|
|
2
|
+
import { diffStrings } from '../basics/text.mjs';
|
|
1
3
|
import * as TinyCollision from '../basics/collision.mjs';
|
|
4
|
+
import TinyElementObserver from './TinyElementObserver.mjs';
|
|
5
|
+
// TITLE: Introduction
|
|
2
6
|
const { areElsColliding, areElsPerfColliding, areElsCollTop, areElsCollBottom, areElsCollLeft, areElsCollRight, } = TinyCollision;
|
|
3
7
|
/**
|
|
4
8
|
* Represents a TinyHtml instance with any constructor element values.
|
|
5
9
|
* @typedef {TinyHtml<ConstructorElValues>} TinyHtmlAny
|
|
6
10
|
*/
|
|
11
|
+
/**
|
|
12
|
+
* Callback function used for hover events.
|
|
13
|
+
* @callback HoverEventCallback
|
|
14
|
+
* @param {MouseEvent} ev - The mouse event triggered on enter or leave.
|
|
15
|
+
* @returns {void} Returns nothing.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Represents a collection of active style-based animations.
|
|
19
|
+
*
|
|
20
|
+
* Each HTMLElement is associated with an array of its currently running
|
|
21
|
+
* `Animation` objects (from the Web Animations API).
|
|
22
|
+
*
|
|
23
|
+
* @typedef {Map<HTMLElement, Animation|null>} StyleFxResult
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Represents a collection of animation keyframe data mapped by CSS property.
|
|
27
|
+
*
|
|
28
|
+
* - The **key** is the CSS property name (e.g. `"height"`, `"opacity"`).
|
|
29
|
+
* - The **value** is an array of values representing the start and end
|
|
30
|
+
* states of the property during the animation.
|
|
31
|
+
*
|
|
32
|
+
* @typedef {Record<string, (string|number)[]>} AnimationSfxData
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Function signature for style effects repeat detectors.
|
|
36
|
+
*
|
|
37
|
+
* @typedef {(effects: AnimationSfxData) => boolean} StyleEffectsRdFn
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* Function signature for style effect property handlers.
|
|
41
|
+
*
|
|
42
|
+
* @typedef {(
|
|
43
|
+
* el: HTMLElement,
|
|
44
|
+
* keyframes: AnimationSfxData,
|
|
45
|
+
* prop: string,
|
|
46
|
+
* style: CSSStyleDeclaration
|
|
47
|
+
* ) => void} StyleEffectsFn
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* A collection of style effect property handlers.
|
|
51
|
+
*
|
|
52
|
+
* @typedef {Record<string, StyleEffectsFn>} StyleEffectsProps
|
|
53
|
+
*/
|
|
7
54
|
/**
|
|
8
55
|
* Callback invoked on each animation frame with the current scroll position,
|
|
9
56
|
* normalized animation time (`0` to `1`), and a completion flag.
|
|
@@ -143,6 +190,31 @@ const __eventRegistry = new WeakMap();
|
|
|
143
190
|
* @type {WeakMap<ConstructorElValues, ElementDataStore>}
|
|
144
191
|
*/
|
|
145
192
|
const __elementDataMap = new WeakMap();
|
|
193
|
+
/**
|
|
194
|
+
* Internal storage for animation-related data, associated with elements.
|
|
195
|
+
* Used to remember original dimensions (height/width) and other properties
|
|
196
|
+
* so that animations like `slideUp` and `slideDown` can restore or continue
|
|
197
|
+
* smoothly, mimicking jQuery's behavior.
|
|
198
|
+
*
|
|
199
|
+
* Each element is mapped to a plain object with keys such as `origHeight`,
|
|
200
|
+
* `origWidth`, etc.
|
|
201
|
+
*
|
|
202
|
+
* @type {WeakMap<HTMLElement, Record<string, string|number>>}
|
|
203
|
+
*/
|
|
204
|
+
const __elementAnimateData = new WeakMap();
|
|
205
|
+
/**
|
|
206
|
+
* Stores the currently active animation for each element,
|
|
207
|
+
* allowing cancellation or replacement of ongoing animations.
|
|
208
|
+
*
|
|
209
|
+
* @type {WeakMap<HTMLElement, { animation: Animation, id: string }>}
|
|
210
|
+
*/
|
|
211
|
+
const __elementCurrentAnimation = new WeakMap();
|
|
212
|
+
/**
|
|
213
|
+
* Mapping of animation shortcuts to their effect definitions.
|
|
214
|
+
* Similar to jQuery's predefined effects (slideDown, fadeIn, etc.).
|
|
215
|
+
*
|
|
216
|
+
* @typedef {Record<string, string|(string|number)[]>} StyleEffects
|
|
217
|
+
*/
|
|
146
218
|
/**
|
|
147
219
|
* Stores directional collision locks separately.
|
|
148
220
|
* Each direction has its own WeakMap to allow independent locking.
|
|
@@ -199,6 +271,7 @@ const __elemCollision = {
|
|
|
199
271
|
* ...children: (string | HtmlParsed)[] // Text or nested elements
|
|
200
272
|
* ]} HtmlParsed
|
|
201
273
|
*/
|
|
274
|
+
// TITLE: Class Intro
|
|
202
275
|
/**
|
|
203
276
|
* TinyHtml is a utility class that provides static and instance-level methods
|
|
204
277
|
* for precise dimension and position computations on HTML elements.
|
|
@@ -213,6 +286,106 @@ const __elemCollision = {
|
|
|
213
286
|
class TinyHtml {
|
|
214
287
|
/** @typedef {import('../basics/collision.mjs').ObjRect} ObjRect */
|
|
215
288
|
static Utils = { ...TinyCollision };
|
|
289
|
+
/**
|
|
290
|
+
* Parse inline styles into an object.
|
|
291
|
+
* @param {string} styleText
|
|
292
|
+
* @returns {Record<string,string>}
|
|
293
|
+
*/
|
|
294
|
+
static parseStyle(styleText) {
|
|
295
|
+
/** @type {Record<string,string>}} */
|
|
296
|
+
const styles = {};
|
|
297
|
+
styleText.split(';').forEach((rule) => {
|
|
298
|
+
const [prop, value] = rule.split(':').map((s) => s && s.trim());
|
|
299
|
+
if (prop && value) {
|
|
300
|
+
styles[prop] = value;
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
return styles;
|
|
304
|
+
}
|
|
305
|
+
/////////////////////////////////////////////////////////////////////
|
|
306
|
+
/**
|
|
307
|
+
* Flag to determine if element observer should start automatically.
|
|
308
|
+
* @type {boolean}
|
|
309
|
+
*/
|
|
310
|
+
static #autoStartElemObserver = true;
|
|
311
|
+
/**
|
|
312
|
+
* Get the auto-start flag for the observer.
|
|
313
|
+
* @returns {boolean}
|
|
314
|
+
*/
|
|
315
|
+
static get autoStartElemObserver() {
|
|
316
|
+
return TinyHtml.#autoStartElemObserver;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Set the auto-start flag for the observer.
|
|
320
|
+
* @param {boolean} value
|
|
321
|
+
*/
|
|
322
|
+
static set autoStartElemObserver(value) {
|
|
323
|
+
if (typeof value !== 'boolean')
|
|
324
|
+
throw new TypeError('autoStartElemObserver must be a boolean.');
|
|
325
|
+
TinyHtml.#autoStartElemObserver = value;
|
|
326
|
+
}
|
|
327
|
+
/** @type {TinyElementObserver} */
|
|
328
|
+
static #tinyObserver = new TinyElementObserver({
|
|
329
|
+
el: typeof window !== 'undefined' && typeof window.document !== 'undefined'
|
|
330
|
+
? window.document.documentElement
|
|
331
|
+
: undefined,
|
|
332
|
+
initDetectors: [
|
|
333
|
+
// Style Detector
|
|
334
|
+
[
|
|
335
|
+
'tinyStyleEvent',
|
|
336
|
+
(mutation) => {
|
|
337
|
+
if (mutation.type !== 'attributes' ||
|
|
338
|
+
mutation.attributeName !== 'style' ||
|
|
339
|
+
!(mutation.target instanceof HTMLElement))
|
|
340
|
+
return;
|
|
341
|
+
const oldVal = mutation.oldValue || '';
|
|
342
|
+
const newVal = mutation.target.getAttribute('style') || '';
|
|
343
|
+
const oldStyles = TinyHtml.parseStyle(oldVal);
|
|
344
|
+
const newStyles = TinyHtml.parseStyle(newVal);
|
|
345
|
+
const changes = diffStrings(oldStyles, newStyles);
|
|
346
|
+
if (Object.keys(changes.added).length ||
|
|
347
|
+
Object.keys(changes.removed).length ||
|
|
348
|
+
Object.keys(changes.modified).length) {
|
|
349
|
+
mutation.target.dispatchEvent(new CustomEvent('tinyhtml.stylechanged', {
|
|
350
|
+
detail: changes,
|
|
351
|
+
}));
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
],
|
|
355
|
+
// Class Detector
|
|
356
|
+
[
|
|
357
|
+
'tinyClassEvent',
|
|
358
|
+
(mutation) => {
|
|
359
|
+
if (mutation.type !== 'attributes' ||
|
|
360
|
+
mutation.attributeName !== 'class' ||
|
|
361
|
+
!(mutation.target instanceof HTMLElement))
|
|
362
|
+
return;
|
|
363
|
+
const oldVal = mutation.oldValue || '';
|
|
364
|
+
const newVal = mutation.target.className || '';
|
|
365
|
+
const oldClasses = oldVal.split(/\s+/).filter(Boolean);
|
|
366
|
+
const newClasses = newVal.split(/\s+/).filter(Boolean);
|
|
367
|
+
const changes = diffArrayList(oldClasses, newClasses);
|
|
368
|
+
if (changes.added.length || changes.removed.length) {
|
|
369
|
+
mutation.target.dispatchEvent(new CustomEvent('tinyhtml.classchanged', {
|
|
370
|
+
detail: changes,
|
|
371
|
+
}));
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
],
|
|
375
|
+
],
|
|
376
|
+
initCfg: {
|
|
377
|
+
attributeOldValue: true,
|
|
378
|
+
attributes: true,
|
|
379
|
+
subtree: true,
|
|
380
|
+
attributeFilter: ['style', 'class'],
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
/** @returns {TinyElementObserver} */
|
|
384
|
+
static get tinyObserver() {
|
|
385
|
+
return TinyHtml.#tinyObserver;
|
|
386
|
+
}
|
|
387
|
+
///////////////////////////////////////////////////////////////////
|
|
388
|
+
// TITLE: Fetch Html List
|
|
216
389
|
/**
|
|
217
390
|
* Fetches an HTML file from the given URL, parses it to JSON.
|
|
218
391
|
*
|
|
@@ -254,6 +427,7 @@ class TinyHtml {
|
|
|
254
427
|
const nodes = await TinyHtml.fetchHtmlNodes(url, ops);
|
|
255
428
|
return TinyHtml.toTinyElm(nodes);
|
|
256
429
|
}
|
|
430
|
+
// TITLE: Fetch Template List
|
|
257
431
|
/**
|
|
258
432
|
* Converts the content of a <template> to an array of HtmlParsed.
|
|
259
433
|
*
|
|
@@ -291,6 +465,7 @@ class TinyHtml {
|
|
|
291
465
|
static templateToTinyElems(nodes) {
|
|
292
466
|
return TinyHtml.toTinyElm(TinyHtml.templateToNodes(nodes));
|
|
293
467
|
}
|
|
468
|
+
// TITLE: Fetch Json List
|
|
294
469
|
/**
|
|
295
470
|
* Parses a full HTML string into a JSON-like structure.
|
|
296
471
|
*
|
|
@@ -372,6 +547,7 @@ class TinyHtml {
|
|
|
372
547
|
static jsonToTinyElems(jsonArray) {
|
|
373
548
|
return TinyHtml.toTinyElm(TinyHtml.jsonToNodes(jsonArray));
|
|
374
549
|
}
|
|
550
|
+
// TITLE: Element Creator
|
|
375
551
|
/**
|
|
376
552
|
* Creates a new TinyHtml element from a tag name and optional attributes.
|
|
377
553
|
*
|
|
@@ -450,6 +626,8 @@ class TinyHtml {
|
|
|
450
626
|
throw new Error('The HTML string must contain a valid HTML element.');
|
|
451
627
|
return new TinyHtml(template.content.firstChild);
|
|
452
628
|
}
|
|
629
|
+
///////////////////////////////////////////////////
|
|
630
|
+
// TITLE: Query Script
|
|
453
631
|
/**
|
|
454
632
|
* Queries the document for the first element matching the CSS selector and wraps it in a TinyHtml instance.
|
|
455
633
|
*
|
|
@@ -555,6 +733,7 @@ class TinyHtml {
|
|
|
555
733
|
return TinyHtml.getByTagNameNS(localName, namespaceURI, TinyHtml._preElem(this, 'getByTagNameNS'));
|
|
556
734
|
}
|
|
557
735
|
//////////////////////////////////////////////////////////////////
|
|
736
|
+
// TITLE: Element getter
|
|
558
737
|
/**
|
|
559
738
|
* Iterates over all elements, executing the provided callback on each.
|
|
560
739
|
* @param {(element: TinyHtmlAny, index: number, items: TinyHtmlAny[]) => void} callback - Function invoked for each element.
|
|
@@ -613,6 +792,8 @@ class TinyHtml {
|
|
|
613
792
|
getAll() {
|
|
614
793
|
return [...this.#el];
|
|
615
794
|
}
|
|
795
|
+
////////////////////////////////////////////////
|
|
796
|
+
// TITLE: Element Getter (Private) (Pt1)
|
|
616
797
|
/**
|
|
617
798
|
* Returns the current Element held by this instance.
|
|
618
799
|
*
|
|
@@ -645,6 +826,7 @@ class TinyHtml {
|
|
|
645
826
|
return [...this.#el];
|
|
646
827
|
}
|
|
647
828
|
//////////////////////////////////////////////////////
|
|
829
|
+
// TITLE: Element Getter (Private) (Pt2)
|
|
648
830
|
/**
|
|
649
831
|
* Prepares and validates multiple elements against allowed types.
|
|
650
832
|
*
|
|
@@ -928,6 +1110,8 @@ class TinyHtml {
|
|
|
928
1110
|
static _preElemWithDoc(elems, where) {
|
|
929
1111
|
return TinyHtml._preElemTemplate(elems, where, [Element, Document], ['Element', 'Document']);
|
|
930
1112
|
}
|
|
1113
|
+
//////////////////////////////////////////////////////
|
|
1114
|
+
// TITLE: Converter DOM <--> TinyHtml
|
|
931
1115
|
/**
|
|
932
1116
|
* Normalizes and converts one or more DOM elements (or TinyHtml instances)
|
|
933
1117
|
* into an array of `TinyHtml` instances.
|
|
@@ -977,6 +1161,8 @@ class TinyHtml {
|
|
|
977
1161
|
return checkElement([elems]);
|
|
978
1162
|
return checkElement(elems);
|
|
979
1163
|
}
|
|
1164
|
+
//////////////////////////////////////////////////
|
|
1165
|
+
// TITLE: DOM Getter
|
|
980
1166
|
/**
|
|
981
1167
|
* Filters an array of elements based on a selector, function, element, or array of elements.
|
|
982
1168
|
*
|
|
@@ -1169,6 +1355,7 @@ class TinyHtml {
|
|
|
1169
1355
|
return TinyHtml.isSameDom(this, elem);
|
|
1170
1356
|
}
|
|
1171
1357
|
//////////////////////////////////////////////////////////////////
|
|
1358
|
+
// TITLE: Data Manager
|
|
1172
1359
|
/**
|
|
1173
1360
|
* Internal data storage for element information.
|
|
1174
1361
|
* @type {ElementDataStore}
|
|
@@ -1277,6 +1464,7 @@ class TinyHtml {
|
|
|
1277
1464
|
return TinyHtml.setData(this, key, value, isPrivate);
|
|
1278
1465
|
}
|
|
1279
1466
|
//////////////////////////////////////////////////////
|
|
1467
|
+
// TITLE: DOM Getter 2
|
|
1280
1468
|
/**
|
|
1281
1469
|
* Get the sibling element in a given direction.
|
|
1282
1470
|
*
|
|
@@ -1549,6 +1737,8 @@ class TinyHtml {
|
|
|
1549
1737
|
contents() {
|
|
1550
1738
|
return TinyHtml.contents(this);
|
|
1551
1739
|
}
|
|
1740
|
+
////////////////////////////////////////////
|
|
1741
|
+
// TITLE: Clone Dom
|
|
1552
1742
|
/**
|
|
1553
1743
|
* Clone each element.
|
|
1554
1744
|
* @param {TinyNode|TinyNode[]} el
|
|
@@ -1568,6 +1758,8 @@ class TinyHtml {
|
|
|
1568
1758
|
clone(deep) {
|
|
1569
1759
|
return TinyHtml.clone(this, deep)[0];
|
|
1570
1760
|
}
|
|
1761
|
+
//////////////////////////////////////////////////
|
|
1762
|
+
// TITLE: Append Content
|
|
1571
1763
|
/**
|
|
1572
1764
|
* Normalize and validate nodes before DOM insertion.
|
|
1573
1765
|
* Converts TinyNode-like structures or strings into DOM-compatible nodes.
|
|
@@ -1837,6 +2029,7 @@ class TinyHtml {
|
|
|
1837
2029
|
return TinyHtml.replaceAll(this, targets);
|
|
1838
2030
|
}
|
|
1839
2031
|
//////////////////////////////////////////////////////
|
|
2032
|
+
// TITLE: Constructor Base
|
|
1840
2033
|
/**
|
|
1841
2034
|
* The target HTML element for instance-level operations.
|
|
1842
2035
|
* @type {ConstructorElValues[]}
|
|
@@ -1893,6 +2086,7 @@ class TinyHtml {
|
|
|
1893
2086
|
return obj != null && obj === obj.window;
|
|
1894
2087
|
}
|
|
1895
2088
|
/////////////////////////////////////////////////////
|
|
2089
|
+
// TITLE: CSS Stuff
|
|
1896
2090
|
/**
|
|
1897
2091
|
* Returns the full computed CSS styles for the given element.
|
|
1898
2092
|
*
|
|
@@ -2016,6 +2210,7 @@ class TinyHtml {
|
|
|
2016
2210
|
return TinyHtml.cssFloats(this, prop);
|
|
2017
2211
|
}
|
|
2018
2212
|
//////////////////////////////////////////////////////////////////////
|
|
2213
|
+
// TITLE: Style Stuff
|
|
2019
2214
|
/**
|
|
2020
2215
|
* Stores camelCase to kebab-case CSS property aliases.
|
|
2021
2216
|
*
|
|
@@ -2512,6 +2707,7 @@ class TinyHtml {
|
|
|
2512
2707
|
return TinyHtml.clearStyle(this);
|
|
2513
2708
|
}
|
|
2514
2709
|
//////////////////////////////////////////////////////////////////////
|
|
2710
|
+
// TITLE: Focus/Blur
|
|
2515
2711
|
/**
|
|
2516
2712
|
* Focus the element.
|
|
2517
2713
|
*
|
|
@@ -2550,6 +2746,8 @@ class TinyHtml {
|
|
|
2550
2746
|
blur() {
|
|
2551
2747
|
return TinyHtml.blur(this);
|
|
2552
2748
|
}
|
|
2749
|
+
/////////////////////////////////////////////////////////
|
|
2750
|
+
// TITLE: Select
|
|
2553
2751
|
/**
|
|
2554
2752
|
* Select the text content of an input or textarea element.
|
|
2555
2753
|
*
|
|
@@ -2572,6 +2770,7 @@ class TinyHtml {
|
|
|
2572
2770
|
select() {
|
|
2573
2771
|
return TinyHtml.select(this);
|
|
2574
2772
|
}
|
|
2773
|
+
// TITLE: Bool Checker
|
|
2575
2774
|
/**
|
|
2576
2775
|
* Interprets a value as a boolean `true` if it matches a common truthy representation.
|
|
2577
2776
|
*
|
|
@@ -2595,6 +2794,7 @@ class TinyHtml {
|
|
|
2595
2794
|
}
|
|
2596
2795
|
}
|
|
2597
2796
|
//////////////////////////////////////////////////////////////////////
|
|
2797
|
+
// TITLE: Window Getter/Setter
|
|
2598
2798
|
/**
|
|
2599
2799
|
* Sets the vertical scroll position of the window.
|
|
2600
2800
|
* @param {number} value - Sets the scroll position.
|
|
@@ -2889,29 +3089,888 @@ class TinyHtml {
|
|
|
2889
3089
|
return TinyHtml.outerWidth(this, includeMargin);
|
|
2890
3090
|
}
|
|
2891
3091
|
//////////////////////////////////////////////////
|
|
3092
|
+
// TITLE: Animate DOM (Data)
|
|
2892
3093
|
/**
|
|
2893
|
-
*
|
|
3094
|
+
* Retrieves stored animation data for a given element and key.
|
|
3095
|
+
* If no data exists yet, initializes storage for that element.
|
|
2894
3096
|
*
|
|
2895
|
-
* @
|
|
2896
|
-
* @param {
|
|
2897
|
-
* @
|
|
2898
|
-
* @param {number | KeyframeAnimationOptions} [ops] - Timing or configuration options for the animation.
|
|
2899
|
-
* @returns {T}
|
|
3097
|
+
* @param {HTMLElement} el - The element whose data should be retrieved.
|
|
3098
|
+
* @param {string} where - The key to read (e.g., "origHeight").
|
|
3099
|
+
* @returns {string|number|undefined} - The stored value, or undefined.
|
|
2900
3100
|
*/
|
|
2901
|
-
static
|
|
2902
|
-
|
|
2903
|
-
|
|
3101
|
+
static getAnimateData(el, where) {
|
|
3102
|
+
let dataset = __elementAnimateData.get(el);
|
|
3103
|
+
if (!dataset) {
|
|
3104
|
+
dataset = {};
|
|
3105
|
+
__elementAnimateData.set(el, dataset);
|
|
3106
|
+
}
|
|
3107
|
+
return dataset[where];
|
|
3108
|
+
}
|
|
3109
|
+
/**
|
|
3110
|
+
* Stores animation data for a given element and key.
|
|
3111
|
+
* Used to cache original size/values for animations.
|
|
3112
|
+
*
|
|
3113
|
+
* @param {HTMLElement} el - The element whose data should be set.
|
|
3114
|
+
* @param {string} where - The key to store under (e.g., "origHeight").
|
|
3115
|
+
* @param {string|number} value - The value to store.
|
|
3116
|
+
*/
|
|
3117
|
+
static setAnimateData(el, where, value) {
|
|
3118
|
+
if (!(el instanceof HTMLElement))
|
|
3119
|
+
throw new TypeError('setAnimateData: el must be an HTMLElement.');
|
|
3120
|
+
if (typeof where !== 'string')
|
|
3121
|
+
throw new TypeError('setAnimateData: where must be a string.');
|
|
3122
|
+
if (!(typeof value === 'string' || typeof value === 'number'))
|
|
3123
|
+
throw new TypeError('setAnimateData: value must be a string or number.');
|
|
3124
|
+
let dataset = __elementAnimateData.get(el);
|
|
3125
|
+
if (!dataset) {
|
|
3126
|
+
dataset = {};
|
|
3127
|
+
__elementAnimateData.set(el, dataset);
|
|
3128
|
+
}
|
|
3129
|
+
dataset[where] = value;
|
|
3130
|
+
}
|
|
3131
|
+
// TITLE: Animate DOM (cancelOldStyleFx)
|
|
3132
|
+
/**
|
|
3133
|
+
* Global configuration flag controlling whether old style-based animations
|
|
3134
|
+
* are cancelled before a new one starts. Defaults to true.
|
|
3135
|
+
*
|
|
3136
|
+
* @type {boolean}
|
|
3137
|
+
*/
|
|
3138
|
+
static #cancelOldStyleFx = true;
|
|
3139
|
+
/**
|
|
3140
|
+
* Returns the current global setting for cancelling old style-based animations.
|
|
3141
|
+
*
|
|
3142
|
+
* @returns {boolean} True if old animations are cancelled by default, false otherwise.
|
|
3143
|
+
*/
|
|
3144
|
+
static get cancelOldStyleFx() {
|
|
3145
|
+
return TinyHtml.#cancelOldStyleFx;
|
|
3146
|
+
}
|
|
3147
|
+
/**
|
|
3148
|
+
* Updates the global setting that determines whether old style-based animations
|
|
3149
|
+
* are cancelled before new ones start.
|
|
3150
|
+
*
|
|
3151
|
+
* @param {boolean} value - True to cancel old animations by default, false to keep them running.
|
|
3152
|
+
* @throws {TypeError} If the value is not a boolean.
|
|
3153
|
+
*/
|
|
3154
|
+
static set cancelOldStyleFx(value) {
|
|
3155
|
+
if (typeof value !== 'boolean')
|
|
3156
|
+
throw new TypeError('Expected a boolean value.');
|
|
3157
|
+
TinyHtml.#cancelOldStyleFx = value;
|
|
3158
|
+
}
|
|
3159
|
+
// TITLE: Animate DOM (styleFxSpeeds)
|
|
3160
|
+
/**
|
|
3161
|
+
* Predefined animation speed options, inspired by jQuery.fx.speeds.
|
|
3162
|
+
* Each entry can be either a number (duration in ms) or a KeyframeAnimationOptions object.
|
|
3163
|
+
*
|
|
3164
|
+
* Usage example:
|
|
3165
|
+
* TinyHtml.animate(el, keyframes, TinyHtml.#fxSpeeds.fast);
|
|
3166
|
+
* TinyHtml.slideDown(el, TinyHtml.#fxSpeeds.slow);
|
|
3167
|
+
*
|
|
3168
|
+
* @type {Record<string, number | KeyframeAnimationOptions>}
|
|
3169
|
+
*/
|
|
3170
|
+
static #styleFxSpeeds = {
|
|
3171
|
+
slow: { duration: 600, easing: 'ease' },
|
|
3172
|
+
fast: { duration: 200, easing: 'ease-out' },
|
|
3173
|
+
_default: { duration: 400, easing: 'linear' },
|
|
3174
|
+
};
|
|
3175
|
+
/**
|
|
3176
|
+
* Get a cloned copy of predefined animation speeds.
|
|
3177
|
+
* @returns {Record<string, number | KeyframeAnimationOptions>}
|
|
3178
|
+
*/
|
|
3179
|
+
static get styleFxSpeeds() {
|
|
3180
|
+
/** @type {Record<string, number | KeyframeAnimationOptions>} */
|
|
3181
|
+
const data = {};
|
|
3182
|
+
for (const name in TinyHtml.#styleFxSpeeds) {
|
|
3183
|
+
const info = TinyHtml.#styleFxSpeeds[name];
|
|
3184
|
+
data[name] = typeof info === 'object' ? { ...info } : info;
|
|
3185
|
+
}
|
|
3186
|
+
return data;
|
|
3187
|
+
}
|
|
3188
|
+
/**
|
|
3189
|
+
* Replace the predefined animation speeds.
|
|
3190
|
+
* @param {Record<string, number | KeyframeAnimationOptions>} speeds
|
|
3191
|
+
* @throws {TypeError} If input is not a valid object of speed definitions.
|
|
3192
|
+
*/
|
|
3193
|
+
static set styleFxSpeeds(speeds) {
|
|
3194
|
+
if (typeof speeds !== 'object' || speeds === null || Array.isArray(speeds))
|
|
3195
|
+
throw new TypeError('styleFxSpeeds must be an object.');
|
|
3196
|
+
for (const [k, v] of Object.entries(speeds)) {
|
|
3197
|
+
if (!(typeof v === 'number' || typeof v === 'object'))
|
|
3198
|
+
throw new TypeError(`styleFxSpeeds["${k}"] must be a number or KeyframeAnimationOptions.`);
|
|
3199
|
+
}
|
|
3200
|
+
TinyHtml.#styleFxSpeeds = {};
|
|
3201
|
+
for (const [k, v] of Object.entries(speeds))
|
|
3202
|
+
TinyHtml.setStyleFxSpeed(k, v);
|
|
3203
|
+
}
|
|
3204
|
+
/**
|
|
3205
|
+
* Get a predefined animation speed by name.
|
|
3206
|
+
*
|
|
3207
|
+
* @param {string} name - The name of the speed entry.
|
|
3208
|
+
* @returns {number | KeyframeAnimationOptions | undefined} A cloned value of the speed entry, or undefined if not found.
|
|
3209
|
+
*/
|
|
3210
|
+
static getStyleFxSpeed(name) {
|
|
3211
|
+
if (typeof name !== 'string')
|
|
3212
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3213
|
+
const spd = TinyHtml.#styleFxSpeeds[name];
|
|
3214
|
+
return typeof spd === 'object' ? { ...spd } : spd;
|
|
3215
|
+
}
|
|
3216
|
+
/**
|
|
3217
|
+
* Set or overwrite a predefined animation speed.
|
|
3218
|
+
*
|
|
3219
|
+
* @param {string} name - The name of the speed entry.
|
|
3220
|
+
* @param {number | KeyframeAnimationOptions} value - The value to store.
|
|
3221
|
+
* @throws {TypeError} If value is not a number or KeyframeAnimationOptions object.
|
|
3222
|
+
*/
|
|
3223
|
+
static setStyleFxSpeed(name, value) {
|
|
3224
|
+
if (typeof name !== 'string')
|
|
3225
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3226
|
+
if (!(typeof value === 'number' || typeof value === 'object'))
|
|
3227
|
+
throw new TypeError('styleFxSpeed must be a number or KeyframeAnimationOptions');
|
|
3228
|
+
TinyHtml.#styleFxSpeeds[name] = typeof value === 'object' ? { ...value } : value;
|
|
3229
|
+
}
|
|
3230
|
+
/**
|
|
3231
|
+
* Delete a predefined animation speed by name.
|
|
3232
|
+
*
|
|
3233
|
+
* @param {string} name - The name of the speed entry to delete.
|
|
3234
|
+
* @returns {boolean} True if the property was deleted, false otherwise.
|
|
3235
|
+
*/
|
|
3236
|
+
static deleteStyleFxSpeed(name) {
|
|
3237
|
+
if (typeof name !== 'string')
|
|
3238
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3239
|
+
return delete TinyHtml.#styleFxSpeeds[name];
|
|
3240
|
+
}
|
|
3241
|
+
/**
|
|
3242
|
+
* Check if a predefined animation speed exists.
|
|
3243
|
+
*
|
|
3244
|
+
* @param {string} name - The name of the speed entry to check.
|
|
3245
|
+
* @returns {boolean} True if the speed entry exists, false otherwise.
|
|
3246
|
+
*/
|
|
3247
|
+
static hasStyleFxSpeed(name) {
|
|
3248
|
+
if (typeof name !== 'string')
|
|
3249
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3250
|
+
return Object.prototype.hasOwnProperty.call(TinyHtml.#styleFxSpeeds, name);
|
|
3251
|
+
}
|
|
3252
|
+
// TITLE: Animate DOM (cssExpand)
|
|
3253
|
+
/**
|
|
3254
|
+
* CSS expansion shorthand used by genStyleFx to include margin/padding values.
|
|
3255
|
+
* @typedef {['Top', 'Right', 'Bottom', 'Left']}
|
|
3256
|
+
*/
|
|
3257
|
+
static #cssExpand = ['Top', 'Right', 'Bottom', 'Left'];
|
|
3258
|
+
// TITLE: Animate DOM (styleEffects)
|
|
3259
|
+
/**
|
|
3260
|
+
* Generate shortcuts
|
|
3261
|
+
* @type {Record<string, StyleEffects>}
|
|
3262
|
+
*/
|
|
3263
|
+
static #styleEffects = {
|
|
3264
|
+
slideDown: TinyHtml.genStyleFx('show'),
|
|
3265
|
+
slideUp: TinyHtml.genStyleFx('hide'),
|
|
3266
|
+
fadeIn: { opacity: 'show' },
|
|
3267
|
+
fadeOut: { opacity: 'hide' },
|
|
3268
|
+
};
|
|
3269
|
+
/**
|
|
3270
|
+
* Returns a deep-cloned copy of the registered style effects.
|
|
3271
|
+
*
|
|
3272
|
+
* @returns {Record<string, StyleEffects>}
|
|
3273
|
+
*/
|
|
3274
|
+
static get styleEffects() {
|
|
3275
|
+
return JSON.parse(JSON.stringify(TinyHtml.#styleEffects));
|
|
3276
|
+
}
|
|
3277
|
+
/**
|
|
3278
|
+
* Replace the entire styleEffects map with a new one.
|
|
3279
|
+
*
|
|
3280
|
+
* @param {Record<string, StyleEffects>} value
|
|
3281
|
+
*/
|
|
3282
|
+
static set styleEffects(value) {
|
|
3283
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
3284
|
+
throw new TypeError('styleEffects must be an object');
|
|
3285
|
+
for (const name in value) {
|
|
3286
|
+
for (const [prop, mode] of Object.entries(value[name])) {
|
|
3287
|
+
if (typeof mode !== 'string' &&
|
|
3288
|
+
(!Array.isArray(mode) ||
|
|
3289
|
+
!mode.every((v) => typeof v === 'string' || typeof v === 'number')))
|
|
3290
|
+
throw new TypeError(`Invalid styleEffect["${prop}"]`);
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
TinyHtml.#styleEffects = {};
|
|
3294
|
+
for (const name in value)
|
|
3295
|
+
TinyHtml.setStyleEffect(name, value[name]);
|
|
3296
|
+
}
|
|
3297
|
+
/**
|
|
3298
|
+
* Get a deep-cloned style effect by name.
|
|
3299
|
+
* @param {string} name
|
|
3300
|
+
* @returns {StyleEffects|undefined}
|
|
3301
|
+
*/
|
|
3302
|
+
static getStyleEffect(name) {
|
|
3303
|
+
if (typeof name !== 'string')
|
|
3304
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3305
|
+
const eff = TinyHtml.#styleEffects[name];
|
|
3306
|
+
return eff ? JSON.parse(JSON.stringify(eff)) : undefined;
|
|
3307
|
+
}
|
|
3308
|
+
/**
|
|
3309
|
+
* Set or replace a style effect.
|
|
3310
|
+
* @param {string} name
|
|
3311
|
+
* @param {StyleEffects} value
|
|
3312
|
+
*/
|
|
3313
|
+
static setStyleEffect(name, value) {
|
|
3314
|
+
if (typeof name !== 'string')
|
|
3315
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3316
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
3317
|
+
throw new TypeError('styleEffect must be an object');
|
|
3318
|
+
}
|
|
3319
|
+
/** @type {StyleEffects} */
|
|
3320
|
+
const copy = {};
|
|
3321
|
+
for (const [prop, mode] of Object.entries(value)) {
|
|
3322
|
+
if (typeof mode === 'string') {
|
|
3323
|
+
copy[prop] = mode;
|
|
3324
|
+
}
|
|
3325
|
+
else if (Array.isArray(mode) &&
|
|
3326
|
+
mode.every((v) => typeof v === 'string' || typeof v === 'number')) {
|
|
3327
|
+
copy[prop] = [...mode];
|
|
3328
|
+
}
|
|
3329
|
+
else {
|
|
3330
|
+
throw new TypeError(`Invalid styleEffect["${prop}"]`);
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
TinyHtml.#styleEffects[name] = copy;
|
|
3334
|
+
}
|
|
3335
|
+
/**
|
|
3336
|
+
* Delete a style effect.
|
|
3337
|
+
* @param {string} name
|
|
3338
|
+
* @returns {boolean} True if deleted.
|
|
3339
|
+
*/
|
|
3340
|
+
static deleteStyleEffect(name) {
|
|
3341
|
+
if (typeof name !== 'string')
|
|
3342
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3343
|
+
return delete TinyHtml.#styleEffects[name];
|
|
3344
|
+
}
|
|
3345
|
+
/**
|
|
3346
|
+
* Check if a style effect exists.
|
|
3347
|
+
* @param {string} name
|
|
3348
|
+
* @returns {boolean}
|
|
3349
|
+
*/
|
|
3350
|
+
static hasStyleEffect(name) {
|
|
3351
|
+
if (typeof name !== 'string')
|
|
3352
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3353
|
+
return Object.prototype.hasOwnProperty.call(TinyHtml.#styleEffects, name);
|
|
3354
|
+
}
|
|
3355
|
+
// TITLE: Animate DOM (styleEffectInverse)
|
|
3356
|
+
/**
|
|
3357
|
+
* Style Effect Inverse Values
|
|
3358
|
+
* @type {Record<string, string>}
|
|
3359
|
+
*/
|
|
3360
|
+
static #styleEffectInverse = {
|
|
3361
|
+
slideDown: 'slideUp',
|
|
3362
|
+
slideUp: 'slideDown',
|
|
3363
|
+
fadeIn: 'fadeOut',
|
|
3364
|
+
fadeOut: 'fadeIn',
|
|
3365
|
+
};
|
|
3366
|
+
/**
|
|
3367
|
+
* Get a cloned copy of style inverse values.
|
|
3368
|
+
* @returns {Record<string, string>}
|
|
3369
|
+
*/
|
|
3370
|
+
static get styleEffectInverse() {
|
|
3371
|
+
return { ...TinyHtml.#styleEffectInverse };
|
|
3372
|
+
}
|
|
3373
|
+
/**
|
|
3374
|
+
* Replace the style inverse values.
|
|
3375
|
+
* @param {Record<string, string>} values
|
|
3376
|
+
* @throws {TypeError} If not a valid object with functions as values.
|
|
3377
|
+
*/
|
|
3378
|
+
static set styleEffectInverse(values) {
|
|
3379
|
+
if (typeof values !== 'object' || values === null)
|
|
3380
|
+
throw new TypeError('styleEffectInverse must be an object.');
|
|
3381
|
+
for (const [k, v] of Object.entries(values)) {
|
|
3382
|
+
if (typeof v !== 'string')
|
|
3383
|
+
throw new TypeError(`styleEffectInverse["${k}"] must be a string.`);
|
|
3384
|
+
}
|
|
3385
|
+
TinyHtml.#styleEffectInverse = {};
|
|
3386
|
+
for (const [k, v] of Object.entries(values))
|
|
3387
|
+
TinyHtml.setStyleEffectInverse(k, v);
|
|
3388
|
+
}
|
|
3389
|
+
/**
|
|
3390
|
+
* Get a registered inverse values.
|
|
3391
|
+
*
|
|
3392
|
+
* @param {string} name - The detector name.
|
|
3393
|
+
* @returns {string | null} The value if found, otherwise undefined.
|
|
3394
|
+
*/
|
|
3395
|
+
static getStyleEffectInverse(name) {
|
|
3396
|
+
if (typeof name !== 'string')
|
|
3397
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3398
|
+
return TinyHtml.#styleEffectInverse[name] || null;
|
|
3399
|
+
}
|
|
3400
|
+
/**
|
|
3401
|
+
* Register or overwrite a inverse value.
|
|
3402
|
+
*
|
|
3403
|
+
* @param {string} name - The detector name.
|
|
3404
|
+
* @param {string} value - The inverse value.
|
|
3405
|
+
* @throws {TypeError} If fn is not a string.
|
|
3406
|
+
*/
|
|
3407
|
+
static setStyleEffectInverse(name, value) {
|
|
3408
|
+
if (typeof name !== 'string')
|
|
3409
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3410
|
+
if (typeof value !== 'string')
|
|
3411
|
+
throw new TypeError(`styleEffectInverse["${name}"] must be a string.`);
|
|
3412
|
+
TinyHtml.#styleEffectInverse[name] = value;
|
|
3413
|
+
}
|
|
3414
|
+
/**
|
|
3415
|
+
* Delete a inverse value by name.
|
|
3416
|
+
*
|
|
3417
|
+
* @param {string} name - The inverse value name to delete.
|
|
3418
|
+
* @returns {boolean} True if deleted, false otherwise.
|
|
3419
|
+
*/
|
|
3420
|
+
static deleteStyleEffectInverse(name) {
|
|
3421
|
+
if (typeof name !== 'string')
|
|
3422
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3423
|
+
return delete TinyHtml.#styleEffectInverse[name];
|
|
3424
|
+
}
|
|
3425
|
+
/**
|
|
3426
|
+
* Check if a inverse value is registered.
|
|
3427
|
+
*
|
|
3428
|
+
* @param {string} name - The inverse value name to check.
|
|
3429
|
+
* @returns {boolean} True if registered, false otherwise.
|
|
3430
|
+
*/
|
|
3431
|
+
static hasStyleEffectInverse(name) {
|
|
3432
|
+
if (typeof name !== 'string')
|
|
3433
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3434
|
+
return Object.prototype.hasOwnProperty.call(TinyHtml.#styleEffectInverse, name);
|
|
3435
|
+
}
|
|
3436
|
+
// TITLE: Animate DOM (styleEffectsRd)
|
|
3437
|
+
/**
|
|
3438
|
+
* Style Effect Repeat Detector
|
|
3439
|
+
* @type {Record<string, StyleEffectsRdFn>}
|
|
3440
|
+
*/
|
|
3441
|
+
static #styleEffectsRd = {
|
|
3442
|
+
slideDown: (effects) => effects.height[0] === effects.height[1] &&
|
|
3443
|
+
(!effects.width || effects.width[0] === effects.width[1]),
|
|
3444
|
+
slideUp: (effects) => effects.height[0] === effects.height[1] &&
|
|
3445
|
+
(!effects.width || effects.width[0] === effects.width[1]),
|
|
3446
|
+
fadeIn: (effects) => Number(effects.opacity[0]) === Number(effects.opacity[1]),
|
|
3447
|
+
fadeOut: (effects) => Number(effects.opacity[0]) === Number(effects.opacity[1]),
|
|
3448
|
+
};
|
|
3449
|
+
/**
|
|
3450
|
+
* Get a cloned copy of style effects repeat detectors.
|
|
3451
|
+
* @returns {Record<string, StyleEffectsRdFn>}
|
|
3452
|
+
*/
|
|
3453
|
+
static get styleEffectsRd() {
|
|
3454
|
+
return { ...TinyHtml.#styleEffectsRd };
|
|
3455
|
+
}
|
|
3456
|
+
/**
|
|
3457
|
+
* Replace the style effects repeat detectors.
|
|
3458
|
+
* @param {Record<string, StyleEffectsRdFn>} detectors
|
|
3459
|
+
* @throws {TypeError} If not a valid object with functions as values.
|
|
3460
|
+
*/
|
|
3461
|
+
static set styleEffectsRd(detectors) {
|
|
3462
|
+
if (typeof detectors !== 'object' || detectors === null)
|
|
3463
|
+
throw new TypeError('styleEffectsRd must be an object.');
|
|
3464
|
+
for (const [k, v] of Object.entries(detectors)) {
|
|
3465
|
+
if (typeof v !== 'function')
|
|
3466
|
+
throw new TypeError(`styleEffectsRd["${k}"] must be a function.`);
|
|
3467
|
+
}
|
|
3468
|
+
TinyHtml.#styleEffectsRd = {};
|
|
3469
|
+
for (const [k, v] of Object.entries(detectors))
|
|
3470
|
+
TinyHtml.setStyleEffectRd(k, v);
|
|
3471
|
+
}
|
|
3472
|
+
/**
|
|
3473
|
+
* Get a registered repeat detector function by name.
|
|
3474
|
+
*
|
|
3475
|
+
* @param {string} name - The detector name.
|
|
3476
|
+
* @returns {StyleEffectsRdFn | null} The function if found, otherwise undefined.
|
|
3477
|
+
*/
|
|
3478
|
+
static getStyleEffectRd(name) {
|
|
3479
|
+
if (typeof name !== 'string')
|
|
3480
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3481
|
+
return TinyHtml.#styleEffectsRd[name] || null;
|
|
3482
|
+
}
|
|
3483
|
+
/**
|
|
3484
|
+
* Register or overwrite a repeat detector function.
|
|
3485
|
+
*
|
|
3486
|
+
* @param {string} name - The detector name.
|
|
3487
|
+
* @param {StyleEffectsRdFn} fn - The detector function.
|
|
3488
|
+
* @throws {TypeError} If fn is not a function.
|
|
3489
|
+
*/
|
|
3490
|
+
static setStyleEffectRd(name, fn) {
|
|
3491
|
+
if (typeof name !== 'string')
|
|
3492
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3493
|
+
if (typeof fn !== 'function')
|
|
3494
|
+
throw new TypeError(`styleEffectsRd["${name}"] must be a function.`);
|
|
3495
|
+
TinyHtml.#styleEffectsRd[name] = fn;
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Delete a repeat detector function by name.
|
|
3499
|
+
*
|
|
3500
|
+
* @param {string} name - The detector name to delete.
|
|
3501
|
+
* @returns {boolean} True if deleted, false otherwise.
|
|
3502
|
+
*/
|
|
3503
|
+
static deleteStyleEffectRd(name) {
|
|
3504
|
+
if (typeof name !== 'string')
|
|
3505
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3506
|
+
return delete TinyHtml.#styleEffectsRd[name];
|
|
3507
|
+
}
|
|
3508
|
+
/**
|
|
3509
|
+
* Check if a repeat detector function is registered.
|
|
3510
|
+
*
|
|
3511
|
+
* @param {string} name - The detector name to check.
|
|
3512
|
+
* @returns {boolean} True if registered, false otherwise.
|
|
3513
|
+
*/
|
|
3514
|
+
static hasStyleEffectRd(name) {
|
|
3515
|
+
if (typeof name !== 'string')
|
|
3516
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3517
|
+
return Object.prototype.hasOwnProperty.call(TinyHtml.#styleEffectsRd, name);
|
|
3518
|
+
}
|
|
3519
|
+
// TITLE: Animate DOM (styleEffectsPromps)
|
|
3520
|
+
/**
|
|
3521
|
+
* Effect property handlers for show, hide, and toggle.
|
|
3522
|
+
* Each function builds keyframes depending on the property being animated.
|
|
3523
|
+
*
|
|
3524
|
+
* @type {StyleEffectsProps}
|
|
3525
|
+
*/
|
|
3526
|
+
static #styleEffectsProps = {
|
|
3527
|
+
show: (el, keyframes, prop, style) => {
|
|
3528
|
+
if (prop === 'height' || prop === 'width') {
|
|
3529
|
+
const targetSize = TinyHtml.getAnimateData(el, `orig${prop}`) || el.scrollHeight + 'px';
|
|
3530
|
+
TinyHtml.setAnimateData(el, `orig${prop}`, targetSize);
|
|
3531
|
+
const current = style[prop]; // pega valor atual
|
|
3532
|
+
keyframes[prop] = [current, targetSize];
|
|
3533
|
+
}
|
|
3534
|
+
else if (prop.startsWith('margin') || prop.startsWith('padding')) {
|
|
3535
|
+
// @ts-ignore
|
|
3536
|
+
const orig = TinyHtml.getAnimateData(el, prop) || style[prop];
|
|
3537
|
+
TinyHtml.setAnimateData(el, prop, orig);
|
|
3538
|
+
keyframes[prop] = ['0px', orig];
|
|
3539
|
+
}
|
|
3540
|
+
else if (prop === 'opacity') {
|
|
3541
|
+
const current = style.opacity;
|
|
3542
|
+
keyframes[prop] = [current, 1];
|
|
3543
|
+
}
|
|
3544
|
+
else {
|
|
3545
|
+
// @ts-ignore
|
|
3546
|
+
const current = TinyHtml.getAnimateData(el, prop) || style[prop];
|
|
3547
|
+
TinyHtml.setAnimateData(el, prop, current);
|
|
3548
|
+
// @ts-ignore
|
|
3549
|
+
keyframes[prop] = [current, style[prop] || 1];
|
|
3550
|
+
}
|
|
3551
|
+
},
|
|
3552
|
+
hide: (el, keyframes, prop, style) => {
|
|
3553
|
+
if (prop === 'height' || prop === 'width') {
|
|
3554
|
+
const targetSize = TinyHtml.getAnimateData(el, `orig${prop}`) || style[prop];
|
|
3555
|
+
TinyHtml.setAnimateData(el, `orig${prop}`, targetSize);
|
|
3556
|
+
const current = style[prop]; // pega valor atual
|
|
3557
|
+
keyframes[prop] = [current, 0];
|
|
3558
|
+
}
|
|
3559
|
+
else if (prop.startsWith('margin') || prop.startsWith('padding')) {
|
|
3560
|
+
// @ts-ignore
|
|
3561
|
+
const orig = TinyHtml.getAnimateData(el, prop) || style[prop];
|
|
3562
|
+
TinyHtml.setAnimateData(el, prop, orig);
|
|
3563
|
+
// @ts-ignore
|
|
3564
|
+
keyframes[prop] = [style[prop], '0px'];
|
|
3565
|
+
}
|
|
3566
|
+
else if (prop === 'opacity') {
|
|
3567
|
+
const current = style.opacity;
|
|
3568
|
+
keyframes[prop] = [current, 0];
|
|
3569
|
+
}
|
|
3570
|
+
else {
|
|
3571
|
+
// @ts-ignore
|
|
3572
|
+
const current = TinyHtml.getAnimateData(el, prop) || style[prop];
|
|
3573
|
+
TinyHtml.setAnimateData(el, prop, current);
|
|
3574
|
+
keyframes[prop] = [current, 0];
|
|
3575
|
+
}
|
|
3576
|
+
},
|
|
3577
|
+
};
|
|
3578
|
+
/**
|
|
3579
|
+
* Returns a shallow-cloned copy of the property effect handlers.
|
|
3580
|
+
*
|
|
3581
|
+
* @returns {StyleEffectsProps}
|
|
3582
|
+
*/
|
|
3583
|
+
static get styleEffectsProps() {
|
|
3584
|
+
return { ...TinyHtml.#styleEffectsProps };
|
|
3585
|
+
}
|
|
3586
|
+
/**
|
|
3587
|
+
* Replace the entire styleEffectsProps map with a new one.
|
|
3588
|
+
*
|
|
3589
|
+
* @param {StyleEffectsProps} value
|
|
3590
|
+
*/
|
|
3591
|
+
static set styleEffectsProps(value) {
|
|
3592
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
3593
|
+
throw new TypeError('styleEffectsProps must be an object');
|
|
3594
|
+
for (const [k, fn] of Object.entries(value)) {
|
|
3595
|
+
if (typeof fn !== 'function')
|
|
3596
|
+
throw new TypeError(`styleEffectsProps["${k}"] must be a function`);
|
|
3597
|
+
}
|
|
3598
|
+
TinyHtml.#styleEffectsProps = {};
|
|
3599
|
+
for (const [k, fn] of Object.entries(value))
|
|
3600
|
+
TinyHtml.setStyleEffectProp(k, fn);
|
|
3601
|
+
}
|
|
3602
|
+
/**
|
|
3603
|
+
* Get a style effect property handler by name.
|
|
3604
|
+
*
|
|
3605
|
+
* @param {string} name - The property handler name.
|
|
3606
|
+
* @returns {StyleEffectsFn | null} The handler function, or undefined if not found.
|
|
3607
|
+
*/
|
|
3608
|
+
static getStyleEffectProp(name) {
|
|
3609
|
+
if (typeof name !== 'string')
|
|
3610
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3611
|
+
return TinyHtml.#styleEffectsProps[name] || null;
|
|
3612
|
+
}
|
|
3613
|
+
/**
|
|
3614
|
+
* Register or overwrite a style effect property handler.
|
|
3615
|
+
*
|
|
3616
|
+
* @param {string} name - The property handler name.
|
|
3617
|
+
* @param {StyleEffectsFn} fn - The handler function.
|
|
3618
|
+
* @throws {TypeError} If fn is not a function.
|
|
3619
|
+
*/
|
|
3620
|
+
static setStyleEffectProp(name, fn) {
|
|
3621
|
+
if (typeof name !== 'string')
|
|
3622
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3623
|
+
if (typeof fn !== 'function')
|
|
3624
|
+
throw new TypeError(`styleEffectsProps["${name}"] must be a function`);
|
|
3625
|
+
TinyHtml.#styleEffectsProps[name] = fn;
|
|
3626
|
+
}
|
|
3627
|
+
/**
|
|
3628
|
+
* Delete a style effect property handler by name.
|
|
3629
|
+
*
|
|
3630
|
+
* @param {string} name - The property handler name to delete.
|
|
3631
|
+
* @returns {boolean} True if deleted, false otherwise.
|
|
3632
|
+
*/
|
|
3633
|
+
static deleteStyleEffectProp(name) {
|
|
3634
|
+
if (typeof name !== 'string')
|
|
3635
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3636
|
+
return delete TinyHtml.#styleEffectsProps[name];
|
|
3637
|
+
}
|
|
3638
|
+
/**
|
|
3639
|
+
* Check if a style effect property handler exists.
|
|
3640
|
+
*
|
|
3641
|
+
* @param {string} name - The property handler name to check.
|
|
3642
|
+
* @returns {boolean} True if the handler exists, false otherwise.
|
|
3643
|
+
*/
|
|
3644
|
+
static hasStyleEffectProp(name) {
|
|
3645
|
+
if (typeof name !== 'string')
|
|
3646
|
+
throw new TypeError('The "name" parameter must be a string.');
|
|
3647
|
+
return Object.prototype.hasOwnProperty.call(TinyHtml.#styleEffectsProps, name);
|
|
3648
|
+
}
|
|
3649
|
+
// TITLE: Gen Style FX Manager
|
|
3650
|
+
/**
|
|
3651
|
+
* Generates effect parameters to create standard animations.
|
|
3652
|
+
*
|
|
3653
|
+
* @param {string} type - The effect type.
|
|
3654
|
+
* @param {boolean} [includeWidth=false] - Whether width (and opacity) should be included.
|
|
3655
|
+
* @returns {Record<string, string>} - A mapping of CSS properties to effect type.
|
|
3656
|
+
*/
|
|
3657
|
+
static genStyleFx(type, includeWidth = false) {
|
|
3658
|
+
if (typeof type !== 'string')
|
|
3659
|
+
throw new TypeError('genStyleFx: type must be a string.');
|
|
3660
|
+
if (typeof includeWidth !== 'boolean')
|
|
3661
|
+
throw new TypeError('genStyleFx: includeWidth must be a boolean.');
|
|
3662
|
+
/** @type {string} */
|
|
3663
|
+
let which;
|
|
3664
|
+
/** @type {number} */
|
|
3665
|
+
let i = 0;
|
|
3666
|
+
/** @type {Record<string, string>} */
|
|
3667
|
+
const attrs = { height: type };
|
|
3668
|
+
const includeWidthNb = includeWidth ? 1 : 0;
|
|
3669
|
+
for (; i < 4; i += 2 - includeWidthNb) {
|
|
3670
|
+
which = TinyHtml.#cssExpand[i];
|
|
3671
|
+
attrs['margin' + which] = type;
|
|
3672
|
+
attrs['padding' + which] = type;
|
|
3673
|
+
}
|
|
3674
|
+
if (includeWidth) {
|
|
3675
|
+
attrs.opacity = type;
|
|
3676
|
+
attrs.width = type;
|
|
3677
|
+
}
|
|
3678
|
+
return attrs;
|
|
3679
|
+
}
|
|
3680
|
+
/**
|
|
3681
|
+
* Applies style-based effects (slide, fade) to one or more elements.
|
|
3682
|
+
* Converts abstract effect definitions (e.g., `{ height: "show" }`)
|
|
3683
|
+
* into concrete Web Animations API keyframes.
|
|
3684
|
+
*
|
|
3685
|
+
* @param {TinyHtmlElement|TinyHtmlElement[]} el - A single element or an array of elements.
|
|
3686
|
+
* @param {string} id - The style effect id.
|
|
3687
|
+
* @param {StyleEffects} props - The style effect definition.
|
|
3688
|
+
* @param {number | KeyframeAnimationOptions | string} [ops] - Timing options.
|
|
3689
|
+
* @returns {StyleFxResult}
|
|
3690
|
+
*/
|
|
3691
|
+
static applyStyleFx(el, id, props, ops) {
|
|
3692
|
+
if (typeof id !== 'string')
|
|
3693
|
+
throw new TypeError('applyStyleFx: id must be a string.');
|
|
3694
|
+
if (typeof props !== 'object' || props === null)
|
|
3695
|
+
throw new TypeError('applyStyleFx: props must be a non-null object.');
|
|
3696
|
+
if (ops !== undefined &&
|
|
3697
|
+
!(typeof ops === 'number' ||
|
|
3698
|
+
typeof ops === 'string' ||
|
|
3699
|
+
(typeof ops === 'object' && ops !== null)))
|
|
3700
|
+
throw new TypeError('applyStyleFx: ops must be a number, string, KeyframeAnimationOptions, or undefined.');
|
|
3701
|
+
/** @type {StyleFxResult} */
|
|
3702
|
+
const results = new Map();
|
|
3703
|
+
TinyHtml._preHtmlElems(el, 'applyStyleFx').forEach((first) => {
|
|
3704
|
+
/**
|
|
3705
|
+
* Generate keyframes based on props
|
|
3706
|
+
* @type {AnimationSfxData}
|
|
3707
|
+
*/
|
|
3708
|
+
const keyframes = {};
|
|
3709
|
+
for (const [prop, action] of Object.entries(props)) {
|
|
3710
|
+
if (typeof action === 'string' && this.#styleEffectsProps[action]) {
|
|
3711
|
+
const style = getComputedStyle(first);
|
|
3712
|
+
this.#styleEffectsProps[action](first, keyframes, prop, style);
|
|
3713
|
+
}
|
|
3714
|
+
else if (typeof action === 'object') {
|
|
3715
|
+
keyframes[prop] = action;
|
|
3716
|
+
}
|
|
3717
|
+
else {
|
|
3718
|
+
throw new TypeError(`applyStyleFx: invalid action for prop "${prop}". Must be string or array.`);
|
|
3719
|
+
}
|
|
3720
|
+
}
|
|
3721
|
+
const isRepeat = typeof TinyHtml.#styleEffectsRd[id] === 'function'
|
|
3722
|
+
? TinyHtml.#styleEffectsRd[id](keyframes)
|
|
3723
|
+
: false;
|
|
3724
|
+
if (isRepeat) {
|
|
3725
|
+
results.set(first, null);
|
|
3726
|
+
return;
|
|
3727
|
+
}
|
|
3728
|
+
/**
|
|
3729
|
+
* Build Web Animations keyframe objects
|
|
3730
|
+
* @type {Keyframe[]}
|
|
3731
|
+
*/
|
|
3732
|
+
const kf = [];
|
|
3733
|
+
const numFrames = keyframes[Object.keys(keyframes)[0]].length; // assume que todos têm o mesmo tamanho
|
|
3734
|
+
for (let i = 0; i < numFrames; i++) {
|
|
3735
|
+
/** @type {Keyframe} */
|
|
3736
|
+
const frame = {};
|
|
3737
|
+
for (const prop in keyframes) {
|
|
3738
|
+
frame[prop] = keyframes[prop][i];
|
|
3739
|
+
}
|
|
3740
|
+
kf.push(frame);
|
|
3741
|
+
}
|
|
3742
|
+
results.set(first, TinyHtml.animate(el, kf, ops, id)[0]);
|
|
3743
|
+
});
|
|
3744
|
+
return results;
|
|
3745
|
+
}
|
|
3746
|
+
/**
|
|
3747
|
+
* Applies style-based effects (slide, fade) to one or more elements.
|
|
3748
|
+
* Converts abstract effect definitions (e.g., `{ height: "show" }`)
|
|
3749
|
+
* into concrete Web Animations API keyframes.
|
|
3750
|
+
*
|
|
3751
|
+
* @param {string} id - The style effect id.
|
|
3752
|
+
* @param {StyleEffects} props - The style effect definition.
|
|
3753
|
+
* @param {number | KeyframeAnimationOptions} [ops] - Timing options.
|
|
3754
|
+
* @returns {StyleFxResult}
|
|
3755
|
+
*/
|
|
3756
|
+
applyStyleFx(id, props, ops) {
|
|
3757
|
+
return TinyHtml.applyStyleFx(this, id, props, ops);
|
|
3758
|
+
}
|
|
3759
|
+
// TITLE: Animate Stuff
|
|
3760
|
+
/**
|
|
3761
|
+
* Get the current animation entry for a given element.
|
|
3762
|
+
* @param {HTMLElement} el - The target element.
|
|
3763
|
+
* @returns {string|null|undefined} Returns string or null to animation.
|
|
3764
|
+
*/
|
|
3765
|
+
static getCurrentAnimationId(el) {
|
|
3766
|
+
if (!(el instanceof HTMLElement))
|
|
3767
|
+
throw new TypeError('Expected an HTMLElement.');
|
|
3768
|
+
return __elementCurrentAnimation.get(el)?.id ?? undefined;
|
|
2904
3769
|
}
|
|
2905
3770
|
/**
|
|
2906
3771
|
* Applies an animation to one or multiple TinyElement instances.
|
|
2907
3772
|
*
|
|
2908
|
-
*
|
|
2909
|
-
*
|
|
2910
|
-
*
|
|
3773
|
+
* If `cancelOldAnim` is true, any currently running animation on the same element
|
|
3774
|
+
* will be cancelled before the new one starts.
|
|
3775
|
+
*
|
|
3776
|
+
* @param {TinyHtmlElement|TinyHtmlElement[]} el - A single TinyElement or an array of TinyElements to animate.
|
|
3777
|
+
* @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes - Keyframes that define the animation.
|
|
3778
|
+
* @param {number | KeyframeAnimationOptions | string} [ops] - Timing or configuration options for the animation.
|
|
3779
|
+
* @param {string|null} [id] - The style effect id.
|
|
3780
|
+
* @param {boolean} [cancelOldAnim=TinyHtml.cancelOldStyleFx] - Whether to cancel previous animations on the same element.
|
|
3781
|
+
* @returns {Animation[]}
|
|
3782
|
+
*/
|
|
3783
|
+
static animate(el, keyframes, ops = TinyHtml.#styleFxSpeeds._default, id = null, cancelOldAnim = TinyHtml.#cancelOldStyleFx) {
|
|
3784
|
+
/** @type {number | KeyframeAnimationOptions} */
|
|
3785
|
+
const fxSpeed = typeof ops === 'string'
|
|
3786
|
+
? (TinyHtml.#styleFxSpeeds[ops] ?? undefined)
|
|
3787
|
+
: typeof ops !== 'number'
|
|
3788
|
+
? { ...ops }
|
|
3789
|
+
: ops;
|
|
3790
|
+
if (typeof fxSpeed === 'object' && typeof fxSpeed.fill === 'undefined') {
|
|
3791
|
+
fxSpeed.fill = 'forwards';
|
|
3792
|
+
}
|
|
3793
|
+
/** @type {Animation[]} */
|
|
3794
|
+
const results = [];
|
|
3795
|
+
TinyHtml._preHtmlElems(el, 'animate').forEach((elem) => {
|
|
3796
|
+
if (cancelOldAnim) {
|
|
3797
|
+
const prevAnim = __elementCurrentAnimation.get(elem);
|
|
3798
|
+
if (prevAnim)
|
|
3799
|
+
prevAnim.animation.cancel();
|
|
3800
|
+
}
|
|
3801
|
+
const anim = elem.animate(keyframes, fxSpeed);
|
|
3802
|
+
results.push(anim);
|
|
3803
|
+
__elementCurrentAnimation.set(elem, { animation: anim, id: id ?? '' });
|
|
3804
|
+
anim.addEventListener('finish', () => {
|
|
3805
|
+
if (__elementCurrentAnimation.get(elem)?.animation === anim)
|
|
3806
|
+
__elementCurrentAnimation.delete(elem);
|
|
3807
|
+
});
|
|
3808
|
+
});
|
|
3809
|
+
return results;
|
|
3810
|
+
}
|
|
3811
|
+
/**
|
|
3812
|
+
* Applies an animation to one or multiple TinyElement instances.
|
|
3813
|
+
*
|
|
3814
|
+
* If `cancelOldAnim` is true, any currently running animation on the same element
|
|
3815
|
+
* will be cancelled before the new one starts.
|
|
3816
|
+
*
|
|
3817
|
+
* @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes - Keyframes that define the animation.
|
|
3818
|
+
* @param {number | KeyframeAnimationOptions | string} [ops] - Timing or configuration options for the animation.
|
|
3819
|
+
* @param {string|null} [id] - The style effect id.
|
|
3820
|
+
* @param {boolean} [cancelOldAnim=TinyHtml.cancelOldStyleFx] - Whether to cancel previous animations on the same element.
|
|
3821
|
+
* @returns {Animation[]}
|
|
3822
|
+
*/
|
|
3823
|
+
animate(keyframes, ops, id, cancelOldAnim) {
|
|
3824
|
+
return TinyHtml.animate(this, keyframes, ops, id, cancelOldAnim);
|
|
3825
|
+
}
|
|
3826
|
+
/**
|
|
3827
|
+
* Stops the current animation(s) on one or multiple TinyElement instances.
|
|
3828
|
+
*
|
|
3829
|
+
* If an animation is running on the element(s), it will be cancelled immediately.
|
|
3830
|
+
*
|
|
3831
|
+
* @param {TinyHtmlElement|TinyHtmlElement[]} el - A single TinyElement or an array of TinyElements.
|
|
3832
|
+
* @returns {boolean[]} The same element(s) provided as input, for chaining.
|
|
3833
|
+
*/
|
|
3834
|
+
static stop(el) {
|
|
3835
|
+
/** @type {boolean[]} */
|
|
3836
|
+
const results = [];
|
|
3837
|
+
TinyHtml._preHtmlElems(el, 'stop').forEach((elem) => {
|
|
3838
|
+
const anim = __elementCurrentAnimation.get(elem);
|
|
3839
|
+
if (anim) {
|
|
3840
|
+
anim.animation.cancel();
|
|
3841
|
+
__elementCurrentAnimation.delete(elem);
|
|
3842
|
+
results.push(true);
|
|
3843
|
+
}
|
|
3844
|
+
else
|
|
3845
|
+
results.push(false);
|
|
3846
|
+
});
|
|
3847
|
+
return results;
|
|
3848
|
+
}
|
|
3849
|
+
/**
|
|
3850
|
+
* Stops the current animation(s) on this TinyElement instance.
|
|
3851
|
+
*
|
|
3852
|
+
* @returns {boolean[]}
|
|
3853
|
+
*/
|
|
3854
|
+
stop() {
|
|
3855
|
+
return TinyHtml.stop(this);
|
|
3856
|
+
}
|
|
3857
|
+
// TITLE: Animate FXs
|
|
3858
|
+
/**
|
|
3859
|
+
* Show animation (slideDown).
|
|
3860
|
+
* @param {TinyHtmlElement|TinyHtmlElement[]} el
|
|
3861
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3862
|
+
* @returns {StyleFxResult}
|
|
3863
|
+
*/
|
|
3864
|
+
static slideDown(el, ops) {
|
|
3865
|
+
return TinyHtml.applyStyleFx(el, 'slideDown', this.#styleEffects['slideDown'], ops ?? { duration: 500, easing: 'ease-out' });
|
|
3866
|
+
}
|
|
3867
|
+
/**
|
|
3868
|
+
* Show animation (slideDown).
|
|
3869
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3870
|
+
* @returns {StyleFxResult}
|
|
3871
|
+
*/
|
|
3872
|
+
slideDown(ops) {
|
|
3873
|
+
return TinyHtml.slideDown(this, ops);
|
|
3874
|
+
}
|
|
3875
|
+
/**
|
|
3876
|
+
* Hide animation (slideUp).
|
|
3877
|
+
* @param {TinyHtmlElement|TinyHtmlElement[]} el
|
|
3878
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3879
|
+
* @returns {StyleFxResult}
|
|
3880
|
+
*/
|
|
3881
|
+
static slideUp(el, ops) {
|
|
3882
|
+
return TinyHtml.applyStyleFx(el, 'slideUp', this.#styleEffects['slideUp'], ops ?? { duration: 500, easing: 'ease-out' });
|
|
3883
|
+
}
|
|
3884
|
+
/**
|
|
3885
|
+
* Hide animation (slideUp).
|
|
3886
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3887
|
+
* @returns {StyleFxResult}
|
|
3888
|
+
*/
|
|
3889
|
+
slideUp(ops) {
|
|
3890
|
+
return TinyHtml.slideUp(this, ops);
|
|
3891
|
+
}
|
|
3892
|
+
/**
|
|
3893
|
+
* Toggle slide animation.
|
|
3894
|
+
* @param {TinyHtmlElement|TinyHtmlElement[]} el
|
|
3895
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3896
|
+
* @returns {StyleFxResult}
|
|
3897
|
+
*/
|
|
3898
|
+
static slideToggle(el, ops) {
|
|
3899
|
+
const first = TinyHtml._preHtmlElem(el, 'slideToggle');
|
|
3900
|
+
const style = getComputedStyle(first);
|
|
3901
|
+
const id = TinyHtml.getCurrentAnimationId(first);
|
|
3902
|
+
const isHidden = style.display === 'none' || first.offsetHeight === 0;
|
|
3903
|
+
return (typeof id === 'string' ? id === 'slideUp' : isHidden)
|
|
3904
|
+
? TinyHtml.slideDown(el, ops)
|
|
3905
|
+
: TinyHtml.slideUp(el, ops);
|
|
3906
|
+
}
|
|
3907
|
+
/**
|
|
3908
|
+
* Toggle slide animation.
|
|
3909
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3910
|
+
* @returns {StyleFxResult}
|
|
3911
|
+
*/
|
|
3912
|
+
slideToggle(ops) {
|
|
3913
|
+
return TinyHtml.slideToggle(this, ops);
|
|
3914
|
+
}
|
|
3915
|
+
/**
|
|
3916
|
+
* Fade in animation.
|
|
3917
|
+
* @param {TinyHtmlElement|TinyHtmlElement[]} el
|
|
3918
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3919
|
+
* @returns {StyleFxResult}
|
|
3920
|
+
*/
|
|
3921
|
+
static fadeIn(el, ops) {
|
|
3922
|
+
return TinyHtml.applyStyleFx(el, 'fadeIn', this.#styleEffects['fadeIn'], ops);
|
|
3923
|
+
}
|
|
3924
|
+
/**
|
|
3925
|
+
* Fade in animation.
|
|
3926
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3927
|
+
* @returns {StyleFxResult}
|
|
3928
|
+
*/
|
|
3929
|
+
fadeIn(ops) {
|
|
3930
|
+
return TinyHtml.fadeIn(this, ops);
|
|
3931
|
+
}
|
|
3932
|
+
/**
|
|
3933
|
+
* Fade out animation.
|
|
3934
|
+
* @param {TinyHtmlElement|TinyHtmlElement[]} el
|
|
3935
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3936
|
+
* @returns {StyleFxResult}
|
|
3937
|
+
*/
|
|
3938
|
+
static fadeOut(el, ops) {
|
|
3939
|
+
return TinyHtml.applyStyleFx(el, 'fadeOut', this.#styleEffects['fadeOut'], ops);
|
|
3940
|
+
}
|
|
3941
|
+
/**
|
|
3942
|
+
* Fade out animation.
|
|
3943
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3944
|
+
* @returns {StyleFxResult}
|
|
3945
|
+
*/
|
|
3946
|
+
fadeOut(ops) {
|
|
3947
|
+
return TinyHtml.fadeOut(this, ops);
|
|
3948
|
+
}
|
|
3949
|
+
/**
|
|
3950
|
+
* Fade toggle animation.
|
|
3951
|
+
* @param {TinyHtmlElement|TinyHtmlElement[]} el
|
|
3952
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3953
|
+
* @returns {StyleFxResult}
|
|
2911
3954
|
*/
|
|
2912
|
-
|
|
2913
|
-
|
|
3955
|
+
static fadeToggle(el, ops) {
|
|
3956
|
+
const first = TinyHtml._preHtmlElem(el, 'fadeToggle');
|
|
3957
|
+
const style = getComputedStyle(first);
|
|
3958
|
+
const id = TinyHtml.getCurrentAnimationId(first);
|
|
3959
|
+
const isHidden = style.opacity === '0' || first.offsetParent === null;
|
|
3960
|
+
return (typeof id === 'string' ? id === 'fadeOut' : isHidden)
|
|
3961
|
+
? TinyHtml.fadeIn(el, ops)
|
|
3962
|
+
: TinyHtml.fadeOut(el, ops);
|
|
2914
3963
|
}
|
|
3964
|
+
/**
|
|
3965
|
+
* Fade toggle animation.
|
|
3966
|
+
* @param {number | KeyframeAnimationOptions | string} [ops]
|
|
3967
|
+
* @returns {StyleFxResult}
|
|
3968
|
+
*/
|
|
3969
|
+
fadeToggle(ops) {
|
|
3970
|
+
return TinyHtml.fadeToggle(this, ops);
|
|
3971
|
+
}
|
|
3972
|
+
///////////////////////////////////////////////////////////////
|
|
3973
|
+
// TITLE: DOM Positions
|
|
2915
3974
|
/**
|
|
2916
3975
|
* Gets the offset of the element relative to the document.
|
|
2917
3976
|
* @param {TinyElement} el - Target element.
|
|
@@ -3003,6 +4062,8 @@ class TinyHtml {
|
|
|
3003
4062
|
offsetParent() {
|
|
3004
4063
|
return TinyHtml.offsetParent(this);
|
|
3005
4064
|
}
|
|
4065
|
+
/////////////////////////////////////////////////
|
|
4066
|
+
// TITLE: Scroll Stuff
|
|
3006
4067
|
/**
|
|
3007
4068
|
* Gets the vertical scroll position.
|
|
3008
4069
|
* @param {TinyElementAndWindow} el - Element or window.
|
|
@@ -3212,6 +4273,8 @@ class TinyHtml {
|
|
|
3212
4273
|
setScrollLeft(value) {
|
|
3213
4274
|
return TinyHtml.setScrollLeft(this, value);
|
|
3214
4275
|
}
|
|
4276
|
+
///////////////////////////////////////////
|
|
4277
|
+
// TITLE: Border Stuff
|
|
3215
4278
|
/**
|
|
3216
4279
|
* Returns the total border width and individual sides from `border{Side}Width` CSS properties.
|
|
3217
4280
|
*
|
|
@@ -3302,6 +4365,7 @@ class TinyHtml {
|
|
|
3302
4365
|
return TinyHtml.padding(this);
|
|
3303
4366
|
}
|
|
3304
4367
|
/////////////////////////////////////////////
|
|
4368
|
+
// TITLE: Class Stuff
|
|
3305
4369
|
/**
|
|
3306
4370
|
* Adds one or more CSS class names to the element.
|
|
3307
4371
|
* @template {TinyElement|TinyElement[]} T
|
|
@@ -3473,6 +4537,7 @@ class TinyHtml {
|
|
|
3473
4537
|
return TinyHtml.classList(this);
|
|
3474
4538
|
}
|
|
3475
4539
|
/////////////////////////////////////////
|
|
4540
|
+
// TITLE: Tag Stuff
|
|
3476
4541
|
/**
|
|
3477
4542
|
* Returns the tag name of the element.
|
|
3478
4543
|
* @param {TinyElement} el - Target element.
|
|
@@ -3505,6 +4570,8 @@ class TinyHtml {
|
|
|
3505
4570
|
id() {
|
|
3506
4571
|
return TinyHtml.id(this);
|
|
3507
4572
|
}
|
|
4573
|
+
//////////////////////////////////////////////////////////////
|
|
4574
|
+
// TITLE: DOM Content Stuff
|
|
3508
4575
|
/**
|
|
3509
4576
|
* Returns the BigInt content of the element.
|
|
3510
4577
|
* @param {TinyElement} el - Target element.
|
|
@@ -3793,6 +4860,8 @@ class TinyHtml {
|
|
|
3793
4860
|
setText(value) {
|
|
3794
4861
|
return TinyHtml.setText(this, value);
|
|
3795
4862
|
}
|
|
4863
|
+
//////////////////////////////////////////////////////
|
|
4864
|
+
// TITLE: DOM Content Stuff (Part 2)
|
|
3796
4865
|
/**
|
|
3797
4866
|
* Remove all child nodes from each element.
|
|
3798
4867
|
* @template {TinyElement|TinyElement[]} T
|
|
@@ -3849,6 +4918,8 @@ class TinyHtml {
|
|
|
3849
4918
|
setHtml(value) {
|
|
3850
4919
|
return TinyHtml.setHtml(this, value);
|
|
3851
4920
|
}
|
|
4921
|
+
///////////////////////////////////////////////
|
|
4922
|
+
// TITLE: Value Stuff
|
|
3852
4923
|
/** @readonly */
|
|
3853
4924
|
static _valHooks = {
|
|
3854
4925
|
option: {
|
|
@@ -4262,6 +5333,7 @@ class TinyHtml {
|
|
|
4262
5333
|
return TinyHtml.valBool(this);
|
|
4263
5334
|
}
|
|
4264
5335
|
////////////////////////////////////////////
|
|
5336
|
+
// TITLE: Listen for Paste
|
|
4265
5337
|
/**
|
|
4266
5338
|
* Registers a listener for the "paste" event to extract files and text from the clipboard (e.g., when the user presses Ctrl+V).
|
|
4267
5339
|
*
|
|
@@ -4315,6 +5387,8 @@ class TinyHtml {
|
|
|
4315
5387
|
listenForPaste({ onFilePaste, onTextPaste } = {}) {
|
|
4316
5388
|
return TinyHtml.listenForPaste(this, { onFilePaste, onTextPaste });
|
|
4317
5389
|
}
|
|
5390
|
+
/////////////////////////////////////////////////////
|
|
5391
|
+
// TITLE: Events Stuff
|
|
4318
5392
|
/**
|
|
4319
5393
|
* Checks if the element has a listener for a specific event.
|
|
4320
5394
|
*
|
|
@@ -4367,6 +5441,40 @@ class TinyHtml {
|
|
|
4367
5441
|
hasExactEventListener(event, handler) {
|
|
4368
5442
|
return TinyHtml.hasExactEventListener(this, event, handler);
|
|
4369
5443
|
}
|
|
5444
|
+
/**
|
|
5445
|
+
* Hover event shortcut.
|
|
5446
|
+
*
|
|
5447
|
+
* Adds `mouseenter` and `mouseleave` event listeners to the target.
|
|
5448
|
+
*
|
|
5449
|
+
* @template {TinyEventTarget|TinyEventTarget[]} T
|
|
5450
|
+
* @param {T} el - The target element(s) to attach the hover handlers.
|
|
5451
|
+
* @param {HoverEventCallback} fnOver - Callback executed when the mouse enters the target.
|
|
5452
|
+
* @param {HoverEventCallback|null} [fnOut] - Optional callback executed when the mouse leaves
|
|
5453
|
+
* the target. If not provided, `fnOver` will be used for both events.
|
|
5454
|
+
* @param {EventRegistryOptions} [options] - Optional event listener options.
|
|
5455
|
+
* @returns {T}
|
|
5456
|
+
*/
|
|
5457
|
+
static hover(el, fnOver, fnOut, options) {
|
|
5458
|
+
// @ts-ignore
|
|
5459
|
+
TinyHtml.on(el, 'mouseenter', fnOver, options);
|
|
5460
|
+
// @ts-ignore
|
|
5461
|
+
TinyHtml.on(el, 'mouseleave', fnOut || fnOver, options);
|
|
5462
|
+
return el;
|
|
5463
|
+
}
|
|
5464
|
+
/**
|
|
5465
|
+
* Hover event shortcut.
|
|
5466
|
+
*
|
|
5467
|
+
* Adds `mouseenter` and `mouseleave` event listeners to the target.
|
|
5468
|
+
*
|
|
5469
|
+
* @param {HoverEventCallback} fnOver - Callback executed when the mouse enters the target.
|
|
5470
|
+
* @param {HoverEventCallback|null} [fnOut] - Optional callback executed when the mouse leaves
|
|
5471
|
+
* the target. If not provided, `fnOver` will be used for both events.
|
|
5472
|
+
* @param {EventRegistryOptions} [options] - Optional event listener options.
|
|
5473
|
+
* @returns {this}
|
|
5474
|
+
*/
|
|
5475
|
+
hover(fnOver, fnOut, options) {
|
|
5476
|
+
return TinyHtml.hover(this, fnOver, fnOut, options);
|
|
5477
|
+
}
|
|
4370
5478
|
/**
|
|
4371
5479
|
* Registers an event listener on the specified element.
|
|
4372
5480
|
*
|
|
@@ -4602,6 +5710,7 @@ class TinyHtml {
|
|
|
4602
5710
|
return TinyHtml.trigger(this, events, payload);
|
|
4603
5711
|
}
|
|
4604
5712
|
///////////////////////////////////////////////////////////////
|
|
5713
|
+
// TITLE: ProFix Manager
|
|
4605
5714
|
/**
|
|
4606
5715
|
* Internal property name normalization map (similar to jQuery's `propFix`).
|
|
4607
5716
|
* Maps attribute-like names to their JavaScript DOM property equivalents.
|
|
@@ -4885,6 +5994,7 @@ class TinyHtml {
|
|
|
4885
5994
|
return TinyHtml.toggleProp(this, name, force);
|
|
4886
5995
|
}
|
|
4887
5996
|
/////////////////////////////////////////////////////
|
|
5997
|
+
// TITLE: Remove Element
|
|
4888
5998
|
/**
|
|
4889
5999
|
* Removes an element from the DOM.
|
|
4890
6000
|
* @template {TinyElement|TinyElement[]} T
|
|
@@ -4902,6 +6012,8 @@ class TinyHtml {
|
|
|
4902
6012
|
remove() {
|
|
4903
6013
|
return TinyHtml.remove(this);
|
|
4904
6014
|
}
|
|
6015
|
+
///////////////////////////////////////////////////
|
|
6016
|
+
// TITLE: Index Manager
|
|
4905
6017
|
/**
|
|
4906
6018
|
* Returns the index of the first element within its parent or relative to a selector/element.
|
|
4907
6019
|
*
|
|
@@ -4932,6 +6044,7 @@ class TinyHtml {
|
|
|
4932
6044
|
return TinyHtml.index(this, elem);
|
|
4933
6045
|
}
|
|
4934
6046
|
////////////////////////////////////////////////////////////////////
|
|
6047
|
+
// TITLE: Collision Stuff
|
|
4935
6048
|
/**
|
|
4936
6049
|
* Creates a new DOMRect object by copying the base rect and applying optional additional dimensions.
|
|
4937
6050
|
*
|
|
@@ -5200,6 +6313,7 @@ class TinyHtml {
|
|
|
5200
6313
|
return TinyHtml.resetCollLockDir(this, direction);
|
|
5201
6314
|
}
|
|
5202
6315
|
//////////////////////////////////////////////////////////////////////////////
|
|
6316
|
+
// TITLE: Viewport Stuff
|
|
5203
6317
|
/**
|
|
5204
6318
|
* Checks if the given element is at least partially visible in the viewport.
|
|
5205
6319
|
*
|
|
@@ -5320,6 +6434,7 @@ class TinyHtml {
|
|
|
5320
6434
|
isFullyInContainer(cont) {
|
|
5321
6435
|
return TinyHtml.isFullyInContainer(this, cont);
|
|
5322
6436
|
}
|
|
6437
|
+
// TITLE: Has Scroll
|
|
5323
6438
|
/**
|
|
5324
6439
|
* Checks if an element has scrollable content.
|
|
5325
6440
|
*
|