tiny-essentials 1.21.9 → 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 +1489 -173
- package/dist/v1/libs/TinyHtml.d.mts +543 -42
- package/dist/v1/libs/TinyHtml.mjs +1200 -61
- package/dist/v1/libs/TinyInventory.d.mts +1 -1
- package/dist/v1/libs/UltraRandomMsgGen.d.mts +7 -7
- package/docs/v1/README.md +9 -0
- package/docs/v1/Regex-Helpers.md +72 -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 +803 -8
- package/package.json +2 -2
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Callback type used for element mutation detectors.
|
|
5
|
+
*
|
|
6
|
+
* @callback ElementDetectorsFn
|
|
7
|
+
* @param {MutationRecord} mutation - Single mutation record being processed.
|
|
8
|
+
* @param {number} index - Index of the current mutation in the batch.
|
|
9
|
+
* @param {MutationRecord[]} mutations - Full list of mutation records from the observer callback.
|
|
10
|
+
* @returns {void}
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* TinyElementObserver
|
|
15
|
+
*
|
|
16
|
+
* A utility class for tracking DOM element mutations.
|
|
17
|
+
* It leverages the native MutationObserver API, providing a higher-level abstraction
|
|
18
|
+
* with a system of configurable detectors that can dispatch custom events or run custom logic.
|
|
19
|
+
*/
|
|
20
|
+
class TinyElementObserver {
|
|
21
|
+
/** @type {Element|undefined} */
|
|
22
|
+
#el;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get the current element being observed.
|
|
26
|
+
* @returns {Element|undefined} The DOM element being tracked, or `undefined` if none is set.
|
|
27
|
+
*/
|
|
28
|
+
get el() {
|
|
29
|
+
return this.#el;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Set the target element to be observed.
|
|
34
|
+
* Can only be set once.
|
|
35
|
+
*
|
|
36
|
+
* @param {Element|undefined} el - The DOM element to observe.
|
|
37
|
+
* @throws {Error} If the element is already defined.
|
|
38
|
+
* @throws {TypeError} If the provided value is not an Element.
|
|
39
|
+
*/
|
|
40
|
+
set el(el) {
|
|
41
|
+
if (this.#el)
|
|
42
|
+
throw new Error('The observed element has already been set and cannot be reassigned.');
|
|
43
|
+
if (typeof el !== 'undefined' && !(el instanceof Element))
|
|
44
|
+
throw new TypeError('The observed element must be a valid DOM Element.');
|
|
45
|
+
this.#el = el;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Configuration settings for the MutationObserver instance.
|
|
50
|
+
*
|
|
51
|
+
* @type {MutationObserverInit}
|
|
52
|
+
*/
|
|
53
|
+
#settings = {};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get the observer settings.
|
|
57
|
+
* @returns {MutationObserverInit}
|
|
58
|
+
*/
|
|
59
|
+
get settings() {
|
|
60
|
+
return this.#settings;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Set the observer settings.
|
|
65
|
+
* @param {MutationObserverInit} settings
|
|
66
|
+
*/
|
|
67
|
+
set settings(settings) {
|
|
68
|
+
if (typeof settings !== 'object' || settings === null)
|
|
69
|
+
throw new TypeError('settings must be a non-null object.');
|
|
70
|
+
this.#settings = settings;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Internal MutationObserver instance that tracks DOM attribute changes.
|
|
75
|
+
* @type {MutationObserver|null}
|
|
76
|
+
*/
|
|
77
|
+
#observer = null;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get the current MutationObserver instance.
|
|
81
|
+
* @returns {MutationObserver|null}
|
|
82
|
+
*/
|
|
83
|
+
get observer() {
|
|
84
|
+
return this.#observer;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* List of detectors executed on observed mutations.
|
|
89
|
+
* Each detector is a tuple:
|
|
90
|
+
* - name: string identifier
|
|
91
|
+
* - handler: function processing MutationRecords
|
|
92
|
+
*
|
|
93
|
+
* @type {Array<[string, ElementDetectorsFn]>}
|
|
94
|
+
*/
|
|
95
|
+
#detectors = [];
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get the element detectors.
|
|
99
|
+
* @returns {Array<[string, ElementDetectorsFn]>}
|
|
100
|
+
*/
|
|
101
|
+
get detectors() {
|
|
102
|
+
return this.#detectors.map((item) => [item[0], item[1]]);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Set the element detectors.
|
|
107
|
+
* @param {Array<[string, ElementDetectorsFn]>} detectors
|
|
108
|
+
*/
|
|
109
|
+
set detectors(detectors) {
|
|
110
|
+
if (!Array.isArray(detectors)) throw new TypeError('detectors must be an array.');
|
|
111
|
+
|
|
112
|
+
/** @type {Array<[string, ElementDetectorsFn]>} */
|
|
113
|
+
const values = [];
|
|
114
|
+
for (const [name, fn] of detectors) {
|
|
115
|
+
if (typeof name !== 'string') throw new TypeError('Detector name must be a string.');
|
|
116
|
+
if (typeof fn !== 'function')
|
|
117
|
+
throw new TypeError(`Detector handler for "${name}" must be a function.`);
|
|
118
|
+
values.push([name, fn]);
|
|
119
|
+
}
|
|
120
|
+
this.#detectors = values;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Returns true if a MutationObserver is currently active.
|
|
125
|
+
* @returns {boolean}
|
|
126
|
+
*/
|
|
127
|
+
get isActive() {
|
|
128
|
+
return !!this.#observer;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get the number of registered detectors.
|
|
133
|
+
*
|
|
134
|
+
* @returns {number} Total count of detectors.
|
|
135
|
+
*/
|
|
136
|
+
get size() {
|
|
137
|
+
return this.#detectors.length;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Create a new TinyElementObserver instance.
|
|
142
|
+
*
|
|
143
|
+
* @param {Object} [settings={}] - Configuration object.
|
|
144
|
+
* @param {Element} [settings.el] - Optional DOM element to observe from the start.
|
|
145
|
+
* @param {Array<[string, ElementDetectorsFn]>} [settings.initDetectors=[]] - Optional initial detectors to register.
|
|
146
|
+
* @param {MutationObserverInit} [settings.initCfg] - Optional MutationObserver configuration.
|
|
147
|
+
*/
|
|
148
|
+
constructor({ el, initDetectors = [], initCfg = {} } = {}) {
|
|
149
|
+
this.el = el;
|
|
150
|
+
if (initDetectors.length) this.detectors = initDetectors;
|
|
151
|
+
if (initCfg) this.settings = initCfg;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Remove all registered detectors.
|
|
156
|
+
* After calling this, no mutation events will be processed
|
|
157
|
+
* until new detectors are added again.
|
|
158
|
+
*/
|
|
159
|
+
clear() {
|
|
160
|
+
this.#detectors = [];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Start tracking DOM mutations on the defined element.
|
|
165
|
+
*
|
|
166
|
+
* @throws {Error} If no element has been set to observe.
|
|
167
|
+
*/
|
|
168
|
+
start() {
|
|
169
|
+
if (!this.#el) throw new Error('Cannot start observation: no target element has been set.');
|
|
170
|
+
if (this.#observer) return;
|
|
171
|
+
this.#observer = new MutationObserver((mutations) => {
|
|
172
|
+
mutations.forEach((value, index, array) =>
|
|
173
|
+
this.#detectors.forEach((item) => item[1](value, index, array)),
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
this.#observer.observe(this.#el, this.#settings);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Stop tracking changes.
|
|
182
|
+
*/
|
|
183
|
+
stop() {
|
|
184
|
+
if (!this.#observer) return;
|
|
185
|
+
this.#observer.disconnect();
|
|
186
|
+
this.#observer = null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ================= Detectors Editor =================
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Add a detector to the end of the array.
|
|
193
|
+
* @param {string} name
|
|
194
|
+
* @param {ElementDetectorsFn} handler
|
|
195
|
+
*/
|
|
196
|
+
add(name, handler) {
|
|
197
|
+
this.#validateDetector(name, handler);
|
|
198
|
+
this.#detectors.push([name, handler]);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Add a detector to the start of the array.
|
|
203
|
+
* @param {string} name
|
|
204
|
+
* @param {ElementDetectorsFn} handler
|
|
205
|
+
*/
|
|
206
|
+
insertAtStart(name, handler) {
|
|
207
|
+
this.#validateDetector(name, handler);
|
|
208
|
+
this.#detectors.unshift([name, handler]);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Insert a detector at a specific index.
|
|
213
|
+
* @param {number} index
|
|
214
|
+
* @param {string} name
|
|
215
|
+
* @param {ElementDetectorsFn} handler
|
|
216
|
+
* @param {'before'|'after'} position - Position relative to the index
|
|
217
|
+
*/
|
|
218
|
+
insertAt(index, name, handler, position = 'after') {
|
|
219
|
+
this.#validateDetector(name, handler);
|
|
220
|
+
if (typeof index !== 'number' || index < 0 || index >= this.#detectors.length)
|
|
221
|
+
throw new RangeError('Invalid index for insertDetectorAt.');
|
|
222
|
+
const insertIndex = position === 'before' ? index : index + 1;
|
|
223
|
+
this.#detectors.splice(insertIndex, 0, [name, handler]);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Remove a detector at a specific index.
|
|
228
|
+
* @param {number} index
|
|
229
|
+
*/
|
|
230
|
+
removeAt(index) {
|
|
231
|
+
if (typeof index !== 'number' || index < 0 || index >= this.#detectors.length)
|
|
232
|
+
throw new RangeError('Invalid index for removeDetectorAt.');
|
|
233
|
+
this.#detectors.splice(index, 1);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Remove detectors relative to a specific index.
|
|
238
|
+
* @param {number} index - Reference index
|
|
239
|
+
* @param {number} before - Number of items before the index to remove
|
|
240
|
+
* @param {number} after - Number of items after the index to remove
|
|
241
|
+
*/
|
|
242
|
+
removeAround(index, before = 0, after = 0) {
|
|
243
|
+
if (typeof index !== 'number' || index < 0 || index >= this.#detectors.length)
|
|
244
|
+
throw new RangeError('Invalid index for removeDetectorsAround.');
|
|
245
|
+
const start = Math.max(0, index - before);
|
|
246
|
+
const deleteCount = before + 1 + after;
|
|
247
|
+
this.#detectors.splice(start, deleteCount);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Check if a detector exists at a specific index.
|
|
252
|
+
* @param {number} index
|
|
253
|
+
* @returns {boolean}
|
|
254
|
+
*/
|
|
255
|
+
isIndexUsed(index) {
|
|
256
|
+
return index >= 0 && index < this.#detectors.length;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Check if a handler function already exists in the array.
|
|
261
|
+
* @param {ElementDetectorsFn} handler
|
|
262
|
+
* @returns {boolean}
|
|
263
|
+
*/
|
|
264
|
+
hasHandler(handler) {
|
|
265
|
+
if (typeof handler !== 'function') throw new TypeError('Handler must be a function.');
|
|
266
|
+
return this.#detectors.some(([_, fn]) => fn === handler);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Internal validation for detector entries.
|
|
271
|
+
* @param {string} name
|
|
272
|
+
* @param {ElementDetectorsFn} handler
|
|
273
|
+
*/
|
|
274
|
+
#validateDetector(name, handler) {
|
|
275
|
+
if (typeof name !== 'string' || !name.trim())
|
|
276
|
+
throw new TypeError('Detector name must be a non-empty string.');
|
|
277
|
+
if (typeof handler !== 'function')
|
|
278
|
+
throw new TypeError(`Detector handler for "${name}" must be a function.`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Completely destroy this observer instance.
|
|
283
|
+
* Stops the MutationObserver (if active) and clears all detectors,
|
|
284
|
+
* leaving the instance unusable until reconfigured.
|
|
285
|
+
*/
|
|
286
|
+
destroy() {
|
|
287
|
+
this.stop();
|
|
288
|
+
this.clear();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
module.exports = TinyElementObserver;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
export default TinyElementObserver;
|
|
2
|
+
/**
|
|
3
|
+
* Callback type used for element mutation detectors.
|
|
4
|
+
*/
|
|
5
|
+
export type ElementDetectorsFn = (mutation: MutationRecord, index: number, mutations: MutationRecord[]) => void;
|
|
6
|
+
/**
|
|
7
|
+
* Callback type used for element mutation detectors.
|
|
8
|
+
*
|
|
9
|
+
* @callback ElementDetectorsFn
|
|
10
|
+
* @param {MutationRecord} mutation - Single mutation record being processed.
|
|
11
|
+
* @param {number} index - Index of the current mutation in the batch.
|
|
12
|
+
* @param {MutationRecord[]} mutations - Full list of mutation records from the observer callback.
|
|
13
|
+
* @returns {void}
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* TinyElementObserver
|
|
17
|
+
*
|
|
18
|
+
* A utility class for tracking DOM element mutations.
|
|
19
|
+
* It leverages the native MutationObserver API, providing a higher-level abstraction
|
|
20
|
+
* with a system of configurable detectors that can dispatch custom events or run custom logic.
|
|
21
|
+
*/
|
|
22
|
+
declare class TinyElementObserver {
|
|
23
|
+
/**
|
|
24
|
+
* Create a new TinyElementObserver instance.
|
|
25
|
+
*
|
|
26
|
+
* @param {Object} [settings={}] - Configuration object.
|
|
27
|
+
* @param {Element} [settings.el] - Optional DOM element to observe from the start.
|
|
28
|
+
* @param {Array<[string, ElementDetectorsFn]>} [settings.initDetectors=[]] - Optional initial detectors to register.
|
|
29
|
+
* @param {MutationObserverInit} [settings.initCfg] - Optional MutationObserver configuration.
|
|
30
|
+
*/
|
|
31
|
+
constructor({ el, initDetectors, initCfg }?: {
|
|
32
|
+
el?: Element | undefined;
|
|
33
|
+
initDetectors?: [string, ElementDetectorsFn][] | undefined;
|
|
34
|
+
initCfg?: MutationObserverInit | undefined;
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Set the target element to be observed.
|
|
38
|
+
* Can only be set once.
|
|
39
|
+
*
|
|
40
|
+
* @param {Element|undefined} el - The DOM element to observe.
|
|
41
|
+
* @throws {Error} If the element is already defined.
|
|
42
|
+
* @throws {TypeError} If the provided value is not an Element.
|
|
43
|
+
*/
|
|
44
|
+
set el(el: Element | undefined);
|
|
45
|
+
/**
|
|
46
|
+
* Get the current element being observed.
|
|
47
|
+
* @returns {Element|undefined} The DOM element being tracked, or `undefined` if none is set.
|
|
48
|
+
*/
|
|
49
|
+
get el(): Element | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Set the observer settings.
|
|
52
|
+
* @param {MutationObserverInit} settings
|
|
53
|
+
*/
|
|
54
|
+
set settings(settings: MutationObserverInit);
|
|
55
|
+
/**
|
|
56
|
+
* Get the observer settings.
|
|
57
|
+
* @returns {MutationObserverInit}
|
|
58
|
+
*/
|
|
59
|
+
get settings(): MutationObserverInit;
|
|
60
|
+
/**
|
|
61
|
+
* Get the current MutationObserver instance.
|
|
62
|
+
* @returns {MutationObserver|null}
|
|
63
|
+
*/
|
|
64
|
+
get observer(): MutationObserver | null;
|
|
65
|
+
/**
|
|
66
|
+
* Set the element detectors.
|
|
67
|
+
* @param {Array<[string, ElementDetectorsFn]>} detectors
|
|
68
|
+
*/
|
|
69
|
+
set detectors(detectors: Array<[string, ElementDetectorsFn]>);
|
|
70
|
+
/**
|
|
71
|
+
* Get the element detectors.
|
|
72
|
+
* @returns {Array<[string, ElementDetectorsFn]>}
|
|
73
|
+
*/
|
|
74
|
+
get detectors(): Array<[string, ElementDetectorsFn]>;
|
|
75
|
+
/**
|
|
76
|
+
* Returns true if a MutationObserver is currently active.
|
|
77
|
+
* @returns {boolean}
|
|
78
|
+
*/
|
|
79
|
+
get isActive(): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Get the number of registered detectors.
|
|
82
|
+
*
|
|
83
|
+
* @returns {number} Total count of detectors.
|
|
84
|
+
*/
|
|
85
|
+
get size(): number;
|
|
86
|
+
/**
|
|
87
|
+
* Remove all registered detectors.
|
|
88
|
+
* After calling this, no mutation events will be processed
|
|
89
|
+
* until new detectors are added again.
|
|
90
|
+
*/
|
|
91
|
+
clear(): void;
|
|
92
|
+
/**
|
|
93
|
+
* Start tracking DOM mutations on the defined element.
|
|
94
|
+
*
|
|
95
|
+
* @throws {Error} If no element has been set to observe.
|
|
96
|
+
*/
|
|
97
|
+
start(): void;
|
|
98
|
+
/**
|
|
99
|
+
* Stop tracking changes.
|
|
100
|
+
*/
|
|
101
|
+
stop(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Add a detector to the end of the array.
|
|
104
|
+
* @param {string} name
|
|
105
|
+
* @param {ElementDetectorsFn} handler
|
|
106
|
+
*/
|
|
107
|
+
add(name: string, handler: ElementDetectorsFn): void;
|
|
108
|
+
/**
|
|
109
|
+
* Add a detector to the start of the array.
|
|
110
|
+
* @param {string} name
|
|
111
|
+
* @param {ElementDetectorsFn} handler
|
|
112
|
+
*/
|
|
113
|
+
insertAtStart(name: string, handler: ElementDetectorsFn): void;
|
|
114
|
+
/**
|
|
115
|
+
* Insert a detector at a specific index.
|
|
116
|
+
* @param {number} index
|
|
117
|
+
* @param {string} name
|
|
118
|
+
* @param {ElementDetectorsFn} handler
|
|
119
|
+
* @param {'before'|'after'} position - Position relative to the index
|
|
120
|
+
*/
|
|
121
|
+
insertAt(index: number, name: string, handler: ElementDetectorsFn, position?: "before" | "after"): void;
|
|
122
|
+
/**
|
|
123
|
+
* Remove a detector at a specific index.
|
|
124
|
+
* @param {number} index
|
|
125
|
+
*/
|
|
126
|
+
removeAt(index: number): void;
|
|
127
|
+
/**
|
|
128
|
+
* Remove detectors relative to a specific index.
|
|
129
|
+
* @param {number} index - Reference index
|
|
130
|
+
* @param {number} before - Number of items before the index to remove
|
|
131
|
+
* @param {number} after - Number of items after the index to remove
|
|
132
|
+
*/
|
|
133
|
+
removeAround(index: number, before?: number, after?: number): void;
|
|
134
|
+
/**
|
|
135
|
+
* Check if a detector exists at a specific index.
|
|
136
|
+
* @param {number} index
|
|
137
|
+
* @returns {boolean}
|
|
138
|
+
*/
|
|
139
|
+
isIndexUsed(index: number): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Check if a handler function already exists in the array.
|
|
142
|
+
* @param {ElementDetectorsFn} handler
|
|
143
|
+
* @returns {boolean}
|
|
144
|
+
*/
|
|
145
|
+
hasHandler(handler: ElementDetectorsFn): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Completely destroy this observer instance.
|
|
148
|
+
* Stops the MutationObserver (if active) and clears all detectors,
|
|
149
|
+
* leaving the instance unusable until reconfigured.
|
|
150
|
+
*/
|
|
151
|
+
destroy(): void;
|
|
152
|
+
#private;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=TinyElementObserver.d.mts.map
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback type used for element mutation detectors.
|
|
3
|
+
*
|
|
4
|
+
* @callback ElementDetectorsFn
|
|
5
|
+
* @param {MutationRecord} mutation - Single mutation record being processed.
|
|
6
|
+
* @param {number} index - Index of the current mutation in the batch.
|
|
7
|
+
* @param {MutationRecord[]} mutations - Full list of mutation records from the observer callback.
|
|
8
|
+
* @returns {void}
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* TinyElementObserver
|
|
12
|
+
*
|
|
13
|
+
* A utility class for tracking DOM element mutations.
|
|
14
|
+
* It leverages the native MutationObserver API, providing a higher-level abstraction
|
|
15
|
+
* with a system of configurable detectors that can dispatch custom events or run custom logic.
|
|
16
|
+
*/
|
|
17
|
+
class TinyElementObserver {
|
|
18
|
+
/** @type {Element|undefined} */
|
|
19
|
+
#el;
|
|
20
|
+
/**
|
|
21
|
+
* Get the current element being observed.
|
|
22
|
+
* @returns {Element|undefined} The DOM element being tracked, or `undefined` if none is set.
|
|
23
|
+
*/
|
|
24
|
+
get el() {
|
|
25
|
+
return this.#el;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Set the target element to be observed.
|
|
29
|
+
* Can only be set once.
|
|
30
|
+
*
|
|
31
|
+
* @param {Element|undefined} el - The DOM element to observe.
|
|
32
|
+
* @throws {Error} If the element is already defined.
|
|
33
|
+
* @throws {TypeError} If the provided value is not an Element.
|
|
34
|
+
*/
|
|
35
|
+
set el(el) {
|
|
36
|
+
if (this.#el)
|
|
37
|
+
throw new Error('The observed element has already been set and cannot be reassigned.');
|
|
38
|
+
if (typeof el !== 'undefined' && !(el instanceof Element))
|
|
39
|
+
throw new TypeError('The observed element must be a valid DOM Element.');
|
|
40
|
+
this.#el = el;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Configuration settings for the MutationObserver instance.
|
|
44
|
+
*
|
|
45
|
+
* @type {MutationObserverInit}
|
|
46
|
+
*/
|
|
47
|
+
#settings = {};
|
|
48
|
+
/**
|
|
49
|
+
* Get the observer settings.
|
|
50
|
+
* @returns {MutationObserverInit}
|
|
51
|
+
*/
|
|
52
|
+
get settings() {
|
|
53
|
+
return this.#settings;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Set the observer settings.
|
|
57
|
+
* @param {MutationObserverInit} settings
|
|
58
|
+
*/
|
|
59
|
+
set settings(settings) {
|
|
60
|
+
if (typeof settings !== 'object' || settings === null)
|
|
61
|
+
throw new TypeError('settings must be a non-null object.');
|
|
62
|
+
this.#settings = settings;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Internal MutationObserver instance that tracks DOM attribute changes.
|
|
66
|
+
* @type {MutationObserver|null}
|
|
67
|
+
*/
|
|
68
|
+
#observer = null;
|
|
69
|
+
/**
|
|
70
|
+
* Get the current MutationObserver instance.
|
|
71
|
+
* @returns {MutationObserver|null}
|
|
72
|
+
*/
|
|
73
|
+
get observer() {
|
|
74
|
+
return this.#observer;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* List of detectors executed on observed mutations.
|
|
78
|
+
* Each detector is a tuple:
|
|
79
|
+
* - name: string identifier
|
|
80
|
+
* - handler: function processing MutationRecords
|
|
81
|
+
*
|
|
82
|
+
* @type {Array<[string, ElementDetectorsFn]>}
|
|
83
|
+
*/
|
|
84
|
+
#detectors = [];
|
|
85
|
+
/**
|
|
86
|
+
* Get the element detectors.
|
|
87
|
+
* @returns {Array<[string, ElementDetectorsFn]>}
|
|
88
|
+
*/
|
|
89
|
+
get detectors() {
|
|
90
|
+
return this.#detectors.map((item) => [item[0], item[1]]);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Set the element detectors.
|
|
94
|
+
* @param {Array<[string, ElementDetectorsFn]>} detectors
|
|
95
|
+
*/
|
|
96
|
+
set detectors(detectors) {
|
|
97
|
+
if (!Array.isArray(detectors))
|
|
98
|
+
throw new TypeError('detectors must be an array.');
|
|
99
|
+
/** @type {Array<[string, ElementDetectorsFn]>} */
|
|
100
|
+
const values = [];
|
|
101
|
+
for (const [name, fn] of detectors) {
|
|
102
|
+
if (typeof name !== 'string')
|
|
103
|
+
throw new TypeError('Detector name must be a string.');
|
|
104
|
+
if (typeof fn !== 'function')
|
|
105
|
+
throw new TypeError(`Detector handler for "${name}" must be a function.`);
|
|
106
|
+
values.push([name, fn]);
|
|
107
|
+
}
|
|
108
|
+
this.#detectors = values;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Returns true if a MutationObserver is currently active.
|
|
112
|
+
* @returns {boolean}
|
|
113
|
+
*/
|
|
114
|
+
get isActive() {
|
|
115
|
+
return !!this.#observer;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get the number of registered detectors.
|
|
119
|
+
*
|
|
120
|
+
* @returns {number} Total count of detectors.
|
|
121
|
+
*/
|
|
122
|
+
get size() {
|
|
123
|
+
return this.#detectors.length;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Create a new TinyElementObserver instance.
|
|
127
|
+
*
|
|
128
|
+
* @param {Object} [settings={}] - Configuration object.
|
|
129
|
+
* @param {Element} [settings.el] - Optional DOM element to observe from the start.
|
|
130
|
+
* @param {Array<[string, ElementDetectorsFn]>} [settings.initDetectors=[]] - Optional initial detectors to register.
|
|
131
|
+
* @param {MutationObserverInit} [settings.initCfg] - Optional MutationObserver configuration.
|
|
132
|
+
*/
|
|
133
|
+
constructor({ el, initDetectors = [], initCfg = {} } = {}) {
|
|
134
|
+
this.el = el;
|
|
135
|
+
if (initDetectors.length)
|
|
136
|
+
this.detectors = initDetectors;
|
|
137
|
+
if (initCfg)
|
|
138
|
+
this.settings = initCfg;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Remove all registered detectors.
|
|
142
|
+
* After calling this, no mutation events will be processed
|
|
143
|
+
* until new detectors are added again.
|
|
144
|
+
*/
|
|
145
|
+
clear() {
|
|
146
|
+
this.#detectors = [];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Start tracking DOM mutations on the defined element.
|
|
150
|
+
*
|
|
151
|
+
* @throws {Error} If no element has been set to observe.
|
|
152
|
+
*/
|
|
153
|
+
start() {
|
|
154
|
+
if (!this.#el)
|
|
155
|
+
throw new Error('Cannot start observation: no target element has been set.');
|
|
156
|
+
if (this.#observer)
|
|
157
|
+
return;
|
|
158
|
+
this.#observer = new MutationObserver((mutations) => {
|
|
159
|
+
mutations.forEach((value, index, array) => this.#detectors.forEach((item) => item[1](value, index, array)));
|
|
160
|
+
});
|
|
161
|
+
this.#observer.observe(this.#el, this.#settings);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Stop tracking changes.
|
|
165
|
+
*/
|
|
166
|
+
stop() {
|
|
167
|
+
if (!this.#observer)
|
|
168
|
+
return;
|
|
169
|
+
this.#observer.disconnect();
|
|
170
|
+
this.#observer = null;
|
|
171
|
+
}
|
|
172
|
+
// ================= Detectors Editor =================
|
|
173
|
+
/**
|
|
174
|
+
* Add a detector to the end of the array.
|
|
175
|
+
* @param {string} name
|
|
176
|
+
* @param {ElementDetectorsFn} handler
|
|
177
|
+
*/
|
|
178
|
+
add(name, handler) {
|
|
179
|
+
this.#validateDetector(name, handler);
|
|
180
|
+
this.#detectors.push([name, handler]);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Add a detector to the start of the array.
|
|
184
|
+
* @param {string} name
|
|
185
|
+
* @param {ElementDetectorsFn} handler
|
|
186
|
+
*/
|
|
187
|
+
insertAtStart(name, handler) {
|
|
188
|
+
this.#validateDetector(name, handler);
|
|
189
|
+
this.#detectors.unshift([name, handler]);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Insert a detector at a specific index.
|
|
193
|
+
* @param {number} index
|
|
194
|
+
* @param {string} name
|
|
195
|
+
* @param {ElementDetectorsFn} handler
|
|
196
|
+
* @param {'before'|'after'} position - Position relative to the index
|
|
197
|
+
*/
|
|
198
|
+
insertAt(index, name, handler, position = 'after') {
|
|
199
|
+
this.#validateDetector(name, handler);
|
|
200
|
+
if (typeof index !== 'number' || index < 0 || index >= this.#detectors.length)
|
|
201
|
+
throw new RangeError('Invalid index for insertDetectorAt.');
|
|
202
|
+
const insertIndex = position === 'before' ? index : index + 1;
|
|
203
|
+
this.#detectors.splice(insertIndex, 0, [name, handler]);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Remove a detector at a specific index.
|
|
207
|
+
* @param {number} index
|
|
208
|
+
*/
|
|
209
|
+
removeAt(index) {
|
|
210
|
+
if (typeof index !== 'number' || index < 0 || index >= this.#detectors.length)
|
|
211
|
+
throw new RangeError('Invalid index for removeDetectorAt.');
|
|
212
|
+
this.#detectors.splice(index, 1);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Remove detectors relative to a specific index.
|
|
216
|
+
* @param {number} index - Reference index
|
|
217
|
+
* @param {number} before - Number of items before the index to remove
|
|
218
|
+
* @param {number} after - Number of items after the index to remove
|
|
219
|
+
*/
|
|
220
|
+
removeAround(index, before = 0, after = 0) {
|
|
221
|
+
if (typeof index !== 'number' || index < 0 || index >= this.#detectors.length)
|
|
222
|
+
throw new RangeError('Invalid index for removeDetectorsAround.');
|
|
223
|
+
const start = Math.max(0, index - before);
|
|
224
|
+
const deleteCount = before + 1 + after;
|
|
225
|
+
this.#detectors.splice(start, deleteCount);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Check if a detector exists at a specific index.
|
|
229
|
+
* @param {number} index
|
|
230
|
+
* @returns {boolean}
|
|
231
|
+
*/
|
|
232
|
+
isIndexUsed(index) {
|
|
233
|
+
return index >= 0 && index < this.#detectors.length;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Check if a handler function already exists in the array.
|
|
237
|
+
* @param {ElementDetectorsFn} handler
|
|
238
|
+
* @returns {boolean}
|
|
239
|
+
*/
|
|
240
|
+
hasHandler(handler) {
|
|
241
|
+
if (typeof handler !== 'function')
|
|
242
|
+
throw new TypeError('Handler must be a function.');
|
|
243
|
+
return this.#detectors.some(([_, fn]) => fn === handler);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Internal validation for detector entries.
|
|
247
|
+
* @param {string} name
|
|
248
|
+
* @param {ElementDetectorsFn} handler
|
|
249
|
+
*/
|
|
250
|
+
#validateDetector(name, handler) {
|
|
251
|
+
if (typeof name !== 'string' || !name.trim())
|
|
252
|
+
throw new TypeError('Detector name must be a non-empty string.');
|
|
253
|
+
if (typeof handler !== 'function')
|
|
254
|
+
throw new TypeError(`Detector handler for "${name}" must be a function.`);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Completely destroy this observer instance.
|
|
258
|
+
* Stops the MutationObserver (if active) and clears all detectors,
|
|
259
|
+
* leaving the instance unusable until reconfigured.
|
|
260
|
+
*/
|
|
261
|
+
destroy() {
|
|
262
|
+
this.stop();
|
|
263
|
+
this.clear();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
export default TinyElementObserver;
|