tiny-essentials 1.14.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/v1/TinyBasicsEs.js +4585 -270
  2. package/dist/v1/TinyBasicsEs.min.js +1 -1
  3. package/dist/v1/TinyDragger.js +4386 -471
  4. package/dist/v1/TinyDragger.min.js +1 -1
  5. package/dist/v1/TinyEssentials.js +4634 -242
  6. package/dist/v1/TinyEssentials.min.js +1 -1
  7. package/dist/v1/TinyHtml.js +4327 -0
  8. package/dist/v1/TinyHtml.min.js +1 -0
  9. package/dist/v1/TinyUploadClicker.js +4628 -246
  10. package/dist/v1/TinyUploadClicker.min.js +1 -1
  11. package/dist/v1/basics/collision.cjs +413 -0
  12. package/dist/v1/basics/collision.d.mts +187 -0
  13. package/dist/v1/basics/collision.mjs +350 -0
  14. package/dist/v1/basics/html.cjs +5 -109
  15. package/dist/v1/basics/html.d.mts +2 -28
  16. package/dist/v1/basics/html.mjs +5 -91
  17. package/dist/v1/basics/html_deprecated.cjs +124 -0
  18. package/dist/v1/basics/html_deprecated.d.mts +40 -0
  19. package/dist/v1/basics/html_deprecated.mjs +97 -0
  20. package/dist/v1/basics/index.cjs +27 -5
  21. package/dist/v1/basics/index.d.mts +26 -6
  22. package/dist/v1/basics/index.mjs +4 -2
  23. package/dist/v1/build/TinyHtml.cjs +7 -0
  24. package/dist/v1/build/TinyHtml.d.mts +3 -0
  25. package/dist/v1/build/TinyHtml.mjs +2 -0
  26. package/dist/v1/index.cjs +29 -5
  27. package/dist/v1/index.d.mts +27 -6
  28. package/dist/v1/index.mjs +5 -2
  29. package/dist/v1/libs/TinyDragger.cjs +91 -16
  30. package/dist/v1/libs/TinyDragger.d.mts +78 -2
  31. package/dist/v1/libs/TinyDragger.mjs +93 -17
  32. package/dist/v1/libs/TinyHtml.cjs +3859 -0
  33. package/dist/v1/libs/TinyHtml.d.mts +2151 -0
  34. package/dist/v1/libs/TinyHtml.mjs +3457 -0
  35. package/docs/v1/README.md +2 -0
  36. package/docs/v1/basics/collision.md +237 -0
  37. package/docs/v1/basics/html.md +1 -105
  38. package/docs/v1/basics/html_deprecated.md +127 -0
  39. package/docs/v1/libs/TinyDragger.md +45 -15
  40. package/docs/v1/libs/TinyHtml.md +1333 -0
  41. package/package.json +8 -2
@@ -2143,6 +2143,16 @@ __webpack_require__.r(__webpack_exports__);
2143
2143
  // EXPORTS
2144
2144
  __webpack_require__.d(__webpack_exports__, {
2145
2145
  addAiMarkerShortcut: () => (/* reexport */ addAiMarkerShortcut),
2146
+ areElsCollBottom: () => (/* reexport */ areElsCollBottom),
2147
+ areElsCollLeft: () => (/* reexport */ areElsCollLeft),
2148
+ areElsCollPerfBottom: () => (/* reexport */ areElsCollPerfBottom),
2149
+ areElsCollPerfLeft: () => (/* reexport */ areElsCollPerfLeft),
2150
+ areElsCollPerfRight: () => (/* reexport */ areElsCollPerfRight),
2151
+ areElsCollPerfTop: () => (/* reexport */ areElsCollPerfTop),
2152
+ areElsCollRight: () => (/* reexport */ areElsCollRight),
2153
+ areElsCollTop: () => (/* reexport */ areElsCollTop),
2154
+ areElsColliding: () => (/* reexport */ areElsColliding),
2155
+ areElsPerfColliding: () => (/* reexport */ areElsPerfColliding),
2146
2156
  areHtmlElsColliding: () => (/* reexport */ areHtmlElsColliding),
2147
2157
  arraySortPositions: () => (/* reexport */ arraySortPositions),
2148
2158
  asyncReplace: () => (/* reexport */ asyncReplace),
@@ -2158,16 +2168,26 @@ __webpack_require__.d(__webpack_exports__, {
2158
2168
  formatTimer: () => (/* reexport */ formatTimer),
2159
2169
  genFibonacciSeq: () => (/* reexport */ genFibonacciSeq),
2160
2170
  getAge: () => (/* reexport */ getAge),
2171
+ getElsCollDetails: () => (/* reexport */ getElsCollDetails),
2172
+ getElsCollDirDepth: () => (/* reexport */ getElsCollDirDepth),
2173
+ getElsCollOverlap: () => (/* reexport */ getElsCollOverlap),
2174
+ getElsCollOverlapPos: () => (/* reexport */ getElsCollOverlapPos),
2175
+ getElsColliding: () => (/* reexport */ getElsColliding),
2176
+ getElsPerfColliding: () => (/* reexport */ getElsPerfColliding),
2177
+ getElsRelativeCenterOffset: () => (/* reexport */ getElsRelativeCenterOffset),
2161
2178
  getHtmlElBorders: () => (/* reexport */ getHtmlElBorders),
2162
2179
  getHtmlElBordersWidth: () => (/* reexport */ getHtmlElBordersWidth),
2163
2180
  getHtmlElMargin: () => (/* reexport */ getHtmlElMargin),
2164
2181
  getHtmlElPadding: () => (/* reexport */ getHtmlElPadding),
2182
+ getRectCenter: () => (/* reexport */ getRectCenter),
2165
2183
  getSimplePerc: () => (/* reexport */ getSimplePerc),
2166
2184
  getTimeDuration: () => (/* reexport */ getTimeDuration),
2167
2185
  installWindowHiddenScript: () => (/* reexport */ installWindowHiddenScript),
2168
2186
  isFullScreenMode: () => (/* reexport */ isFullScreenMode),
2187
+ isInViewport: () => (/* reexport */ isInViewport),
2169
2188
  isJsonObject: () => (/* reexport */ isJsonObject),
2170
2189
  isScreenFilled: () => (/* reexport */ isScreenFilled),
2190
+ isScrolledIntoView: () => (/* reexport */ isScrolledIntoView),
2171
2191
  objType: () => (/* reexport */ objType),
2172
2192
  offFullScreenChange: () => (/* reexport */ offFullScreenChange),
2173
2193
  onFullScreenChange: () => (/* reexport */ onFullScreenChange),
@@ -2184,6 +2204,30 @@ __webpack_require__.d(__webpack_exports__, {
2184
2204
  toTitleCaseLowerFirst: () => (/* reexport */ toTitleCaseLowerFirst)
2185
2205
  });
2186
2206
 
2207
+ // NAMESPACE OBJECT: ./src/v1/basics/collision.mjs
2208
+ var collision_namespaceObject = {};
2209
+ __webpack_require__.r(collision_namespaceObject);
2210
+ __webpack_require__.d(collision_namespaceObject, {
2211
+ areElsCollBottom: () => (areElsCollBottom),
2212
+ areElsCollLeft: () => (areElsCollLeft),
2213
+ areElsCollPerfBottom: () => (areElsCollPerfBottom),
2214
+ areElsCollPerfLeft: () => (areElsCollPerfLeft),
2215
+ areElsCollPerfRight: () => (areElsCollPerfRight),
2216
+ areElsCollPerfTop: () => (areElsCollPerfTop),
2217
+ areElsCollRight: () => (areElsCollRight),
2218
+ areElsCollTop: () => (areElsCollTop),
2219
+ areElsColliding: () => (areElsColliding),
2220
+ areElsPerfColliding: () => (areElsPerfColliding),
2221
+ getElsCollDetails: () => (getElsCollDetails),
2222
+ getElsCollDirDepth: () => (getElsCollDirDepth),
2223
+ getElsCollOverlap: () => (getElsCollOverlap),
2224
+ getElsCollOverlapPos: () => (getElsCollOverlapPos),
2225
+ getElsColliding: () => (getElsColliding),
2226
+ getElsPerfColliding: () => (getElsPerfColliding),
2227
+ getElsRelativeCenterOffset: () => (getElsRelativeCenterOffset),
2228
+ getRectCenter: () => (getRectCenter)
2229
+ });
2230
+
2187
2231
  ;// ./src/legacy/libs/arraySortPositions.mjs
2188
2232
  /**
2189
2233
  * Generates a comparator function to sort an array of objects by a given key.
@@ -2854,24 +2898,7 @@ extendObjType([
2854
2898
  ;// ./src/v1/basics/html.mjs
2855
2899
 
2856
2900
 
2857
- /**
2858
- * Checks if two DOM elements are colliding on the screen.
2859
- *
2860
- * @param {Element} elem1 - First DOM element.
2861
- * @param {Element} elem2 - Second DOM element.
2862
- * @returns {boolean} - Returns true if the elements are colliding.
2863
- */
2864
- function areHtmlElsColliding(elem1, elem2) {
2865
- const rect1 = elem1.getBoundingClientRect();
2866
- const rect2 = elem2.getBoundingClientRect();
2867
-
2868
- return !(
2869
- rect1.right < rect2.left ||
2870
- rect1.left > rect2.right ||
2871
- rect1.bottom < rect2.top ||
2872
- rect1.top > rect2.bottom
2873
- );
2874
- }
2901
+ /////////////////////////////////////////////////////////////////
2875
2902
 
2876
2903
  /**
2877
2904
  * Reads the contents of a file using the specified FileReader method.
@@ -3092,89 +3119,7 @@ async function fetchJson(
3092
3119
  );
3093
3120
  }
3094
3121
 
3095
- /**
3096
- * @typedef {Object} HtmlElBoxSides
3097
- * @property {number} x - Total horizontal size (left + right)
3098
- * @property {number} y - Total vertical size (top + bottom)
3099
- * @property {number} left
3100
- * @property {number} right
3101
- * @property {number} top
3102
- * @property {number} bottom
3103
- */
3104
-
3105
- /**
3106
- * Returns the total border width and individual sides from `border{Side}Width` CSS properties.
3107
- *
3108
- * @param {Element} el - The target DOM element.
3109
- * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border widths, and each side individually.
3110
- */
3111
- const getHtmlElBordersWidth = (el) => {
3112
- const styles = getComputedStyle(el);
3113
- const left = parseFloat(styles.borderLeftWidth) || 0;
3114
- const right = parseFloat(styles.borderRightWidth) || 0;
3115
- const top = parseFloat(styles.borderTopWidth) || 0;
3116
- const bottom = parseFloat(styles.borderBottomWidth) || 0;
3117
- const x = left + right;
3118
- const y = top + bottom;
3119
-
3120
- return { x, y, left, right, top, bottom };
3121
- };
3122
-
3123
- /**
3124
- * Returns the total border size and individual sides from `border{Side}` CSS properties.
3125
- *
3126
- * @param {Element} el - The target DOM element.
3127
- * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border sizes, and each side individually.
3128
- */
3129
- const getHtmlElBorders = (el) => {
3130
- const styles = getComputedStyle(el);
3131
- const left = parseFloat(styles.borderLeft) || 0;
3132
- const right = parseFloat(styles.borderRight) || 0;
3133
- const top = parseFloat(styles.borderTop) || 0;
3134
- const bottom = parseFloat(styles.borderBottom) || 0;
3135
- const x = left + right;
3136
- const y = top + bottom;
3137
-
3138
- return { x, y, left, right, top, bottom };
3139
- };
3140
-
3141
- /**
3142
- * Returns the total margin and individual sides from `margin{Side}` CSS properties.
3143
- *
3144
- * @param {Element} el - The target DOM element.
3145
- * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) margins, and each side individually.
3146
- */
3147
- const getHtmlElMargin = (el) => {
3148
- const styles = getComputedStyle(el);
3149
- const left = parseFloat(styles.marginLeft) || 0;
3150
- const right = parseFloat(styles.marginRight) || 0;
3151
- const top = parseFloat(styles.marginTop) || 0;
3152
- const bottom = parseFloat(styles.marginBottom) || 0;
3153
-
3154
- const x = left + right;
3155
- const y = top + bottom;
3156
-
3157
- return { x, y, left, right, top, bottom };
3158
- };
3159
-
3160
- /**
3161
- * Returns the total padding and individual sides from `padding{Side}` CSS properties.
3162
- *
3163
- * @param {Element} el - The target DOM element.
3164
- * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) paddings, and each side individually.
3165
- */
3166
- const getHtmlElPadding = (el) => {
3167
- const styles = getComputedStyle(el);
3168
- const left = parseFloat(styles.paddingLeft) || 0;
3169
- const right = parseFloat(styles.paddingRight) || 0;
3170
- const top = parseFloat(styles.paddingTop) || 0;
3171
- const bottom = parseFloat(styles.paddingBottom) || 0;
3172
-
3173
- const x = left + right;
3174
- const y = top + bottom;
3175
-
3176
- return { x, y, left, right, top, bottom };
3177
- };
3122
+ ///////////////////////////////////////////////////////////////////////////////
3178
3123
 
3179
3124
  /**
3180
3125
  * Installs a script that toggles CSS classes on a given element
@@ -3182,7 +3127,7 @@ const getHtmlElPadding = (el) => {
3182
3127
  * triggers callbacks on visibility changes.
3183
3128
  *
3184
3129
  * @param {Object} [settings={}]
3185
- * @param {HTMLElement} [settings.element=document.body] - The element to receive visibility classes.
3130
+ * @param {Element} [settings.element=document.body] - The element to receive visibility classes.
3186
3131
  * @param {string} [settings.hiddenClass='windowHidden'] - CSS class applied when the page is hidden.
3187
3132
  * @param {string} [settings.visibleClass='windowVisible'] - CSS class applied when the page is visible.
3188
3133
  * @param {() => void} [settings.onVisible] - Callback called when page becomes visible.
@@ -3197,8 +3142,8 @@ function installWindowHiddenScript({
3197
3142
  onVisible,
3198
3143
  onHidden,
3199
3144
  } = {}) {
3200
- if (!(element instanceof HTMLElement))
3201
- throw new TypeError(`"element" must be an instance of HTMLElement.`);
3145
+ if (!(element instanceof Element))
3146
+ throw new TypeError(`"element" must be an instance of Element.`);
3202
3147
  if (typeof hiddenClass !== 'string') throw new TypeError(`"hiddenClass" must be a string.`);
3203
3148
  if (typeof visibleClass !== 'string') throw new TypeError(`"visibleClass" must be a string.`);
3204
3149
  if (onVisible !== undefined && typeof onVisible !== 'function')
@@ -3299,224 +3244,4592 @@ function installWindowHiddenScript({
3299
3244
  return uninstall;
3300
3245
  }
3301
3246
 
3302
- ;// ./src/v1/basics/fullScreen.mjs
3247
+ ;// ./src/v1/basics/collision.mjs
3303
3248
  /**
3304
- * Checks if the document is currently in fullscreen mode.
3249
+ * A direction relative to a rectangle.
3305
3250
  *
3306
- * @returns {boolean}
3251
+ * Represents one of the four cardinal directions from the perspective of the element.
3252
+ *
3253
+ * @typedef {'top' | 'bottom' | 'left' | 'right'} Dirs
3307
3254
  */
3308
- const documentIsFullScreen = () =>
3309
- !!(
3310
- document.fullscreenElement ||
3311
- // @ts-ignore
3312
- document.webkitFullscreenElement ||
3313
- // @ts-ignore
3314
- document.mozFullScreenElement ||
3315
- // @ts-ignore
3316
- document.msFullscreenElement ||
3317
- // @ts-ignore
3318
- document.webkitIsFullScreen ||
3319
- // @ts-ignore
3320
- document.mozFullScreen
3321
- );
3322
3255
 
3323
3256
  /**
3324
- * Checks if the window occupies the entire screen dimensions.
3257
+ * Represents all directional aspects of a collision.
3325
3258
  *
3326
- * @returns {boolean}
3259
+ * @typedef {Object} CollDirs
3260
+ * @property {Dirs | 'center' | null} in - The dominant direction of entry. `'center'` if all sides are equally overlapped. `null` if no collision.
3261
+ * @property {Dirs | null} x - The horizontal direction (`'left'` or `'right'`) the collision is biased toward, or `null`.
3262
+ * @property {Dirs | null} y - The vertical direction (`'top'` or `'bottom'`) the collision is biased toward, or `null`.
3327
3263
  */
3328
- const isScreenFilled = () =>
3329
- window.innerHeight === screen.height && window.innerWidth === screen.width;
3330
3264
 
3331
3265
  /**
3332
- * Checks if fullscreen mode is active either via document API or by matching screen dimensions.
3266
+ * Indicates if a collision is in the negative direction (rect2 is outside rect1).
3333
3267
  *
3334
- * @returns {boolean}
3268
+ * @typedef {Object} NegCollDirs
3269
+ * @property {Dirs | null} x - Horizontal direction of negative overlap, if any.
3270
+ * @property {Dirs | null} y - Vertical direction of negative overlap, if any.
3335
3271
  */
3336
- const isFullScreenMode = () => documentIsFullScreen() || isScreenFilled();
3337
3272
 
3338
3273
  /**
3339
- * Requests fullscreen mode for the document.
3274
+ * Collision depth values from each side of rect2 inside rect1.
3340
3275
  *
3341
- * @param {FullscreenOptions} [ops]
3342
- * @returns {Promise<void>}
3276
+ * Positive values indicate penetration; negative values indicate gaps.
3277
+ *
3278
+ * @typedef {Object} CollData
3279
+ * @property {number} top - Depth from rect2's top into rect1.
3280
+ * @property {number} bottom - Depth from rect2's bottom into rect1.
3281
+ * @property {number} left - Depth from rect2's left into rect1.
3282
+ * @property {number} right - Depth from rect2's right into rect1.
3343
3283
  */
3344
- const requestFullScreen = (ops) =>
3345
- new Promise(async (resolve, reject) => {
3346
- const docElm = document.documentElement;
3347
- try {
3348
- if (docElm.requestFullscreen) {
3349
- await docElm.requestFullscreen(ops);
3350
- // @ts-ignore
3351
- } else if (docElm.mozRequestFullScreen) {
3352
- // @ts-ignore
3353
- docElm.mozRequestFullScreen(ops);
3354
- // @ts-ignore
3355
- } else if (docElm.webkitRequestFullScreen) {
3356
- // @ts-ignore
3357
- docElm.webkitRequestFullScreen(ops);
3358
- // @ts-ignore
3359
- } else if (docElm.msRequestFullscreen) {
3360
- // @ts-ignore
3361
- docElm.msRequestFullscreen(ops);
3362
- }
3363
- resolve();
3364
- } catch (err) {
3365
- reject(err);
3366
- }
3367
- });
3368
3284
 
3369
3285
  /**
3370
- * Exits fullscreen mode.
3286
+ * X and Y offset representing center difference between two rectangles.
3371
3287
  *
3372
- * @returns {Promise<void>}
3288
+ * Useful to measure how far one element's center is from another.
3289
+ *
3290
+ * @typedef {Object} CollCenter
3291
+ * @property {number} x - Horizontal distance in pixels from rect1's center to rect2's center.
3292
+ * @property {number} y - Vertical distance in pixels from rect1's center to rect2's center.
3373
3293
  */
3374
- const exitFullScreen = () =>
3375
- new Promise((resolve, reject) => {
3376
- if (document.exitFullscreen) {
3377
- document.exitFullscreen().then(resolve).catch(reject);
3378
- } else {
3379
- try {
3380
- // @ts-ignore
3381
- if (document.mozCancelFullScreen) document.mozCancelFullScreen();
3382
- // @ts-ignore
3383
- else if (document.webkitCancelFullScreen) document.webkitCancelFullScreen();
3384
- // @ts-ignore
3385
- else if (document.msExitFullscreen) document.msExitFullscreen();
3386
- else throw new Error('Fullscreen API is not supported');
3387
- resolve();
3388
- } catch (err) {
3389
- reject(err);
3390
- }
3391
- }
3392
- });
3393
-
3394
- /** @type {readonly string[]} */
3395
- const fullScreenEvents = [
3396
- 'fullscreenchange',
3397
- 'webkitfullscreenchange',
3398
- 'mozfullscreenchange',
3399
- 'MSFullscreenChange',
3400
- ];
3401
3294
 
3402
3295
  /**
3403
- * Attaches a listener to fullscreen change events.
3296
+ * Represents a rectangular area in absolute pixel values.
3404
3297
  *
3405
- * @param {EventListenerOrEventListenerObject} listener
3406
- * @param {boolean|AddEventListenerOptions} [ops]
3407
- * @returns {void}
3298
+ * Similar to `DOMRect`, this object describes the dimensions and position of a box
3299
+ * in the 2D plane, typically representing an element's bounding box.
3300
+ *
3301
+ * @typedef {Object} ObjRect
3302
+ * @property {number} height - The total height of the rectangle in pixels.
3303
+ * @property {number} width - The total width of the rectangle in pixels.
3304
+ * @property {number} top - The Y-coordinate of the top edge of the rectangle.
3305
+ * @property {number} bottom - The Y-coordinate of the bottom edge of the rectangle.
3306
+ * @property {number} left - The X-coordinate of the left edge of the rectangle.
3307
+ * @property {number} right - The X-coordinate of the right edge of the rectangle.
3408
3308
  */
3409
- const onFullScreenChange = (listener, ops) => {
3410
- fullScreenEvents.forEach((event) => {
3411
- document.addEventListener(event, listener, ops);
3412
- });
3413
- };
3309
+
3310
+ // Normal collision checks (loose overlap detection)
3414
3311
 
3415
3312
  /**
3416
- * Removes a listener from fullscreen change events.
3313
+ * Checks if rect1 is completely above rect2 (no vertical overlap).
3417
3314
  *
3418
- * @param {EventListenerOrEventListenerObject} listener
3419
- * @param {boolean|AddEventListenerOptions} [ops]
3420
- * @returns {void}
3315
+ * @param {ObjRect} rect1 - The bounding rectangle of the first element.
3316
+ * @param {ObjRect} rect2 - The bounding rectangle of the second element.
3317
+ * @returns {boolean} True if rect1 is entirely above rect2.
3421
3318
  */
3422
- const offFullScreenChange = (listener, ops) => {
3423
- fullScreenEvents.forEach((event) => {
3424
- document.removeEventListener(event, listener, ops);
3425
- });
3426
- };
3319
+ const areElsCollTop = (rect1, rect2) => rect1.bottom < rect2.top;
3427
3320
 
3428
- ;// ./src/v1/basics/simpleMath.mjs
3429
3321
  /**
3430
- * Executes a Rule of Three calculation.
3431
- *
3432
- * @param {number} val1 - The first reference value (numerator in direct proportion, denominator in inverse).
3433
- * @param {number} val2 - The second reference value (denominator in direct proportion, numerator in inverse).
3434
- * @param {number} val3 - The third value (numerator in direct proportion, denominator in inverse).
3435
- * @param {boolean} [inverse] - Whether the calculation should use inverse proportion (true for inverse, false for direct).
3436
- * @returns {number} The result of the Rule of Three operation.
3437
- *
3438
- * Rule of Three Formula (Direct Proportion):
3439
- * val1 / val2 = val3 / result
3440
- *
3441
- * For Inverse Proportion:
3442
- * val1 / val3 = val2 / result
3443
- *
3444
- * Visual Representation:
3445
- *
3446
- * For Direct Proportion:
3447
- * val1 val2
3448
- * ----- = ------
3449
- * val3 result
3450
- *
3451
- * For Inverse Proportion:
3452
- * val1 val2
3453
- * ----- = ------
3454
- * val3 result
3322
+ * Checks if rect1 is completely below rect2 (no vertical overlap).
3455
3323
  *
3456
- * @example
3457
- * // Direct proportion:
3458
- * ruleOfThree.execute(2, 6, 3, false); // 9
3324
+ * @param {ObjRect} rect1
3325
+ * @param {ObjRect} rect2
3326
+ * @returns {boolean} True if rect1 is entirely below rect2.
3327
+ */
3328
+ const areElsCollBottom = (rect1, rect2) => rect1.top > rect2.bottom;
3329
+
3330
+ /**
3331
+ * Checks if rect1 is completely to the left of rect2 (no horizontal overlap).
3459
3332
  *
3460
- * @example
3461
- * // Inverse proportion:
3462
- * ruleOfThree.execute(2, 6, 3, true); // 4
3333
+ * @param {ObjRect} rect1
3334
+ * @param {ObjRect} rect2
3335
+ * @returns {boolean} True if rect1 is entirely to the left of rect2.
3463
3336
  */
3464
- function ruleOfThree(val1, val2, val3, inverse = false) {
3465
- return inverse ? Number(val1 * val2) / val3 : Number(val3 * val2) / val1;
3466
- }
3337
+ const areElsCollLeft = (rect1, rect2) => rect1.right < rect2.left;
3467
3338
 
3468
3339
  /**
3469
- * Calculates a percentage of a given base value.
3470
- * @param {number} price - The base value.
3471
- * @param {number} percentage - The percentage to apply.
3472
- * @returns {number} The result of the percentage calculation.
3340
+ * Checks if rect1 is completely to the right of rect2 (no horizontal overlap).
3473
3341
  *
3474
- * @example
3475
- * getSimplePerc(200, 15); // 30
3342
+ * @param {ObjRect} rect1
3343
+ * @param {ObjRect} rect2
3344
+ * @returns {boolean} True if rect1 is entirely to the right of rect2.
3476
3345
  */
3477
- function getSimplePerc(price, percentage) {
3478
- return price * (percentage / 100);
3479
- }
3346
+ const areElsCollRight = (rect1, rect2) => rect1.left > rect2.right;
3347
+
3348
+ // Perfect collision checks (touch included)
3480
3349
 
3481
3350
  /**
3482
- * Calculates the age based on the given date.
3351
+ * Checks if rect1 is perfectly above rect2 (no vertical touch or overlap).
3483
3352
  *
3484
- * @param {number|string|Date} timeData - The birth date (can be a timestamp, ISO string, or Date object).
3485
- * @param {Date|null} [now=null] - The Date object representing the current date. Defaults to the current date and time if not provided.
3486
- * @returns {number|null} The age in years, or null if `timeData` is not provided or invalid.
3353
+ * @param {ObjRect} rect1
3354
+ * @param {ObjRect} rect2
3355
+ * @returns {boolean} True if rect1 is fully above or touching rect2's top.
3487
3356
  */
3488
- function getAge(timeData = 0, now = null) {
3489
- if (typeof timeData !== 'undefined' && timeData !== null && timeData !== 0) {
3490
- const birthDate = new Date(timeData);
3491
- if (Number.isNaN(birthDate.getTime())) return null;
3357
+ const areElsCollPerfTop = (rect1, rect2) => rect1.bottom <= rect2.top;
3492
3358
 
3493
- const currentDate = now instanceof Date ? now : new Date();
3359
+ /**
3360
+ * Checks if rect1 is perfectly below rect2 (no vertical touch or overlap).
3361
+ *
3362
+ * @param {ObjRect} rect1
3363
+ * @param {ObjRect} rect2
3364
+ * @returns {boolean} True if rect1 is fully below or touching rect2's bottom.
3365
+ */
3366
+ const areElsCollPerfBottom = (rect1, rect2) => rect1.top >= rect2.bottom;
3494
3367
 
3495
- let age = currentDate.getFullYear() - birthDate.getFullYear();
3368
+ /**
3369
+ * Checks if rect1 is perfectly to the left of rect2 (no horizontal touch or overlap).
3370
+ *
3371
+ * @param {ObjRect} rect1
3372
+ * @param {ObjRect} rect2
3373
+ * @returns {boolean} True if rect1 is fully left or touching rect2's left.
3374
+ */
3375
+ const areElsCollPerfLeft = (rect1, rect2) => rect1.right <= rect2.left;
3496
3376
 
3497
- const currentMonth = currentDate.getMonth();
3498
- const birthMonth = birthDate.getMonth();
3377
+ /**
3378
+ * Checks if rect1 is perfectly to the right of rect2 (no horizontal touch or overlap).
3379
+ *
3380
+ * @param {ObjRect} rect1
3381
+ * @param {ObjRect} rect2
3382
+ * @returns {boolean} True if rect1 is fully right or touching rect2's right.
3383
+ */
3384
+ const areElsCollPerfRight = (rect1, rect2) => rect1.left >= rect2.right;
3499
3385
 
3500
- const currentDay = currentDate.getDate();
3501
- const birthDay = birthDate.getDate();
3386
+ // Main collision check
3502
3387
 
3503
- // Adjust if birthday hasn't occurred yet this year
3504
- if (currentMonth < birthMonth || (currentMonth === birthMonth && currentDay < birthDay)) age--;
3388
+ /**
3389
+ * Returns true if rect1 and rect2 are colliding (partially or fully overlapping).
3390
+ *
3391
+ * @param {ObjRect} rect1
3392
+ * @param {ObjRect} rect2
3393
+ * @returns {boolean} True if there's any collision between rect1 and rect2.
3394
+ */
3395
+ const areElsColliding = (rect1, rect2) =>
3396
+ !(
3397
+ areElsCollLeft(rect1, rect2) ||
3398
+ areElsCollRight(rect1, rect2) ||
3399
+ areElsCollTop(rect1, rect2) ||
3400
+ areElsCollBottom(rect1, rect2)
3401
+ );
3505
3402
 
3506
- return Math.abs(age);
3507
- }
3403
+ /**
3404
+ * Returns true if rect1 and rect2 are colliding or perfectly touching.
3405
+ *
3406
+ * @param {ObjRect} rect1
3407
+ * @param {ObjRect} rect2
3408
+ * @returns {boolean} True if there's any contact or overlap.
3409
+ */
3410
+ const areElsPerfColliding = (rect1, rect2) =>
3411
+ !(
3412
+ areElsCollPerfLeft(rect1, rect2) ||
3413
+ areElsCollPerfRight(rect1, rect2) ||
3414
+ areElsCollPerfTop(rect1, rect2) ||
3415
+ areElsCollPerfBottom(rect1, rect2)
3416
+ );
3508
3417
 
3509
- return null;
3510
- }
3418
+ // Collision direction guess (loose and perfect)
3511
3419
 
3512
3420
  /**
3513
- * @typedef {Object} FormattedByteResult
3514
- * @property {string|null} unit - The resulting unit (e.g., 'MB', 'GB') or null if input is invalid.
3515
- * @property {number|null} value - The numerical value in the chosen unit, or null if input is invalid.
3421
+ * Attempts to determine the direction rect1 entered rect2 based on loose overlap rules.
3422
+ *
3423
+ * @param {ObjRect} rect1
3424
+ * @param {ObjRect} rect2
3425
+ * @returns {string|null} 'top' | 'bottom' | 'left' | 'right' | null
3516
3426
  */
3427
+ const getElsColliding = (rect1, rect2) => {
3428
+ if (areElsCollLeft(rect1, rect2)) return 'left';
3429
+ else if (areElsCollRight(rect1, rect2)) return 'right';
3430
+ else if (areElsCollTop(rect1, rect2)) return 'top';
3431
+ else if (areElsCollBottom(rect1, rect2)) return 'bottom';
3432
+ return null;
3433
+ };
3517
3434
 
3518
3435
  /**
3519
- * Converts a byte value into a human-readable format with unit and value separated.
3436
+ * Attempts to determine the direction rect1 touched or entered rect2 using perfect mode.
3437
+ *
3438
+ * @param {ObjRect} rect1
3439
+ * @param {ObjRect} rect2
3440
+ * @returns {'top' | 'bottom' | 'left' | 'right' | null}
3441
+ */
3442
+ const getElsPerfColliding = (rect1, rect2) => {
3443
+ if (areElsCollPerfLeft(rect1, rect2)) return 'left';
3444
+ else if (areElsCollPerfRight(rect1, rect2)) return 'right';
3445
+ else if (areElsCollPerfTop(rect1, rect2)) return 'top';
3446
+ else if (areElsCollPerfBottom(rect1, rect2)) return 'bottom';
3447
+ return null;
3448
+ };
3449
+
3450
+ // Overlap Calculation
3451
+
3452
+ /**
3453
+ * Calculates overlap values between rect1 and rect2 in all directions.
3454
+ *
3455
+ * @param {ObjRect} rect1
3456
+ * @param {ObjRect} rect2
3457
+ * @returns {{
3458
+ * overlapLeft: number,
3459
+ * overlapRight: number,
3460
+ * overlapTop: number,
3461
+ * overlapBottom: number
3462
+ * }} Distance of overlap from each direction (can be negative).
3463
+ */
3464
+ const getElsCollOverlap = (rect1, rect2) => ({
3465
+ overlapLeft: rect2.right - rect1.left,
3466
+ overlapRight: rect1.right - rect2.left,
3467
+ overlapTop: rect2.bottom - rect1.top,
3468
+ overlapBottom: rect1.bottom - rect2.top,
3469
+ });
3470
+
3471
+ /**
3472
+ * Determines directional collision based on overlap depth.
3473
+ *
3474
+ * @param {Object} [settings={}]
3475
+ * @param {number} [settings.overlapLeft]
3476
+ * @param {number} [settings.overlapRight]
3477
+ * @param {number} [settings.overlapTop]
3478
+ * @param {number} [settings.overlapBottom]
3479
+ * @returns {{ dirX: Dirs, dirY: Dirs }} Direction of strongest X/Y overlap.
3480
+ */
3481
+ const getElsCollOverlapPos = ({
3482
+ overlapLeft = -1,
3483
+ overlapRight = -1,
3484
+ overlapTop = -1,
3485
+ overlapBottom = -1,
3486
+ } = {}) => ({
3487
+ dirX: overlapLeft < overlapRight ? 'right' : 'left',
3488
+ dirY: overlapTop < overlapBottom ? 'bottom' : 'top',
3489
+ });
3490
+
3491
+ // Center utils
3492
+
3493
+ /**
3494
+ * Calculates the center point (X and Y) of a given Rect.
3495
+ *
3496
+ * @param {ObjRect} rect - The bounding rectangle of the element.
3497
+ * @returns {{ x: number, y: number }} An object with the `x` and `y` coordinates of the center.
3498
+ */
3499
+ const getRectCenter = (rect) => ({
3500
+ x: rect.left + rect.width / 2,
3501
+ y: rect.top + rect.height / 2,
3502
+ });
3503
+
3504
+ /**
3505
+ * Calculates the offset between the center of rect2 and the center of rect1.
3506
+ *
3507
+ * The values will be 0 when rect1 is perfectly centered over rect2.
3508
+ *
3509
+ * @param {ObjRect} rect1 - The bounding rectangle of the reference element.
3510
+ * @param {ObjRect} rect2 - The bounding rectangle of the element being compared.
3511
+ * @returns {{
3512
+ * x: number,
3513
+ * y: number
3514
+ * }} An object with the X and Y offset in pixels from rect1's center to rect2's center.
3515
+ */
3516
+ function getElsRelativeCenterOffset(rect1, rect2) {
3517
+ const center1X = rect1.left + rect1.width / 2;
3518
+ const center1Y = rect1.top + rect1.height / 2;
3519
+
3520
+ const center2X = rect2.left + rect2.width / 2;
3521
+ const center2Y = rect2.top + rect2.height / 2;
3522
+
3523
+ return {
3524
+ x: center2X - center1X,
3525
+ y: center2Y - center1Y,
3526
+ };
3527
+ }
3528
+
3529
+ // Direction & Depth detection
3530
+
3531
+ /**
3532
+ * Detects the direction of the dominant collision between two elements
3533
+ * and calculates how deep the overlap is in both x and y axes.
3534
+ *
3535
+ * @param {ObjRect} rect1 - The bounding rectangle of the first element.
3536
+ * @param {ObjRect} rect2 - The bounding rectangle of the second element.
3537
+ * @returns {{
3538
+ * inDir: Dirs | null;
3539
+ * dirX: Dirs | null;
3540
+ * dirY: Dirs | null;
3541
+ * depthX: number;
3542
+ * depthY: number;
3543
+ * }} An object containing the collision direction and how deep the overlap is.
3544
+ */
3545
+ function getElsCollDirDepth(rect1, rect2) {
3546
+ if (!areElsPerfColliding(rect1, rect2))
3547
+ return {
3548
+ inDir: null,
3549
+ dirX: null,
3550
+ dirY: null,
3551
+ depthX: 0,
3552
+ depthY: 0,
3553
+ };
3554
+
3555
+ const { overlapLeft, overlapRight, overlapTop, overlapBottom } = getElsCollOverlap(rect1, rect2);
3556
+ const { dirX, dirY } = getElsCollOverlapPos({
3557
+ overlapLeft,
3558
+ overlapRight,
3559
+ overlapTop,
3560
+ overlapBottom,
3561
+ });
3562
+ const depthX = Math.min(overlapLeft, overlapRight);
3563
+ const depthY = Math.min(overlapTop, overlapBottom);
3564
+
3565
+ /** @type {Dirs} */
3566
+ let inDir;
3567
+
3568
+ if (depthX < depthY) inDir = dirX;
3569
+ else inDir = dirY;
3570
+ return { inDir, dirX, dirY, depthX, depthY };
3571
+ }
3572
+
3573
+ // Full detail report
3574
+
3575
+ /**
3576
+ * Detects the collision direction and depth between two DOMRects.
3577
+ *
3578
+ * @param {ObjRect} rect1 - The bounding rectangle of the first element.
3579
+ * @param {ObjRect} rect2 - The bounding rectangle of the second element.
3580
+ * @returns {{ depth: CollData; dirs: CollDirs; isNeg: NegCollDirs; }} Collision info or null if no collision is detected.
3581
+ */
3582
+ function getElsCollDetails(rect1, rect2) {
3583
+ const isColliding = areElsPerfColliding(rect1, rect2);
3584
+
3585
+ /** @type {CollDirs} */
3586
+ const dirs = { in: null, x: null, y: null };
3587
+
3588
+ /** @type {NegCollDirs} */
3589
+ const isNeg = { y: null, x: null };
3590
+
3591
+ /** @type {Record<Dirs, number>} */
3592
+ const depth = { top: 0, bottom: 0, left: 0, right: 0 };
3593
+
3594
+ // Depth
3595
+ // Yes, it's actually reversed the values orders
3596
+ const { overlapLeft, overlapRight, overlapTop, overlapBottom } = getElsCollOverlap(rect2, rect1);
3597
+ depth.top = overlapTop;
3598
+ depth.bottom = overlapBottom;
3599
+ depth.left = overlapLeft;
3600
+ depth.right = overlapRight;
3601
+
3602
+ // Dirs
3603
+ /**
3604
+ * Detect the direction with the smallest positive overlap (entry point)
3605
+ * @type {[Dirs, number][]}
3606
+ */
3607
+ // @ts-ignore
3608
+ const entries = Object.entries(depth)
3609
+ .filter(([, val]) => val > 0)
3610
+ .sort((a, b) => a[1] - b[1]);
3611
+
3612
+ // Yes, it's actually reversed the values orders here too
3613
+ const { dirX, dirY } = getElsCollOverlapPos({
3614
+ overlapLeft: overlapRight,
3615
+ overlapRight: overlapLeft,
3616
+ overlapTop: overlapBottom,
3617
+ overlapBottom: overlapTop,
3618
+ });
3619
+ dirs.y = dirY;
3620
+ dirs.x = dirX;
3621
+
3622
+ // isNeg
3623
+ if (depth.bottom < 0) isNeg.y = 'bottom';
3624
+ else if (depth.top < 0) isNeg.y = 'top';
3625
+ if (depth.left < 0) isNeg.x = 'left';
3626
+ else if (depth.right < 0) isNeg.x = 'right';
3627
+
3628
+ // Inside Dir
3629
+ dirs.in = isColliding
3630
+ ? depth.top === depth.bottom && depth.bottom === depth.left && depth.left === depth.right
3631
+ ? 'center'
3632
+ : entries.length
3633
+ ? entries[0][0]
3634
+ : 'top'
3635
+ : null; // fallback in case of exact match
3636
+
3637
+ // Complete
3638
+ return { dirs, depth, isNeg };
3639
+ }
3640
+
3641
+ ;// ./src/v1/libs/TinyHtml.mjs
3642
+
3643
+
3644
+ const {
3645
+ areElsColliding: TinyHtml_areElsColliding,
3646
+ areElsPerfColliding: TinyHtml_areElsPerfColliding,
3647
+ areElsCollTop: TinyHtml_areElsCollTop,
3648
+ areElsCollBottom: TinyHtml_areElsCollBottom,
3649
+ areElsCollLeft: TinyHtml_areElsCollLeft,
3650
+ areElsCollRight: TinyHtml_areElsCollRight,
3651
+ } = collision_namespaceObject;
3652
+
3653
+ /**
3654
+ * Represents a raw Node element or an instance of TinyHtml.
3655
+ * This type is used to abstract interactions with both plain elements
3656
+ * and wrapped elements via the TinyHtml class.
3657
+ *
3658
+ * @typedef {Node|TinyHtml|null} TinyNode
3659
+ */
3660
+
3661
+ /**
3662
+ * Represents a raw DOM element or an instance of TinyHtml.
3663
+ * This type is used to abstract interactions with both plain elements
3664
+ * and wrapped elements via the TinyHtml class.
3665
+ *
3666
+ * @typedef {Element|TinyHtml} TinyElement
3667
+ */
3668
+
3669
+ /**
3670
+ * Represents a raw DOM html element or an instance of TinyHtml.
3671
+ * This type is used to abstract interactions with both plain elements
3672
+ * and wrapped elements via the TinyHtml class.
3673
+ *
3674
+ * @typedef {HTMLElement|TinyHtml} TinyHtmlElement
3675
+ */
3676
+
3677
+ /**
3678
+ * Represents a raw DOM event target element or an instance of TinyHtml.
3679
+ * This type is used to abstract interactions with both plain elements
3680
+ * and wrapped elements via the TinyHtml class.
3681
+ *
3682
+ * @typedef {EventTarget|TinyHtml} TinyEventTarget
3683
+ */
3684
+
3685
+ /**
3686
+ * Represents a raw DOM input element or an instance of TinyHtml.
3687
+ * This type is used to abstract interactions with both plain elements
3688
+ * and wrapped elements via the TinyHtml class.
3689
+ *
3690
+ * @typedef {InputElement|TinyHtml} TinyInputElement
3691
+ */
3692
+
3693
+ /**
3694
+ * Represents a raw DOM element/window or an instance of TinyHtml.
3695
+ * This type is used to abstract interactions with both plain elements
3696
+ * and wrapped elements via the TinyHtml class.
3697
+ *
3698
+ * @typedef {ElementAndWindow|TinyHtml} TinyElementAndWindow
3699
+ */
3700
+
3701
+ /**
3702
+ * Represents a value that can be either a DOM Element or the global Window object.
3703
+ * Useful for functions that operate generically on scrollable or measurable targets.
3704
+ *
3705
+ * @typedef {Element|Window} ElementAndWindow
3706
+ */
3707
+
3708
+ /**
3709
+ * A parameter type used for filtering or matching elements.
3710
+ * It can be:
3711
+ * - A string (CSS selector),
3712
+ * - A raw DOM element,
3713
+ * - An array of raw DOM elements,
3714
+ * - A filtering function that receives an index and element,
3715
+ * and returns true if it matches.
3716
+ *
3717
+ * @typedef {string|Element|Element[]|((index: number, el: Element) => boolean)} WinnowRequest
3718
+ */
3719
+
3720
+ /**
3721
+ * Elements accepted as constructor values for TinyHtml.
3722
+ * These include common DOM elements and root containers.
3723
+ *
3724
+ * @typedef {Window|Element|Document} ConstructorElValues
3725
+ */
3726
+
3727
+ /**
3728
+ * The handler function used in event listeners.
3729
+ *
3730
+ * @typedef {(e: Event) => any} EventRegistryHandle
3731
+ */
3732
+
3733
+ /**
3734
+ * Options passed to `addEventListener` or `removeEventListener`.
3735
+ * Can be a boolean or an object of type `AddEventListenerOptions`.
3736
+ *
3737
+ * @typedef {boolean|AddEventListenerOptions} EventRegistryOptions
3738
+ */
3739
+
3740
+ /**
3741
+ * Structure describing a registered event callback and its options.
3742
+ *
3743
+ * @typedef {Object} EventRegistryItem
3744
+ * @property {EventRegistryHandle} handler - The function to be executed when the event is triggered.
3745
+ * @property {EventRegistryOptions} [options] - Optional configuration passed to the listener.
3746
+ */
3747
+
3748
+ /**
3749
+ * Maps event names (e.g., `"click"`, `"keydown"`) to a list of registered handlers and options.
3750
+ *
3751
+ * @typedef {Record<string, EventRegistryItem[]>} EventRegistryList
3752
+ */
3753
+
3754
+ /**
3755
+ * WeakMap storing all event listeners per element.
3756
+ * Each element has a registry mapping event names to their handler lists.
3757
+ *
3758
+ * @type {WeakMap<ConstructorElValues|EventTarget, EventRegistryList>}
3759
+ */
3760
+ const __eventRegistry = new WeakMap();
3761
+
3762
+ /**
3763
+ * A key-value store associated with a specific DOM element.
3764
+ * Keys are strings, and values can be of any type.
3765
+ *
3766
+ * @typedef {Record<string, *>} ElementDataStore
3767
+ */
3768
+
3769
+ /**
3770
+ * WeakMap to hold private data for elements
3771
+ *
3772
+ * @type {WeakMap<ConstructorElValues, ElementDataStore>}
3773
+ */
3774
+ const __elementDataMap = new WeakMap();
3775
+
3776
+ /**
3777
+ * Stores directional collision locks separately.
3778
+ * Each direction has its own WeakMap to allow independent locking.
3779
+ *
3780
+ * @type {{
3781
+ * top: WeakMap<Element, true>,
3782
+ * bottom: WeakMap<Element, true>,
3783
+ * left: WeakMap<Element, true>,
3784
+ * right: WeakMap<Element, true>
3785
+ * }}
3786
+ */
3787
+ const __elemCollision = {
3788
+ top: new WeakMap(),
3789
+ bottom: new WeakMap(),
3790
+ left: new WeakMap(),
3791
+ right: new WeakMap(),
3792
+ };
3793
+
3794
+ /**
3795
+ * Possible directions from which a collision was detected and locked.
3796
+ *
3797
+ * @typedef {'top'|'bottom'|'left'|'right'} CollisionDirLock
3798
+ */
3799
+
3800
+ /**
3801
+ * @typedef {Object} HtmlElBoxSides
3802
+ * @property {number} x - Total horizontal size (left + right)
3803
+ * @property {number} y - Total vertical size (top + bottom)
3804
+ * @property {number} left
3805
+ * @property {number} right
3806
+ * @property {number} top
3807
+ * @property {number} bottom
3808
+ */
3809
+
3810
+ /**
3811
+ * @typedef {string | number | Date | boolean | null} SetValueBase - Primitive types accepted as input values.
3812
+ */
3813
+
3814
+ /**
3815
+ * @typedef {'string' | 'date' | 'number'} GetValueTypes
3816
+ * Types of value extractors supported by TinyHtml._valTypes.
3817
+ */
3818
+
3819
+ /**
3820
+ * @typedef {SetValueBase|SetValueBase[]} SetValueList - A single value or an array of values to be assigned to the input element.
3821
+ */
3822
+
3823
+ /**
3824
+ * A list of HTML form elements that can have a `.value` property used by TinyHtml.
3825
+ * Includes common input types used in forms.
3826
+ *
3827
+ * @typedef {HTMLInputElement|HTMLSelectElement|HTMLTextAreaElement|HTMLOptionElement} InputElement
3828
+ */
3829
+
3830
+ /**
3831
+ * TinyHtml is a utility class that provides static and instance-level methods
3832
+ * for precise dimension and position computations on HTML elements.
3833
+ * It mimics some jQuery functionalities while using native browser APIs.
3834
+ *
3835
+ * Inspired by the jQuery project's open source implementations of element dimension
3836
+ * and offset utilities. This class serves as a lightweight alternative using modern DOM APIs.
3837
+ *
3838
+ * @class
3839
+ */
3840
+ class TinyHtml_TinyHtml {
3841
+ /** @typedef {import('../basics/collision.mjs').ObjRect} ObjRect */
3842
+
3843
+ static Utils = { ...collision_namespaceObject };
3844
+
3845
+ /**
3846
+ * Queries the document for the first element matching the CSS selector and wraps it in a TinyHtml instance.
3847
+ *
3848
+ * @param {string} selector - A valid CSS selector string.
3849
+ * @returns {TinyHtml} A TinyHtml instance wrapping the matched element.
3850
+ * @throws {Error} If no element matches the selector.
3851
+ */
3852
+ static query(selector) {
3853
+ const newEl = document.querySelector(selector);
3854
+ if (!newEl) throw new Error(`[TinyHtml] query(): No element found for selector "${selector}".`);
3855
+ return new TinyHtml_TinyHtml(newEl);
3856
+ }
3857
+
3858
+ /**
3859
+ * Queries the document for all elements matching the CSS selector and wraps them in TinyHtml instances.
3860
+ *
3861
+ * @param {string} selector - A valid CSS selector string.
3862
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the matched elements.
3863
+ */
3864
+ static queryAll(selector) {
3865
+ const newEls = document.querySelectorAll(selector);
3866
+ return TinyHtml_TinyHtml.toTinyElm([...newEls]);
3867
+ }
3868
+
3869
+ /**
3870
+ * Retrieves an element by its ID and wraps it in a TinyHtml instance.
3871
+ *
3872
+ * @param {string} selector - The ID of the element to retrieve.
3873
+ * @returns {TinyHtml} A TinyHtml instance wrapping the found element.
3874
+ * @throws {Error} If no element is found with the specified ID.
3875
+ */
3876
+ static getById(selector) {
3877
+ const newEl = document.getElementById(selector);
3878
+ if (!newEl) throw new Error(`[TinyHtml] getById(): No element found with ID "${selector}".`);
3879
+ return new TinyHtml_TinyHtml(newEl);
3880
+ }
3881
+
3882
+ /**
3883
+ * Retrieves all elements with the specified class name and wraps them in TinyHtml instances.
3884
+ *
3885
+ * @param {string} selector - The class name to search for.
3886
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
3887
+ */
3888
+ static getByClassName(selector) {
3889
+ const newEls = document.getElementsByClassName(selector);
3890
+ return TinyHtml_TinyHtml.toTinyElm([...newEls]);
3891
+ }
3892
+
3893
+ /**
3894
+ * Retrieves all elements with the specified name attribute and wraps them in TinyHtml instances.
3895
+ *
3896
+ * @param {string} selector - The name attribute to search for.
3897
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
3898
+ */
3899
+ static getByName(selector) {
3900
+ const newEls = document.getElementsByName(selector);
3901
+ return TinyHtml_TinyHtml.toTinyElm([...newEls]);
3902
+ }
3903
+
3904
+ /**
3905
+ * Retrieves all elements with the specified local tag name within the given namespace URI,
3906
+ * and wraps them in TinyHtml instances.
3907
+ *
3908
+ * @param {string} localName - The local name (tag) of the elements to search for.
3909
+ * @param {string|null} [namespaceURI='http://www.w3.org/1999/xhtml'] - The namespace URI to search within.
3910
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
3911
+ */
3912
+ static getByTagNameNS(localName, namespaceURI = 'http://www.w3.org/1999/xhtml') {
3913
+ const newEls = document.getElementsByTagNameNS(namespaceURI, localName);
3914
+ return TinyHtml_TinyHtml.toTinyElm([...newEls]);
3915
+ }
3916
+
3917
+ //////////////////////////////////////////////////////////////////
3918
+
3919
+ /**
3920
+ * Returns the current target held by this instance.
3921
+ *
3922
+ * @returns {ConstructorElValues} - The instance's target element.
3923
+ */
3924
+ get() {
3925
+ return this.#el;
3926
+ }
3927
+
3928
+ /**
3929
+ * Returns the current Element held by this instance.
3930
+ *
3931
+ * @param {string} where - The method name or context calling this.
3932
+ * @returns {ConstructorElValues} - The instance's element.
3933
+ * @readonly
3934
+ */
3935
+ _getElement(where) {
3936
+ if (
3937
+ !(this.#el instanceof Element) &&
3938
+ !(this.#el instanceof Window) &&
3939
+ !(this.#el instanceof Document)
3940
+ )
3941
+ throw new Error(`[TinyHtml] Invalid Element in ${where}().`);
3942
+ return this.#el;
3943
+ }
3944
+
3945
+ //////////////////////////////////////////////////////
3946
+
3947
+ /**
3948
+ * @param {TinyElement|EventTarget|null|(TinyElement|EventTarget|null)[]} elems
3949
+ * @param {string} where
3950
+ * @param {any[]} TheTinyElements
3951
+ * @param {string[]} elemName
3952
+ * @returns {any[]}
3953
+ * @readonly
3954
+ */
3955
+ static _preElemsTemplate(elems, where, TheTinyElements, elemName) {
3956
+ /** @param {(TinyElement|EventTarget|null)[]} item */
3957
+ const checkElement = (item) =>
3958
+ item.map((elem) => {
3959
+ const result = elem instanceof TinyHtml_TinyHtml ? elem._getElement(where) : elem;
3960
+ let allowed = false;
3961
+ for (const TheTinyElement of TheTinyElements) {
3962
+ if (result instanceof TheTinyElement) {
3963
+ allowed = true;
3964
+ break;
3965
+ }
3966
+ }
3967
+ if (!allowed)
3968
+ throw new Error(
3969
+ `[TinyHtml] Invalid element of the list "${elemName.join(',')}" in ${where}().`,
3970
+ );
3971
+ return result;
3972
+ });
3973
+ if (!Array.isArray(elems)) return checkElement([elems]);
3974
+ return checkElement(elems);
3975
+ }
3976
+
3977
+ /**
3978
+ * @param {TinyElement|EventTarget|null|(TinyElement|EventTarget|null)[]} elems
3979
+ * @param {string} where
3980
+ * @param {any[]} TheTinyElements
3981
+ * @param {string[]} elemName
3982
+ * @param {boolean} [canNull=false]
3983
+ * @returns {any}
3984
+ * @readonly
3985
+ */
3986
+ static _preElemTemplate(elems, where, TheTinyElements, elemName, canNull = false) {
3987
+ /** @param {(TinyElement|EventTarget|null)[]} item */
3988
+ const checkElement = (item) => {
3989
+ const elem = item[0];
3990
+ let result = elem instanceof TinyHtml_TinyHtml ? elem._getElement(where) : elem;
3991
+ let allowed = false;
3992
+ for (const TheTinyElement of TheTinyElements) {
3993
+ if (result instanceof TheTinyElement) {
3994
+ allowed = true;
3995
+ break;
3996
+ }
3997
+ }
3998
+
3999
+ if (canNull && (result === null || typeof result === 'undefined')) {
4000
+ result = null;
4001
+ allowed = true;
4002
+ }
4003
+
4004
+ if (!allowed)
4005
+ throw new Error(
4006
+ `[TinyHtml] Invalid element of the list "${elemName.join(',')}" in ${where}().`,
4007
+ );
4008
+ return result;
4009
+ };
4010
+ if (!Array.isArray(elems)) return checkElement([elems]);
4011
+ if (elems.length > 1)
4012
+ throw new Error(
4013
+ `[TinyHtml] Invalid element amount in ${where}() (Received ${elems.length}/1).`,
4014
+ );
4015
+ return checkElement(elems);
4016
+ }
4017
+
4018
+ /**
4019
+ * Ensures the input is returned as an array.
4020
+ * Useful to normalize operations across multiple or single elements.
4021
+ *
4022
+ * @param {TinyElement|TinyElement[]} elems - A single element or array of elements.
4023
+ * @param {string} where - The method or context name where validation is being called.
4024
+ * @returns {Element[]} - Always returns an array of elements.
4025
+ * @readonly
4026
+ */
4027
+ static _preElems(elems, where) {
4028
+ return TinyHtml_TinyHtml._preElemsTemplate(elems, where, [Element], ['Element']);
4029
+ }
4030
+
4031
+ /**
4032
+ * Ensures the input is returned as an single element.
4033
+ * Useful to normalize operations across multiple or single elements.
4034
+ *
4035
+ * @param {TinyElement|TinyElement[]} elems - A single element or array of elements.
4036
+ * @param {string} where - The method or context name where validation is being called.
4037
+ * @returns {Element} - Always returns an single element.
4038
+ * @readonly
4039
+ */
4040
+ static _preElem(elems, where) {
4041
+ return TinyHtml_TinyHtml._preElemTemplate(elems, where, [Element], ['Element']);
4042
+ }
4043
+
4044
+ /**
4045
+ * Ensures the input is returned as an array.
4046
+ * Useful to normalize operations across multiple or single nodes.
4047
+ *
4048
+ * @param {TinyNode|TinyNode[]} elems - A single node or array of nodes.
4049
+ * @param {string} where - The method or context name where validation is being called.
4050
+ * @returns {Node[]} - Always returns an array of nodes.
4051
+ * @readonly
4052
+ */
4053
+ static _preNodeElems(elems, where) {
4054
+ return TinyHtml_TinyHtml._preElemsTemplate(elems, where, [Node], ['Node']);
4055
+ }
4056
+
4057
+ /**
4058
+ * Ensures the input is returned as an single node.
4059
+ * Useful to normalize operations across multiple or single nodes.
4060
+ *
4061
+ * @param {TinyNode|TinyNode[]} elems - A single node or array of nodes.
4062
+ * @param {string} where - The method or context name where validation is being called.
4063
+ * @returns {Node} - Always returns an single node.
4064
+ * @readonly
4065
+ */
4066
+ static _preNodeElem(elems, where) {
4067
+ return TinyHtml_TinyHtml._preElemTemplate(elems, where, [Node], ['Node']);
4068
+ }
4069
+
4070
+ /**
4071
+ * Ensures the input is returned as an single node.
4072
+ * Useful to normalize operations across multiple or single nodes.
4073
+ *
4074
+ * @param {TinyNode|TinyNode[]} elems - A single node or array of nodes.
4075
+ * @param {string} where - The method or context name where validation is being called.
4076
+ * @returns {Node|null} - Always returns an single node or null.
4077
+ * @readonly
4078
+ */
4079
+ static _preNodeElemWithNull(elems, where) {
4080
+ return TinyHtml_TinyHtml._preElemTemplate(elems, where, [Node], ['Node'], true);
4081
+ }
4082
+
4083
+ /**
4084
+ * Ensures the input is returned as an array.
4085
+ * Useful to normalize operations across multiple or single html elements.
4086
+ *
4087
+ * @param {TinyElement|TinyElement[]} elems - A single html element or array of html elements.
4088
+ * @param {string} where - The method or context name where validation is being called.
4089
+ * @returns {HTMLElement[]} - Always returns an array of html elements.
4090
+ * @readonly
4091
+ */
4092
+ static _preHtmlElems(elems, where) {
4093
+ return TinyHtml_TinyHtml._preElemsTemplate(elems, where, [HTMLElement], ['HTMLElement']);
4094
+ }
4095
+
4096
+ /**
4097
+ * Ensures the input is returned as an single html element.
4098
+ * Useful to normalize operations across multiple or single html elements.
4099
+ *
4100
+ * @param {TinyElement|TinyElement[]} elems - A single html element or array of html elements.
4101
+ * @param {string} where - The method or context name where validation is being called.
4102
+ * @returns {HTMLElement} - Always returns an single html element.
4103
+ * @readonly
4104
+ */
4105
+ static _preHtmlElem(elems, where) {
4106
+ return TinyHtml_TinyHtml._preElemTemplate(elems, where, [HTMLElement], ['HTMLElement']);
4107
+ }
4108
+
4109
+ /**
4110
+ * Ensures the input is returned as an array.
4111
+ * Useful to normalize operations across multiple or single event target elements.
4112
+ *
4113
+ * @param {TinyInputElement|TinyInputElement[]} elems - A single event target element or array of html elements.
4114
+ * @param {string} where - The method or context name where validation is being called.
4115
+ * @returns {InputElement[]} - Always returns an array of event target elements.
4116
+ * @readonly
4117
+ */
4118
+ static _preInputElems(elems, where) {
4119
+ return TinyHtml_TinyHtml._preElemsTemplate(
4120
+ elems,
4121
+ where,
4122
+ [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, HTMLOptionElement],
4123
+ ['HTMLInputElement', 'HTMLSelectElement', 'HTMLTextAreaElement', 'HTMLOptionElement'],
4124
+ );
4125
+ }
4126
+
4127
+ /**
4128
+ * Ensures the input is returned as an single event target element.
4129
+ * Useful to normalize operations across multiple or single event target elements.
4130
+ *
4131
+ * @param {TinyInputElement|TinyInputElement[]} elems - A single event target element or array of html elements.
4132
+ * @param {string} where - The method or context name where validation is being called.
4133
+ * @returns {InputElement} - Always returns an single event target element.
4134
+ * @readonly
4135
+ */
4136
+ static _preInputElem(elems, where) {
4137
+ return TinyHtml_TinyHtml._preElemTemplate(
4138
+ elems,
4139
+ where,
4140
+ [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, HTMLOptionElement],
4141
+ ['HTMLInputElement', 'HTMLSelectElement', 'HTMLTextAreaElement', 'HTMLOptionElement'],
4142
+ );
4143
+ }
4144
+
4145
+ /**
4146
+ * Ensures the input is returned as an array.
4147
+ * Useful to normalize operations across multiple or single event target elements.
4148
+ *
4149
+ * @param {TinyEventTarget|TinyEventTarget[]} elems - A single event target element or array of html elements.
4150
+ * @param {string} where - The method or context name where validation is being called.
4151
+ * @returns {EventTarget[]} - Always returns an array of event target elements.
4152
+ * @readonly
4153
+ */
4154
+ static _preEventTargetElems(elems, where) {
4155
+ return TinyHtml_TinyHtml._preElemsTemplate(elems, where, [EventTarget], ['EventTarget']);
4156
+ }
4157
+
4158
+ /**
4159
+ * Ensures the input is returned as an single event target element.
4160
+ * Useful to normalize operations across multiple or single event target elements.
4161
+ *
4162
+ * @param {TinyEventTarget|TinyEventTarget[]} elems - A single event target element or array of html elements.
4163
+ * @param {string} where - The method or context name where validation is being called.
4164
+ * @returns {EventTarget} - Always returns an single event target element.
4165
+ * @readonly
4166
+ */
4167
+ static _preEventTargetElem(elems, where) {
4168
+ return TinyHtml_TinyHtml._preElemTemplate(elems, where, [EventTarget], ['EventTarget']);
4169
+ }
4170
+
4171
+ /**
4172
+ * Ensures the input is returned as an array.
4173
+ * Useful to normalize operations across multiple or single element/window elements.
4174
+ *
4175
+ * @param {TinyElementAndWindow|TinyElementAndWindow[]} elems - A single element/window element or array of html elements.
4176
+ * @param {string} where - The method or context name where validation is being called.
4177
+ * @returns {ElementAndWindow[]} - Always returns an array of element/window elements.
4178
+ * @readonly
4179
+ */
4180
+ static _preElemsAndWindow(elems, where) {
4181
+ return TinyHtml_TinyHtml._preElemsTemplate(elems, where, [Element, Window], ['Element', 'Window']);
4182
+ }
4183
+
4184
+ /**
4185
+ * Ensures the input is returned as an single element/window element.
4186
+ * Useful to normalize operations across multiple or single element/window elements.
4187
+ *
4188
+ * @param {TinyElementAndWindow|TinyElementAndWindow[]} elems - A single element/window element or array of html elements.
4189
+ * @param {string} where - The method or context name where validation is being called.
4190
+ * @returns {ElementAndWindow} - Always returns an single element/window element.
4191
+ * @readonly
4192
+ */
4193
+ static _preElemAndWindow(elems, where) {
4194
+ return TinyHtml_TinyHtml._preElemTemplate(elems, where, [Element, Window], ['Element', 'Window']);
4195
+ }
4196
+
4197
+ /**
4198
+ * Normalizes and converts one or more DOM elements (or TinyHtml instances)
4199
+ * into an array of `TinyHtml` instances.
4200
+ *
4201
+ * - If a plain DOM element is passed, it is wrapped into a `TinyHtml` instance.
4202
+ * - If a `TinyHtml` instance is already passed, it is preserved.
4203
+ * - If an array is passed, all elements inside are converted accordingly.
4204
+ *
4205
+ * This ensures consistent access to methods of the `TinyHtml` class regardless
4206
+ * of the input form.
4207
+ *
4208
+ * @param {TinyElement|TinyElement[]} elems - A single element or an array of elements (DOM or TinyHtml).
4209
+ * @returns {TinyHtml[]} An array of TinyHtml instances corresponding to the input elements.
4210
+ */
4211
+ static toTinyElm(elems) {
4212
+ /** @param {TinyElement[]} item */
4213
+ const checkElement = (item) =>
4214
+ item.map((elem) => (!(elem instanceof TinyHtml_TinyHtml) ? new TinyHtml_TinyHtml(elem) : elem));
4215
+ if (!Array.isArray(elems)) return checkElement([elems]);
4216
+ return checkElement(elems);
4217
+ }
4218
+
4219
+ /**
4220
+ * Extracts native `Element` instances from one or more elements,
4221
+ * which can be either raw DOM elements or wrapped in `TinyHtml`.
4222
+ *
4223
+ * - If a `TinyHtml` instance is passed, its internal DOM element is extracted.
4224
+ * - If a raw DOM element is passed, it is returned as-is.
4225
+ * - If an array is passed, each element is processed accordingly.
4226
+ *
4227
+ * This function guarantees that the return value is always an array of
4228
+ * raw `Element` objects, regardless of whether the input was
4229
+ * a mix of `TinyHtml` or native DOM elements.
4230
+ *
4231
+ * @param {TinyElement|TinyElement[]} elems - A single element or an array of elements (DOM or TinyHtml`).
4232
+ * @returns {Element[]} An array of Element instances extracted from the input.
4233
+ */
4234
+ static fromTinyElm(elems) {
4235
+ /** @param {TinyElement[]} item */
4236
+ const checkElement = (item) =>
4237
+ item.map(
4238
+ (elem) =>
4239
+ /** @type {Element} */ (
4240
+ elem instanceof TinyHtml_TinyHtml ? elem._getElement('fromTinyElm') : elem
4241
+ ),
4242
+ );
4243
+ if (!Array.isArray(elems)) return checkElement([elems]);
4244
+ return checkElement(elems);
4245
+ }
4246
+
4247
+ /**
4248
+ * Filters an array of elements based on a selector, function, element, or array of elements.
4249
+ *
4250
+ * @param {TinyElement|TinyElement[]} elems
4251
+ * @param {WinnowRequest} qualifier
4252
+ * @param {string} where - The context/method name using this validation.
4253
+ * @param {boolean} not Whether to invert the result (used for .not())
4254
+ * @returns {Element[]}
4255
+ */
4256
+ static winnow(elems, qualifier, where, not = false) {
4257
+ if (typeof not !== 'boolean') throw new TypeError('The "not" must be a boolean.');
4258
+ if (typeof qualifier === 'function') {
4259
+ return TinyHtml_TinyHtml._preElems(elems, where).filter(
4260
+ (el, i) => !!qualifier.call(el, i, el) !== not,
4261
+ );
4262
+ }
4263
+
4264
+ if (qualifier instanceof Element) {
4265
+ return TinyHtml_TinyHtml._preElems(elems, where).filter((el) => (el === qualifier) !== not);
4266
+ }
4267
+
4268
+ if (
4269
+ Array.isArray(qualifier) ||
4270
+ (typeof qualifier !== 'string' &&
4271
+ // @ts-ignore
4272
+ qualifier.length != null)
4273
+ ) {
4274
+ return TinyHtml_TinyHtml._preElems(elems, where).filter((el) => qualifier.includes(el) !== not);
4275
+ }
4276
+
4277
+ // Assume it's a selector string
4278
+ let selector = qualifier;
4279
+ if (not) selector = `:not(${selector})`;
4280
+ return TinyHtml_TinyHtml._preElems(elems, where).filter(
4281
+ (el) => el.nodeType === 1 && el.matches(selector),
4282
+ );
4283
+ }
4284
+
4285
+ /**
4286
+ * Filters a set of elements by a CSS selector.
4287
+ *
4288
+ * @param {TinyElement|TinyElement[]} elems
4289
+ * @param {string} selector
4290
+ * @param {boolean} not
4291
+ * @returns {Element[]}
4292
+ */
4293
+ static filter(elems, selector, not = false) {
4294
+ if (not) selector = `:not(${selector})`;
4295
+ return TinyHtml_TinyHtml._preElems(elems, 'filter').filter(
4296
+ (el) => el.nodeType === 1 && el.matches(selector),
4297
+ );
4298
+ }
4299
+
4300
+ /**
4301
+ * Returns only the elements matching the given selector or function.
4302
+ *
4303
+ * @param {TinyElement|TinyElement[]} elems
4304
+ * @param {WinnowRequest} selector
4305
+ * @returns {Element[]}
4306
+ */
4307
+ static filterOnly(elems, selector) {
4308
+ return TinyHtml_TinyHtml.winnow(elems, selector, 'filterOnly', false);
4309
+ }
4310
+
4311
+ /**
4312
+ * Returns only the elements **not** matching the given selector or function.
4313
+ *
4314
+ * @param {TinyElement|TinyElement[]} elems
4315
+ * @param {WinnowRequest} selector
4316
+ * @returns {Element[]}
4317
+ */
4318
+ static not(elems, selector) {
4319
+ return TinyHtml_TinyHtml.winnow(elems, selector, 'not', true);
4320
+ }
4321
+
4322
+ /**
4323
+ * Returns only the elements **not** matching the given selector or function.
4324
+ *
4325
+ * @param {WinnowRequest} selector
4326
+ * @returns {Element[]}
4327
+ */
4328
+ not(selector) {
4329
+ return TinyHtml_TinyHtml.not(this, selector);
4330
+ }
4331
+
4332
+ /**
4333
+ * Finds elements matching a selector within a context.
4334
+ *
4335
+ * @param {TinyElement|TinyElement[]} context
4336
+ * @param {string} selector
4337
+ * @returns {Element[]}
4338
+ */
4339
+ static find(context, selector) {
4340
+ const result = [];
4341
+ for (const el of TinyHtml_TinyHtml._preElems(context, 'find')) {
4342
+ result.push(...el.querySelectorAll(selector));
4343
+ }
4344
+ return [...new Set(result)];
4345
+ }
4346
+
4347
+ /**
4348
+ * Finds elements in your element matching a selector within a context.
4349
+ *
4350
+ * @param {string} selector
4351
+ * @returns {Element[]}
4352
+ */
4353
+ find(selector) {
4354
+ return TinyHtml_TinyHtml.find(this, selector);
4355
+ }
4356
+
4357
+ /**
4358
+ * Checks if at least one element matches the selector.
4359
+ *
4360
+ * @param {TinyElement|TinyElement[]} elems
4361
+ * @param {WinnowRequest} selector
4362
+ * @returns {boolean}
4363
+ */
4364
+ static is(elems, selector) {
4365
+ return TinyHtml_TinyHtml.winnow(elems, selector, 'is', false).length > 0;
4366
+ }
4367
+
4368
+ /**
4369
+ * Checks if the element matches the selector.
4370
+ *
4371
+ * @param {WinnowRequest} selector
4372
+ * @returns {boolean}
4373
+ */
4374
+ is(selector) {
4375
+ return TinyHtml_TinyHtml.is(this, selector);
4376
+ }
4377
+
4378
+ /**
4379
+ * Returns elements from the current list that contain the given target(s).
4380
+ * @param {TinyElement|TinyElement[]} roots - A single element or an array of elements (DOM or TinyHtml).
4381
+ * @param {string|TinyElement|TinyElement[]} target - Selector or DOM element(s).
4382
+ * @returns {Element[]} Elements that contain the target.
4383
+ */
4384
+ static has(roots, target) {
4385
+ const targets =
4386
+ typeof target === 'string'
4387
+ ? [...document.querySelectorAll(target)]
4388
+ : TinyHtml_TinyHtml._preElems(target, 'has');
4389
+
4390
+ return TinyHtml_TinyHtml._preElems(roots, 'has').filter((root) =>
4391
+ targets.some((t) => root && root.contains(t)),
4392
+ );
4393
+ }
4394
+
4395
+ /**
4396
+ * Return if the element has the target(s).
4397
+ * @param {string|TinyElement|TinyElement[]} target - Selector or DOM element(s).
4398
+ * @returns {boolean} Elements that contain the target.
4399
+ */
4400
+ has(target) {
4401
+ return TinyHtml_TinyHtml.has(this, target).length > 0;
4402
+ }
4403
+
4404
+ /**
4405
+ * Finds the closest ancestor (including self) that matches the selector.
4406
+ *
4407
+ * @param {TinyElement|TinyElement[]} els - A single element or an array of elements (DOM or TinyHtml).
4408
+ * @param {string|Element} selector - A selector string or DOM element to match.
4409
+ * @param {Element|null} [context] - An optional context to stop searching.
4410
+ * @returns {Element[]}
4411
+ */
4412
+ static closest(els, selector, context) {
4413
+ const matched = [];
4414
+
4415
+ for (const el of TinyHtml_TinyHtml._preElems(els, 'closest')) {
4416
+ /** @type {Element | null} */
4417
+ let current = el;
4418
+ while (current && current !== context) {
4419
+ if (
4420
+ current.nodeType === 1 &&
4421
+ (typeof selector === 'string' ? current.matches(selector) : current === selector)
4422
+ ) {
4423
+ matched.push(current);
4424
+ break;
4425
+ }
4426
+ current = current.parentElement;
4427
+ }
4428
+ }
4429
+
4430
+ return [...new Set(matched)];
4431
+ }
4432
+
4433
+ /**
4434
+ * Finds the closest ancestor (including self) that matches the selector.
4435
+ *
4436
+ * @param {string|Element} selector - A selector string or DOM element to match.
4437
+ * @param {Element|null} [context] - An optional context to stop searching.
4438
+ * @returns {Element[]}
4439
+ */
4440
+ closest(selector, context) {
4441
+ return TinyHtml_TinyHtml.closest(this, selector, context);
4442
+ }
4443
+
4444
+ /**
4445
+ * Compares two DOM elements to determine if they refer to the same node in the document.
4446
+ *
4447
+ * This performs a strict equality check (`===`) between the two elements.
4448
+ *
4449
+ * @param {TinyNode} elem - The first DOM element to compare.
4450
+ * @param {TinyNode} otherElem - The second DOM element to compare.
4451
+ * @returns {boolean} `true` if both elements are the same DOM node; otherwise, `false`.
4452
+ */
4453
+ static isSameDom(elem, otherElem) {
4454
+ return (
4455
+ TinyHtml_TinyHtml._preNodeElem(elem, 'isSameDom') === TinyHtml_TinyHtml._preNodeElem(otherElem, 'isSameDom')
4456
+ );
4457
+ }
4458
+
4459
+ /**
4460
+ * Compares two DOM elements to determine if they refer to the same node in the document.
4461
+ *
4462
+ * This performs a strict equality check (`===`) between the two elements.
4463
+ *
4464
+ * @param {TinyNode} elem - The DOM element to compare.
4465
+ * @returns {boolean} `true` if both elements are the same DOM node; otherwise, `false`.
4466
+ */
4467
+ isSameDom(elem) {
4468
+ return TinyHtml_TinyHtml.isSameDom(this, elem);
4469
+ }
4470
+
4471
+ //////////////////////////////////////////////////////////////////
4472
+
4473
+ /** @type {ElementDataStore} */
4474
+ _data = {};
4475
+
4476
+ /**
4477
+ * Internal data selectors for accessing public or private data stores.
4478
+ *
4479
+ * @type {Record<string, (where: string, elem: TinyElement) => ElementDataStore>}
4480
+ * @readonly
4481
+ */
4482
+ static _dataSelector = {
4483
+ public: (where, el) => {
4484
+ const elem = TinyHtml_TinyHtml._preElem(el, where);
4485
+ let data = __elementDataMap.get(elem);
4486
+ if (!data) {
4487
+ data = {};
4488
+ __elementDataMap.set(elem, data);
4489
+ }
4490
+ return data;
4491
+ },
4492
+ private: (where, el) => {
4493
+ if (!(el instanceof TinyHtml_TinyHtml))
4494
+ throw new Error(`Element must be a TinyHtml instance to execute ${where}().`);
4495
+ return el._data;
4496
+ },
4497
+ };
4498
+
4499
+ /**
4500
+ * Retrieves data associated with a DOM element.
4501
+ *
4502
+ * If a `key` is provided, the corresponding value is returned.
4503
+ * If no `key` is given, a shallow copy of all stored data is returned.
4504
+ *
4505
+ * @param {TinyElement} el - The DOM element.
4506
+ * @param {string|null} [key] - The specific key to retrieve from the data store.
4507
+ * @param {boolean} [isPrivate=false] - Whether to access the private data store.
4508
+ * @returns {ElementDataStore|undefined|any} - The stored value, all data, or undefined if the key doesn't exist.
4509
+ */
4510
+ static data(el, key, isPrivate = false) {
4511
+ // Get or initialize the data object
4512
+ const data = TinyHtml_TinyHtml._dataSelector[!isPrivate ? 'public' : 'private']('data', el);
4513
+
4514
+ // Getter for all
4515
+ if (key === undefined || key === null) return { ...data };
4516
+
4517
+ // Getter for specific key
4518
+ if (typeof key !== 'string') throw new TypeError('The key must be a string.');
4519
+ return data.hasOwnProperty(key) ? data[key] : undefined;
4520
+ }
4521
+
4522
+ /**
4523
+ * Retrieves data associated with a DOM element.
4524
+ *
4525
+ * If a `key` is provided, the corresponding value is returned.
4526
+ * If no `key` is given, a shallow copy of all stored data is returned.
4527
+ *
4528
+ * @param {string} [key] - The specific key to retrieve from the data store.
4529
+ * @param {boolean} [isPrivate=false] - Whether to access the private data store.
4530
+ * @returns {ElementDataStore|undefined|any} - The stored value, all data, or undefined if the key doesn't exist.
4531
+ */
4532
+ data(key, isPrivate) {
4533
+ return TinyHtml_TinyHtml.data(this, key, isPrivate);
4534
+ }
4535
+
4536
+ /**
4537
+ * Stores a value associated with a specific key for a DOM element.
4538
+ *
4539
+ * @param {TinyElement} el - The DOM element.
4540
+ * @param {string} key - The key under which the data will be stored.
4541
+ * @param {any} value - The value to store.
4542
+ * @param {boolean} [isPrivate=false] - Whether to store the data in the private store.
4543
+ * @returns {void}
4544
+ */
4545
+ static setData(el, key, value, isPrivate = false) {
4546
+ const data = TinyHtml_TinyHtml._dataSelector[!isPrivate ? 'public' : 'private']('setData', el);
4547
+ if (typeof key !== 'string') throw new TypeError('The key must be a string.');
4548
+ data[key] = value;
4549
+ }
4550
+
4551
+ /**
4552
+ * Stores a value associated with a specific key for a DOM element.
4553
+ *
4554
+ * @param {string} key - The key under which the data will be stored.
4555
+ * @param {any} value - The value to store.
4556
+ * @param {boolean} [isPrivate=false] - Whether to store the data in the private store.
4557
+ * @returns {void}
4558
+ */
4559
+ setData(key, value, isPrivate = false) {
4560
+ return TinyHtml_TinyHtml.setData(this, key, value, isPrivate);
4561
+ }
4562
+
4563
+ //////////////////////////////////////////////////////
4564
+
4565
+ /**
4566
+ * Get the sibling element in a given direction.
4567
+ *
4568
+ * @param {TinyNode} el
4569
+ * @param {"previousSibling"|"nextSibling"} direction
4570
+ * @param {string} where
4571
+ * @returns {ChildNode|null}
4572
+ * @readonly
4573
+ */
4574
+ static _getSibling(el, direction, where) {
4575
+ /** @type {Node|null} */
4576
+ let newCurrent = TinyHtml_TinyHtml._preNodeElemWithNull(el, where);
4577
+ while (newCurrent && (newCurrent = newCurrent[direction]) && newCurrent.nodeType !== 1) {}
4578
+ if (!(newCurrent instanceof Node)) return null;
4579
+ return /** @type {ChildNode} */ (newCurrent);
4580
+ }
4581
+
4582
+ /**
4583
+ * Get all sibling elements excluding the given one.
4584
+ *
4585
+ * @param {Node|null} start
4586
+ * @param {Node|null} [exclude]
4587
+ * @returns {ChildNode[]}
4588
+ * @readonly
4589
+ */
4590
+ static _getSiblings(start, exclude) {
4591
+ /** @type {Node|null} */
4592
+ let st = start;
4593
+ const siblings = [];
4594
+ for (; st; st = st.nextSibling) {
4595
+ if (st.nodeType === 1 && st !== exclude) {
4596
+ siblings.push(st);
4597
+ }
4598
+ }
4599
+ return /** @type {ChildNode[]} */ (siblings);
4600
+ }
4601
+
4602
+ /**
4603
+ * Traverse DOM in a direction collecting elements.
4604
+ *
4605
+ * @param {TinyNode} el
4606
+ * @param {"parentNode"|"nextSibling"|"previousSibling"} direction
4607
+ * @param {TinyNode|string} [until]
4608
+ * @param {string} [where='domDir']
4609
+ * @returns {ChildNode[]}
4610
+ */
4611
+ static domDir(el, direction, until, where = 'domDir') {
4612
+ if (typeof direction !== 'string') throw new TypeError('The "direction" must be a string.');
4613
+ let elem = TinyHtml_TinyHtml._preNodeElemWithNull(el, where);
4614
+ const matched = [];
4615
+ // @ts-ignore
4616
+ while (elem && (elem = elem[direction])) {
4617
+ if (elem.nodeType !== 1) continue;
4618
+ if (
4619
+ until &&
4620
+ (typeof until === 'string'
4621
+ ? // @ts-ignore
4622
+ elem.matches(until)
4623
+ : elem === until)
4624
+ )
4625
+ break;
4626
+ matched.push(elem);
4627
+ }
4628
+ return /** @type {ChildNode[]} */ (matched);
4629
+ }
4630
+
4631
+ /**
4632
+ * Returns the direct parent node of the given element, excluding document fragments.
4633
+ *
4634
+ * @param {TinyNode} el - The DOM node to find the parent of.
4635
+ * @returns {ParentNode|null} The parent node or null if not found.
4636
+ */
4637
+ static parent(el) {
4638
+ let elem = TinyHtml_TinyHtml._preNodeElemWithNull(el, 'parent');
4639
+ const parent = elem ? elem.parentNode : null;
4640
+ return parent && parent.nodeType !== 11 ? parent : null;
4641
+ }
4642
+
4643
+ /**
4644
+ * Returns the direct parent node of the given element, excluding document fragments.
4645
+ *
4646
+ * @returns {ParentNode|null} The parent node or null if not found.
4647
+ */
4648
+ parent() {
4649
+ return TinyHtml_TinyHtml.parent(this);
4650
+ }
4651
+
4652
+ /**
4653
+ * Returns all ancestor nodes of the given element, optionally stopping before a specific ancestor.
4654
+ *
4655
+ * @param {TinyNode} el - The DOM node to start from.
4656
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
4657
+ * @returns {ChildNode[]} An array of ancestor nodes.
4658
+ */
4659
+ static parents(el, until) {
4660
+ return TinyHtml_TinyHtml.domDir(el, 'parentNode', until, 'parents');
4661
+ }
4662
+
4663
+ /**
4664
+ * Returns all ancestor nodes of the given element, optionally stopping before a specific ancestor.
4665
+ *
4666
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
4667
+ * @returns {ChildNode[]} An array of ancestor nodes.
4668
+ */
4669
+ parents(until) {
4670
+ return TinyHtml_TinyHtml.parents(this, until);
4671
+ }
4672
+
4673
+ /**
4674
+ * Returns the next sibling of the given element.
4675
+ *
4676
+ * @param {TinyNode} el - The DOM node to start from.
4677
+ * @returns {ChildNode|null} The next sibling or null if none found.
4678
+ */
4679
+ static next(el) {
4680
+ return TinyHtml_TinyHtml._getSibling(el, 'nextSibling', 'next');
4681
+ }
4682
+
4683
+ /**
4684
+ * Returns the next sibling of the given element.
4685
+ *
4686
+ * @returns {ChildNode|null} The next sibling or null if none found.
4687
+ */
4688
+ next() {
4689
+ return TinyHtml_TinyHtml.next(this);
4690
+ }
4691
+
4692
+ /**
4693
+ * Returns the previous sibling of the given element.
4694
+ *
4695
+ * @param {TinyNode} el - The DOM node to start from.
4696
+ * @returns {ChildNode|null} The previous sibling or null if none found.
4697
+ */
4698
+ static prev(el) {
4699
+ return TinyHtml_TinyHtml._getSibling(el, 'previousSibling', 'prev');
4700
+ }
4701
+
4702
+ /**
4703
+ * Returns the previous sibling of the given element.
4704
+ *
4705
+ * @returns {ChildNode|null} The previous sibling or null if none found.
4706
+ */
4707
+ prev() {
4708
+ return TinyHtml_TinyHtml.prev(this);
4709
+ }
4710
+
4711
+ /**
4712
+ * Returns all next sibling nodes after the given element.
4713
+ *
4714
+ * @param {TinyNode} el - The DOM node to start from.
4715
+ * @returns {ChildNode[]} An array of next sibling nodes.
4716
+ */
4717
+ static nextAll(el) {
4718
+ return TinyHtml_TinyHtml.domDir(el, 'nextSibling', undefined, 'nextAll');
4719
+ }
4720
+
4721
+ /**
4722
+ * Returns all next sibling nodes after the given element.
4723
+ *
4724
+ * @returns {ChildNode[]} An array of next sibling nodes.
4725
+ */
4726
+ nextAll() {
4727
+ return TinyHtml_TinyHtml.nextAll(this);
4728
+ }
4729
+
4730
+ /**
4731
+ * Returns all previous sibling nodes before the given element.
4732
+ *
4733
+ * @param {TinyNode} el - The DOM node to start from.
4734
+ * @returns {ChildNode[]} An array of previous sibling nodes.
4735
+ */
4736
+ static prevAll(el) {
4737
+ return TinyHtml_TinyHtml.domDir(el, 'previousSibling', undefined, 'prevAll');
4738
+ }
4739
+
4740
+ /**
4741
+ * Returns all previous sibling nodes before the given element.
4742
+ *
4743
+ * @returns {ChildNode[]} An array of previous sibling nodes.
4744
+ */
4745
+ prevAll() {
4746
+ return TinyHtml_TinyHtml.prevAll(this);
4747
+ }
4748
+
4749
+ /**
4750
+ * Returns all next sibling nodes up to (but not including) the node matched by a selector or element.
4751
+ *
4752
+ * @param {TinyNode} el - The DOM node to start from.
4753
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
4754
+ * @returns {ChildNode[]} An array of next sibling nodes.
4755
+ */
4756
+ static nextUntil(el, until) {
4757
+ return TinyHtml_TinyHtml.domDir(el, 'nextSibling', until, 'nextUtil');
4758
+ }
4759
+
4760
+ /**
4761
+ * Returns all next sibling nodes up to (but not including) the node matched by a selector or element.
4762
+ *
4763
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
4764
+ * @returns {ChildNode[]} An array of next sibling nodes.
4765
+ */
4766
+ nextUntil(until) {
4767
+ return TinyHtml_TinyHtml.nextUntil(this, until);
4768
+ }
4769
+
4770
+ /**
4771
+ * Returns all previous sibling nodes up to (but not including) the node matched by a selector or element.
4772
+ *
4773
+ * @param {TinyNode} el - The DOM node to start from.
4774
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
4775
+ * @returns {ChildNode[]} An array of previous sibling nodes.
4776
+ */
4777
+ static prevUntil(el, until) {
4778
+ return TinyHtml_TinyHtml.domDir(el, 'previousSibling', until, 'prevUtil');
4779
+ }
4780
+
4781
+ /**
4782
+ * Returns all previous sibling nodes up to (but not including) the node matched by a selector or element.
4783
+ *
4784
+ * @param {TinyNode|string} [until] - A node or selector to stop before.
4785
+ * @returns {ChildNode[]} An array of previous sibling nodes.
4786
+ */
4787
+ prevUntil(until) {
4788
+ return TinyHtml_TinyHtml.prevUntil(this, until);
4789
+ }
4790
+
4791
+ /**
4792
+ * Returns all sibling nodes of the given element, excluding itself.
4793
+ *
4794
+ * @param {TinyNode} el - The DOM node to find siblings of.
4795
+ * @returns {ChildNode[]} An array of sibling nodes.
4796
+ */
4797
+ static siblings(el) {
4798
+ const elem = TinyHtml_TinyHtml._preNodeElemWithNull(el, 'siblings');
4799
+ return TinyHtml_TinyHtml._getSiblings(elem && elem.parentNode ? elem.parentNode.firstChild : null, elem);
4800
+ }
4801
+
4802
+ /**
4803
+ * Returns all sibling nodes of the given element, excluding itself.
4804
+ *
4805
+ * @returns {ChildNode[]} An array of sibling nodes.
4806
+ */
4807
+ siblings() {
4808
+ return TinyHtml_TinyHtml.siblings(this);
4809
+ }
4810
+
4811
+ /**
4812
+ * Returns all child nodes of the given element.
4813
+ *
4814
+ * @param {TinyNode} el - The DOM node to get children from.
4815
+ * @returns {ChildNode[]} An array of child nodes.
4816
+ */
4817
+ static children(el) {
4818
+ const elem = TinyHtml_TinyHtml._preNodeElemWithNull(el, 'children');
4819
+ return TinyHtml_TinyHtml._getSiblings(elem ? elem.firstChild : null);
4820
+ }
4821
+
4822
+ /**
4823
+ * Returns all child nodes of the given element.
4824
+ *
4825
+ * @returns {ChildNode[]} An array of child nodes.
4826
+ */
4827
+ children() {
4828
+ return TinyHtml_TinyHtml.children(this);
4829
+ }
4830
+
4831
+ /**
4832
+ * Returns the contents of the given node. For `<template>` it returns its content; for `<iframe>`, the document.
4833
+ *
4834
+ * @param {TinyNode} el - The DOM node to get contents from.
4835
+ * @returns {(ChildNode|DocumentFragment)[]|Document[]} An array of child nodes or the content document of an iframe.
4836
+ */
4837
+ static contents(el) {
4838
+ const elem = TinyHtml_TinyHtml._preNodeElemWithNull(el, 'contents');
4839
+ if (
4840
+ elem instanceof HTMLIFrameElement &&
4841
+ elem.contentDocument != null &&
4842
+ Object.getPrototypeOf(elem.contentDocument)
4843
+ ) {
4844
+ return [elem.contentDocument];
4845
+ }
4846
+
4847
+ if (elem instanceof HTMLTemplateElement) {
4848
+ return Array.from((elem.content || elem).childNodes);
4849
+ }
4850
+
4851
+ if (elem) return Array.from(elem.childNodes);
4852
+ return [];
4853
+ }
4854
+
4855
+ /**
4856
+ * Returns the contents of the given node. For `<template>` it returns its content; for `<iframe>`, the document.
4857
+ *
4858
+ * @returns {(ChildNode|DocumentFragment)[]|Document[]} An array of child nodes or the content document of an iframe.
4859
+ */
4860
+ contents() {
4861
+ return TinyHtml_TinyHtml.contents(this);
4862
+ }
4863
+
4864
+ /**
4865
+ * Clone each element.
4866
+ * @param {TinyNode|TinyNode[]} el
4867
+ * @param {boolean} [deep=true]
4868
+ * @returns {Node[]}
4869
+ */
4870
+ static clone(el, deep = true) {
4871
+ if (typeof deep !== 'boolean') throw new TypeError('The "deep" must be a boolean.');
4872
+ return TinyHtml_TinyHtml._preNodeElems(el, 'clone').map((el) => el.cloneNode(deep));
4873
+ }
4874
+
4875
+ /**
4876
+ * Clone the element.
4877
+ * @param {boolean} [deep=true]
4878
+ * @returns {Node}
4879
+ */
4880
+ clone(deep) {
4881
+ return TinyHtml_TinyHtml.clone(this, deep)[0];
4882
+ }
4883
+
4884
+ /**
4885
+ * Normalize and validate nodes before DOM insertion.
4886
+ * Converts TinyNode-like structures or strings into DOM-compatible nodes.
4887
+ * @type {(where: string, ...nodes: (TinyNode | TinyNode[] | string)[]) => (Node | string)[]}
4888
+ * @readonly
4889
+ */
4890
+ static _appendChecker(where, ...nodes) {
4891
+ const results = [];
4892
+ const nds = [...nodes];
4893
+ for (const index in nds) {
4894
+ if (typeof nds[index] !== 'string') {
4895
+ results.push(TinyHtml_TinyHtml._preNodeElem(nds[index], where));
4896
+ } else results.push(nds[index]);
4897
+ }
4898
+ return results;
4899
+ }
4900
+
4901
+ /**
4902
+ * Appends child elements or strings to the end of the target element(s).
4903
+ *
4904
+ * @param {TinyElement | TinyElement[]} el - The target element(s) to receive children.
4905
+ * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to append.
4906
+ */
4907
+ static append(el, ...children) {
4908
+ const elem = TinyHtml_TinyHtml._preElem(el, 'append');
4909
+ elem.append(...TinyHtml_TinyHtml._appendChecker('append', ...children));
4910
+ }
4911
+
4912
+ /**
4913
+ * Appends child elements or strings to the end of the target element(s).
4914
+ *
4915
+ * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to append.
4916
+ */
4917
+ append(...children) {
4918
+ return TinyHtml_TinyHtml.append(this, ...children);
4919
+ }
4920
+
4921
+ /**
4922
+ * Prepends child elements or strings to the beginning of the target element(s).
4923
+ *
4924
+ * @param {TinyElement | TinyElement[]} el - The target element(s) to receive children.
4925
+ * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to prepend.
4926
+ */
4927
+ static prepend(el, ...children) {
4928
+ const elem = TinyHtml_TinyHtml._preElem(el, 'prepend');
4929
+ elem.prepend(...TinyHtml_TinyHtml._appendChecker('prepend', ...children));
4930
+ }
4931
+
4932
+ /**
4933
+ * Prepends child elements or strings to the beginning of the target element(s).
4934
+ *
4935
+ * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to prepend.
4936
+ */
4937
+ prepend(...children) {
4938
+ return TinyHtml_TinyHtml.prepend(this, ...children);
4939
+ }
4940
+
4941
+ /**
4942
+ * Inserts elements or strings immediately before the target element(s) in the DOM.
4943
+ *
4944
+ * @param {TinyElement | TinyElement[]} el - The target element(s) before which new content is inserted.
4945
+ * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert before the target.
4946
+ */
4947
+ static before(el, ...children) {
4948
+ const elem = TinyHtml_TinyHtml._preElem(el, 'before');
4949
+ elem.before(...TinyHtml_TinyHtml._appendChecker('before', ...children));
4950
+ }
4951
+
4952
+ /**
4953
+ * Inserts elements or strings immediately before the target element(s) in the DOM.
4954
+ *
4955
+ * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert before the target.
4956
+ */
4957
+ before(...children) {
4958
+ return TinyHtml_TinyHtml.before(this, ...children);
4959
+ }
4960
+
4961
+ /**
4962
+ * Inserts elements or strings immediately after the target element(s) in the DOM.
4963
+ *
4964
+ * @param {TinyElement | TinyElement[]} el - The target element(s) after which new content is inserted.
4965
+ * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert after the target.
4966
+ */
4967
+ static after(el, ...children) {
4968
+ const elem = TinyHtml_TinyHtml._preElem(el, 'after');
4969
+ elem.after(...TinyHtml_TinyHtml._appendChecker('after', ...children));
4970
+ }
4971
+
4972
+ /**
4973
+ * Inserts elements or strings immediately after the target element(s) in the DOM.
4974
+ *
4975
+ * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert after the target.
4976
+ */
4977
+ after(...children) {
4978
+ return TinyHtml_TinyHtml.after(this, ...children);
4979
+ }
4980
+
4981
+ /**
4982
+ * Replaces the target element(s) in the DOM with new elements or text.
4983
+ *
4984
+ * @param {TinyElement | TinyElement[]} el - The element(s) to be replaced.
4985
+ * @param {...(TinyNode | TinyNode[] | string)} newNodes - New elements or text to replace the target.
4986
+ */
4987
+ static replaceWith(el, ...newNodes) {
4988
+ const elem = TinyHtml_TinyHtml._preElem(el, 'replaceWith');
4989
+ elem.replaceWith(...TinyHtml_TinyHtml._appendChecker('replaceWith', ...newNodes));
4990
+ }
4991
+
4992
+ /**
4993
+ * Replaces the target element(s) in the DOM with new elements or text.
4994
+ *
4995
+ * @param {...(TinyNode | TinyNode[] | string)} newNodes - New elements or text to replace the target.
4996
+ */
4997
+ replaceWith(...newNodes) {
4998
+ return TinyHtml_TinyHtml.replaceWith(this, ...newNodes);
4999
+ }
5000
+
5001
+ /**
5002
+ * Appends the given element(s) to each target element in sequence.
5003
+ *
5004
+ * @param {TinyNode | TinyNode[]} el - The element(s) to append.
5005
+ * @param {TinyNode | TinyNode[]} targets - Target element(s) where content will be appended.
5006
+ */
5007
+ static appendTo(el, targets) {
5008
+ const elems = TinyHtml_TinyHtml._preNodeElems(el, 'appendTo');
5009
+ const tars = TinyHtml_TinyHtml._preNodeElems(targets, 'appendTo');
5010
+ tars.forEach((target, i) => {
5011
+ elems.forEach((elem) =>
5012
+ target.appendChild(i === tars.length - 1 ? elem : elem.cloneNode(true)),
5013
+ );
5014
+ });
5015
+ }
5016
+
5017
+ /**
5018
+ * Appends the given element(s) to each target element in sequence.
5019
+ *
5020
+ * @param {TinyNode | TinyNode[]} targets - Target element(s) where content will be appended.
5021
+ */
5022
+ appendTo(targets) {
5023
+ return TinyHtml_TinyHtml.appendTo(this, targets);
5024
+ }
5025
+
5026
+ /**
5027
+ * Prepends the given element(s) to each target element in sequence.
5028
+ *
5029
+ * @param {TinyElement | TinyElement[]} el - The element(s) to prepend.
5030
+ * @param {TinyElement | TinyElement[]} targets - Target element(s) where content will be prepended.
5031
+ */
5032
+ static prependTo(el, targets) {
5033
+ const elems = TinyHtml_TinyHtml._preElems(el, 'prependTo');
5034
+ const tars = TinyHtml_TinyHtml._preElems(targets, 'prependTo');
5035
+ tars.forEach((target, i) => {
5036
+ elems
5037
+ .slice()
5038
+ .reverse()
5039
+ .forEach((elem) => target.prepend(i === tars.length - 1 ? elem : elem.cloneNode(true)));
5040
+ });
5041
+ }
5042
+
5043
+ /**
5044
+ * Prepends the given element(s) to each target element in sequence.
5045
+ *
5046
+ * @param {TinyElement | TinyElement[]} targets - Target element(s) where content will be prepended.
5047
+ */
5048
+ prependTo(targets) {
5049
+ return TinyHtml_TinyHtml.prependTo(this, targets);
5050
+ }
5051
+
5052
+ /**
5053
+ * Inserts the element before a child of a given target, or before the target itself.
5054
+ *
5055
+ * @param {TinyNode | TinyNode[]} el - The element(s) to insert.
5056
+ * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5057
+ * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert before, defaults to target.
5058
+ */
5059
+ static insertBefore(el, target, child = null) {
5060
+ const elem = TinyHtml_TinyHtml._preNodeElem(el, 'insertBefore');
5061
+ const targ = TinyHtml_TinyHtml._preNodeElem(target, 'insertBefore');
5062
+ const childNode = TinyHtml_TinyHtml._preNodeElemWithNull(child, 'insertBefore');
5063
+ if (!targ.parentNode) throw new Error('');
5064
+ targ.parentNode.insertBefore(elem, childNode || targ);
5065
+ }
5066
+
5067
+ /**
5068
+ * Inserts the element before a child of a given target, or before the target itself.
5069
+ *
5070
+ * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5071
+ * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert before, defaults to target.
5072
+ */
5073
+ insertBefore(target, child) {
5074
+ return TinyHtml_TinyHtml.insertBefore(this, target, child);
5075
+ }
5076
+
5077
+ /**
5078
+ * Inserts the element after a child of a given target, or after the target itself.
5079
+ *
5080
+ * @param {TinyNode | TinyNode[]} el - The element(s) to insert.
5081
+ * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5082
+ * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert after, defaults to target.
5083
+ */
5084
+ static insertAfter(el, target, child = null) {
5085
+ const elem = TinyHtml_TinyHtml._preNodeElem(el, 'insertAfter');
5086
+ const targ = TinyHtml_TinyHtml._preNodeElem(target, 'insertBefore');
5087
+ const childNode = TinyHtml_TinyHtml._preNodeElemWithNull(child, 'insertBefore');
5088
+ if (!targ.parentNode) throw new Error('');
5089
+ targ.parentNode.insertBefore(elem, childNode || targ.nextSibling);
5090
+ }
5091
+
5092
+ /**
5093
+ * Inserts the element after a child of a given target, or after the target itself.
5094
+ *
5095
+ * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5096
+ * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert after, defaults to target.
5097
+ */
5098
+ insertAfter(target, child) {
5099
+ return TinyHtml_TinyHtml.insertAfter(this, target, child);
5100
+ }
5101
+
5102
+ /**
5103
+ * Replaces all target elements with the provided element(s).
5104
+ * If multiple targets exist, the inserted elements are cloned accordingly.
5105
+ *
5106
+ * @param {TinyNode | TinyNode[]} el - The new element(s) to insert.
5107
+ * @param {TinyNode | TinyNode[]} targets - The elements to be replaced.
5108
+ */
5109
+ static replaceAll(el, targets) {
5110
+ const elems = TinyHtml_TinyHtml._preNodeElems(el, 'replaceAll');
5111
+ const tars = TinyHtml_TinyHtml._preNodeElems(targets, 'replaceAll');
5112
+ tars.forEach((target, i) => {
5113
+ const parent = target.parentNode;
5114
+ elems.forEach((elem) => {
5115
+ if (parent)
5116
+ parent.replaceChild(i === tars.length - 1 ? elem : elem.cloneNode(true), target);
5117
+ });
5118
+ });
5119
+ }
5120
+
5121
+ /**
5122
+ * Replaces all target elements with the provided element(s).
5123
+ * If multiple targets exist, the inserted elements are cloned accordingly.
5124
+ *
5125
+ * @param {TinyNode | TinyNode[]} targets - The elements to be replaced.
5126
+ */
5127
+ replaceAll(targets) {
5128
+ return TinyHtml_TinyHtml.replaceAll(this, targets);
5129
+ }
5130
+
5131
+ //////////////////////////////////////////////////////
5132
+
5133
+ /**
5134
+ * The target HTML element for instance-level operations.
5135
+ * @type {ConstructorElValues}
5136
+ */
5137
+ #el;
5138
+
5139
+ /**
5140
+ * Creates an instance of TinyHtml for a specific Element.
5141
+ * Useful when you want to operate repeatedly on the same element using instance methods.
5142
+ * @param {ConstructorElValues} el - The element to wrap and manipulate.
5143
+ */
5144
+ constructor(el) {
5145
+ if (el instanceof TinyHtml_TinyHtml)
5146
+ throw new Error(
5147
+ `[TinyHtml] You are trying to put a TinyHtml inside another TinyHtml in constructor.`,
5148
+ );
5149
+ if (!(el instanceof Element) && !(el instanceof Window) && !(el instanceof Document))
5150
+ throw new Error(`[TinyHtml] Invalid Target in constructor.`);
5151
+ this.#el = el;
5152
+ }
5153
+
5154
+ /**
5155
+ * Checks whether the given object is a window.
5156
+ * @param {*} obj - The object to test.
5157
+ * @returns {obj is Window} - True if it's a Window.
5158
+ */
5159
+ static isWindow(obj) {
5160
+ return obj != null && obj === obj.window;
5161
+ }
5162
+
5163
+ /////////////////////////////////////////////////////
5164
+
5165
+ /**
5166
+ * Returns the full computed CSS styles for the given element.
5167
+ *
5168
+ * @param {TinyElement} el - The element to retrieve styles from.
5169
+ * @returns {CSSStyleDeclaration} The computed style object for the element.
5170
+ */
5171
+ static css(el) {
5172
+ const elem = TinyHtml_TinyHtml._preElem(el, 'css');
5173
+ return window.getComputedStyle(elem);
5174
+ }
5175
+
5176
+ /**
5177
+ * Returns the full computed CSS styles for the given element.
5178
+ *
5179
+ * @returns {CSSStyleDeclaration} The computed style object for the element.
5180
+ */
5181
+ css() {
5182
+ return TinyHtml_TinyHtml.css(this);
5183
+ }
5184
+
5185
+ /**
5186
+ * Returns the value of a specific computed CSS property from the given element as a string.
5187
+ *
5188
+ * @param {TinyElement} el - The element to retrieve the style property from.
5189
+ * @param {string} prop - The name of the CSS property (camelCase or kebab-case).
5190
+ * @returns {string|null} The value of the CSS property as a string, or null if not found or invalid.
5191
+ */
5192
+ static cssString(el, prop) {
5193
+ const elem = TinyHtml_TinyHtml._preElem(el, 'cssString');
5194
+ if (typeof prop !== 'string') throw new TypeError('The prop must be a string.');
5195
+ // @ts-ignore
5196
+ const val = window.getComputedStyle(elem)[prop];
5197
+ return typeof val === 'string' ? val : typeof val === 'number' ? val.toString() : null;
5198
+ }
5199
+
5200
+ /**
5201
+ * Returns the value of a specific computed CSS property from the given element as a string.
5202
+ *
5203
+ * @param {string} prop - The name of the CSS property (camelCase or kebab-case).
5204
+ * @returns {string|null} The value of the CSS property as a string, or null if not found or invalid.
5205
+ */
5206
+ cssString(prop) {
5207
+ return TinyHtml_TinyHtml.cssString(this, prop);
5208
+ }
5209
+
5210
+ /**
5211
+ * Returns a subset of computed CSS styles based on the given list of properties.
5212
+ *
5213
+ * @param {TinyElement} el - The element to retrieve styles from.
5214
+ * @param {string[]} prop - An array of CSS property names to retrieve.
5215
+ * @returns {Partial<CSSStyleDeclaration>} An object containing the requested styles.
5216
+ */
5217
+ static cssList(el, prop) {
5218
+ const elem = TinyHtml_TinyHtml._preElem(el, 'cssList');
5219
+ if (!Array.isArray(prop)) throw new TypeError('The prop must be an array of strings.');
5220
+
5221
+ const css = window.getComputedStyle(elem);
5222
+ /** @type {Partial<CSSStyleDeclaration>} */
5223
+ const result = {};
5224
+
5225
+ for (const p of prop) {
5226
+ if (typeof p !== 'undefined') {
5227
+ // @ts-ignore
5228
+ result[p] = css.getPropertyValue(p);
5229
+ }
5230
+ }
5231
+
5232
+ return result;
5233
+ }
5234
+
5235
+ /**
5236
+ * Returns a subset of computed CSS styles based on the given list of properties.
5237
+ *
5238
+ * @param {string[]} prop - An array of CSS property names to retrieve.
5239
+ * @returns {Partial<CSSStyleDeclaration>} An object containing the requested styles.
5240
+ */
5241
+ cssList(prop) {
5242
+ return TinyHtml_TinyHtml.cssList(this, prop);
5243
+ }
5244
+
5245
+ /**
5246
+ * Returns the computed CSS float value of a property.
5247
+ * @param {TinyElement} el - The element to inspect.
5248
+ * @param {string} prop - The CSS property.
5249
+ * @returns {number} - The parsed float value.
5250
+ */
5251
+ static cssFloat(el, prop) {
5252
+ const elem = TinyHtml_TinyHtml._preElem(el, 'cssFloat');
5253
+ if (typeof prop !== 'string') throw new TypeError('The prop must be a string.');
5254
+ // @ts-ignore
5255
+ const val = window.getComputedStyle(elem)[prop];
5256
+ return parseFloat(val) || 0;
5257
+ }
5258
+
5259
+ /**
5260
+ * Returns the computed CSS float value of a property.
5261
+ * @param {string} prop - The CSS property.
5262
+ * @returns {number} - The parsed float value.
5263
+ */
5264
+ cssFloat(prop) {
5265
+ return TinyHtml_TinyHtml.cssFloat(this, prop);
5266
+ }
5267
+
5268
+ /**
5269
+ * Returns computed float values of multiple CSS properties.
5270
+ * @param {TinyElement} el - The element to inspect.
5271
+ * @param {string[]} prop - An array of CSS properties.
5272
+ * @returns {Record<string, number>} - Map of property to float value.
5273
+ */
5274
+ static cssFloats(el, prop) {
5275
+ const elem = TinyHtml_TinyHtml._preElem(el, 'cssFloats');
5276
+ if (!Array.isArray(prop)) throw new TypeError('The prop must be an array of strings.');
5277
+ const css = window.getComputedStyle(elem);
5278
+ /** @type {Record<string, number>} */
5279
+ const result = {};
5280
+ for (const name of prop) {
5281
+ // @ts-ignore
5282
+ result[name] = parseFloat(css[name]) || 0;
5283
+ }
5284
+ return result;
5285
+ }
5286
+
5287
+ /**
5288
+ * Returns computed float values of multiple CSS properties.
5289
+ * @param {string[]} prop - An array of CSS properties.
5290
+ * @returns {Record<string, number>} - Map of property to float value.
5291
+ */
5292
+ cssFloats(prop) {
5293
+ return TinyHtml_TinyHtml.cssFloats(this, prop);
5294
+ }
5295
+
5296
+ //////////////////////////////////////////////////////////////////////
5297
+
5298
+ /**
5299
+ * Focus the element.
5300
+ *
5301
+ * @param {TinyHtmlElement} el - The element or a selector string.
5302
+ */
5303
+ static focus(el) {
5304
+ const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'focus');
5305
+ elem.focus();
5306
+ }
5307
+
5308
+ /**
5309
+ * Focus the element.
5310
+ */
5311
+ focus() {
5312
+ return TinyHtml_TinyHtml.focus(this);
5313
+ }
5314
+
5315
+ /**
5316
+ * Blur the element.
5317
+ *
5318
+ * @param {TinyHtmlElement} el - The element or a selector string.
5319
+ */
5320
+ static blur(el) {
5321
+ const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'blur');
5322
+ elem.blur();
5323
+ }
5324
+
5325
+ /**
5326
+ * Blur the element.
5327
+ */
5328
+ blur() {
5329
+ return TinyHtml_TinyHtml.blur(this);
5330
+ }
5331
+
5332
+ //////////////////////////////////////////////////////////////////////
5333
+
5334
+ /**
5335
+ * Sets the vertical scroll position of the window.
5336
+ * @param {number} value - Sets the scroll position.
5337
+ */
5338
+ static setWinScrollTop(value) {
5339
+ if (typeof value !== 'number') throw new TypeError('The value must be a number.');
5340
+ window.scrollTo({ top: value });
5341
+ }
5342
+
5343
+ /**
5344
+ * Sets the horizontal scroll position of the window.
5345
+ * @param {number} value - Sets the scroll position.
5346
+ */
5347
+ static setWinScrollLeft(value) {
5348
+ if (typeof value !== 'number') throw new TypeError('The value must be a number.');
5349
+ window.scrollTo({ left: value });
5350
+ }
5351
+
5352
+ /**
5353
+ * Gets the vertical scroll position of the window.
5354
+ * @returns {number} - The current scroll top value.
5355
+ */
5356
+ static winScrollTop() {
5357
+ return window.scrollY || document.documentElement.scrollTop;
5358
+ }
5359
+
5360
+ /**
5361
+ * Gets the horizontal scroll position of the window.
5362
+ * @returns {number} - The current scroll left value.
5363
+ */
5364
+ static winScrollLeft() {
5365
+ return window.scrollX || document.documentElement.scrollLeft;
5366
+ }
5367
+
5368
+ /**
5369
+ * Returns the current height of the viewport.
5370
+ * @returns {number} - Viewport height in pixels.
5371
+ */
5372
+ static winInnerHeight() {
5373
+ return window.innerHeight || document.documentElement.clientHeight;
5374
+ }
5375
+
5376
+ /**
5377
+ * Returns the current width of the viewport.
5378
+ * @returns {number} - Viewport width in pixels.
5379
+ */
5380
+ static winInnerWidth() {
5381
+ return window.innerWidth || document.documentElement.clientWidth;
5382
+ }
5383
+
5384
+ /**
5385
+ * Gets the width or height of an element based on the box model.
5386
+ * @param {TinyElementAndWindow} el - The element or window.
5387
+ * @param {"width"|"height"} type - Dimension type.
5388
+ * @param {"content"|"padding"|"border"|"margin"} [extra='content'] - Box model context.
5389
+ * @returns {number} - Computed dimension.
5390
+ * @throws {TypeError} If `type` or `extra` is not a string.
5391
+ */
5392
+ static getDimension(el, type, extra = 'content') {
5393
+ const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'getDimension');
5394
+ if (typeof type !== 'string') throw new TypeError('The type must be a string.');
5395
+ if (typeof extra !== 'string') throw new TypeError('The extra must be a string.');
5396
+
5397
+ const name = type === 'width' ? 'Width' : 'Height';
5398
+
5399
+ if (TinyHtml_TinyHtml.isWindow(elem)) {
5400
+ return extra === 'margin'
5401
+ ? // @ts-ignore
5402
+ elem['inner' + name]
5403
+ : // @ts-ignore
5404
+ elem.document.documentElement['client' + name];
5405
+ }
5406
+ /** @type {Element} */
5407
+ const elHtml = elem;
5408
+
5409
+ if (elHtml.nodeType === 9) {
5410
+ // @ts-ignore
5411
+ const doc = elHtml.documentElement;
5412
+ return Math.max(
5413
+ // @ts-ignore
5414
+ elHtml.body['scroll' + name],
5415
+ doc['scroll' + name],
5416
+ // @ts-ignore
5417
+ elHtml.body['offset' + name],
5418
+ doc['offset' + name],
5419
+ doc['client' + name],
5420
+ );
5421
+ }
5422
+
5423
+ let size = elHtml.getBoundingClientRect()[type];
5424
+
5425
+ /**
5426
+ * Auxiliary function to add measures on one side and the other
5427
+ * @param {string} prefix
5428
+ */
5429
+ function sumSides(prefix) {
5430
+ if (type === 'width') {
5431
+ return (
5432
+ TinyHtml_TinyHtml.cssFloat(elHtml, prefix + 'Left') + TinyHtml_TinyHtml.cssFloat(elHtml, prefix + 'Right')
5433
+ );
5434
+ } else {
5435
+ return (
5436
+ TinyHtml_TinyHtml.cssFloat(elHtml, prefix + 'Top') + TinyHtml_TinyHtml.cssFloat(elHtml, prefix + 'Bottom')
5437
+ );
5438
+ }
5439
+ }
5440
+
5441
+ switch (extra) {
5442
+ case 'content':
5443
+ // remove padding + border
5444
+ size -= sumSides('padding');
5445
+ size -= sumSides('border');
5446
+ break;
5447
+
5448
+ case 'padding':
5449
+ // remove border only (padding included in the bounding rect)
5450
+ size -= sumSides('border');
5451
+ break;
5452
+
5453
+ case 'border':
5454
+ // bounding rect already includes border + padding, so do not move the size
5455
+ break;
5456
+
5457
+ case 'margin':
5458
+ // adds margin (margin is out of bounding rect)
5459
+ size += sumSides('margin');
5460
+ break;
5461
+ }
5462
+
5463
+ return size;
5464
+ }
5465
+
5466
+ /**
5467
+ * Gets the width or height of an element based on the box model.
5468
+ * @param {"width"|"height"} type - Dimension type.
5469
+ * @param {"content"|"padding"|"border"|"margin"} extra - Box model context.
5470
+ * @returns {number} - Computed dimension.
5471
+ */
5472
+ getDimension(type, extra) {
5473
+ return TinyHtml_TinyHtml.getDimension(this, type, extra);
5474
+ }
5475
+
5476
+ /**
5477
+ * Sets the height of the element.
5478
+ * @param {TinyHtmlElement} el - Target element.
5479
+ * @param {string|number} value - Height value.
5480
+ * @throws {TypeError} If `value` is neither a string nor number.
5481
+ */
5482
+ static setHeight(el, value) {
5483
+ const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'setHeight');
5484
+ if (typeof value !== 'number' && typeof value !== 'string')
5485
+ throw new TypeError('The value must be a string or number.');
5486
+ elem.style.height = typeof value === 'number' ? `${value}px` : value;
5487
+ }
5488
+
5489
+ /**
5490
+ * Sets the height of the element.
5491
+ * @param {string|number} value - Height value.
5492
+ */
5493
+ setHeight(value) {
5494
+ return TinyHtml_TinyHtml.setHeight(this, value);
5495
+ }
5496
+
5497
+ /**
5498
+ * Sets the width of the element.
5499
+ * @param {TinyHtmlElement} el - Target element.
5500
+ * @param {string|number} value - Width value.
5501
+ * @throws {TypeError} If `value` is neither a string nor number.
5502
+ */
5503
+ static setWidth(el, value) {
5504
+ const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'setWidth');
5505
+ if (typeof value !== 'number' && typeof value !== 'string')
5506
+ throw new TypeError('The value must be a string or number.');
5507
+ elem.style.width = typeof value === 'number' ? `${value}px` : value;
5508
+ }
5509
+
5510
+ /**
5511
+ * Sets the width of the element.
5512
+ * @param {string|number} value - Width value.
5513
+ */
5514
+ setWidth(value) {
5515
+ return TinyHtml_TinyHtml.setWidth(this, value);
5516
+ }
5517
+
5518
+ /**
5519
+ * Returns content box height.
5520
+ * @param {TinyElementAndWindow} el - Target element.
5521
+ * @returns {number}
5522
+ */
5523
+ static height(el) {
5524
+ const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'height');
5525
+ return TinyHtml_TinyHtml.getDimension(elem, 'height', 'content');
5526
+ }
5527
+
5528
+ /**
5529
+ * Returns content box height.
5530
+ * @returns {number}
5531
+ */
5532
+ height() {
5533
+ return TinyHtml_TinyHtml.height(this);
5534
+ }
5535
+
5536
+ /**
5537
+ * Returns content box width.
5538
+ * @param {TinyElementAndWindow} el - Target element.
5539
+ * @returns {number}
5540
+ */
5541
+ static width(el) {
5542
+ const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'width');
5543
+ return TinyHtml_TinyHtml.getDimension(elem, 'width', 'content');
5544
+ }
5545
+
5546
+ /**
5547
+ * Returns content box width.
5548
+ * @returns {number}
5549
+ */
5550
+ width() {
5551
+ return TinyHtml_TinyHtml.width(this);
5552
+ }
5553
+
5554
+ /**
5555
+ * Returns padding box height.
5556
+ * @param {TinyElementAndWindow} el - Target element.
5557
+ * @returns {number}
5558
+ */
5559
+ static innerHeight(el) {
5560
+ const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'innerHeight');
5561
+ return TinyHtml_TinyHtml.getDimension(elem, 'height', 'padding');
5562
+ }
5563
+
5564
+ /**
5565
+ * Returns padding box height.
5566
+ * @returns {number}
5567
+ */
5568
+ innerHeight() {
5569
+ return TinyHtml_TinyHtml.innerHeight(this);
5570
+ }
5571
+
5572
+ /**
5573
+ * Returns padding box width.
5574
+ * @param {TinyElementAndWindow} el - Target element.
5575
+ * @returns {number}
5576
+ */
5577
+ static innerWidth(el) {
5578
+ const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'innerWidth');
5579
+ return TinyHtml_TinyHtml.getDimension(elem, 'width', 'padding');
5580
+ }
5581
+
5582
+ /**
5583
+ * Returns padding box width.
5584
+ * @returns {number}
5585
+ */
5586
+ innerWidth() {
5587
+ return TinyHtml_TinyHtml.innerWidth(this);
5588
+ }
5589
+
5590
+ /**
5591
+ * Returns outer height of the element, optionally including margin.
5592
+ * @param {TinyElementAndWindow} el - Target element.
5593
+ * @param {boolean} [includeMargin=false] - Whether to include margin.
5594
+ * @returns {number}
5595
+ */
5596
+ static outerHeight(el, includeMargin = false) {
5597
+ if (typeof includeMargin !== 'boolean')
5598
+ throw new TypeError('The "includeMargin" must be a boolean.');
5599
+ const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'outerHeight');
5600
+ return TinyHtml_TinyHtml.getDimension(elem, 'height', includeMargin ? 'margin' : 'border');
5601
+ }
5602
+
5603
+ /**
5604
+ * Returns outer height of the element, optionally including margin.
5605
+ * @param {boolean} [includeMargin=false] - Whether to include margin.
5606
+ * @returns {number}
5607
+ */
5608
+ outerHeight(includeMargin) {
5609
+ return TinyHtml_TinyHtml.outerHeight(this, includeMargin);
5610
+ }
5611
+
5612
+ /**
5613
+ * Returns outer width of the element, optionally including margin.
5614
+ * @param {TinyElementAndWindow} el - Target element.
5615
+ * @param {boolean} [includeMargin=false] - Whether to include margin.
5616
+ * @returns {number}
5617
+ */
5618
+ static outerWidth(el, includeMargin = false) {
5619
+ if (typeof includeMargin !== 'boolean')
5620
+ throw new TypeError('The "includeMargin" must be a boolean.');
5621
+ const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'outerWidth');
5622
+ return TinyHtml_TinyHtml.getDimension(elem, 'width', includeMargin ? 'margin' : 'border');
5623
+ }
5624
+
5625
+ /**
5626
+ * Returns outer width of the element, optionally including margin.
5627
+ * @param {boolean} [includeMargin=false] - Whether to include margin.
5628
+ * @returns {number}
5629
+ */
5630
+ outerWidth(includeMargin) {
5631
+ return TinyHtml_TinyHtml.outerWidth(this, includeMargin);
5632
+ }
5633
+
5634
+ //////////////////////////////////////////////////
5635
+
5636
+ /**
5637
+ * Gets the offset of the element relative to the document.
5638
+ * @param {TinyElement} el - Target element.
5639
+ * @returns {{top: number, left: number}}
5640
+ */
5641
+ static offset(el) {
5642
+ const elem = TinyHtml_TinyHtml._preElem(el, 'offset');
5643
+ const rect = elem.getBoundingClientRect();
5644
+ const scrollTop = window.scrollY || document.documentElement.scrollTop;
5645
+ const scrollLeft = window.scrollX || document.documentElement.scrollLeft;
5646
+
5647
+ return {
5648
+ top: rect.top + scrollTop,
5649
+ left: rect.left + scrollLeft,
5650
+ };
5651
+ }
5652
+
5653
+ /**
5654
+ * Gets the offset of the element relative to the document.
5655
+ * @returns {{top: number, left: number}}
5656
+ */
5657
+ offset() {
5658
+ return TinyHtml_TinyHtml.offset(this);
5659
+ }
5660
+
5661
+ /**
5662
+ * Gets the position of the element relative to its offset parent.
5663
+ * @param {TinyHtmlElement} el - Target element.
5664
+ * @returns {{top: number, left: number}}
5665
+ */
5666
+ static position(el) {
5667
+ const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'position');
5668
+
5669
+ let offsetParent;
5670
+ let offset;
5671
+ let parentOffset = { top: 0, left: 0 };
5672
+
5673
+ const computedStyle = window.getComputedStyle(elem);
5674
+
5675
+ if (computedStyle.position === 'fixed') {
5676
+ offset = elem.getBoundingClientRect();
5677
+ } else {
5678
+ offset = TinyHtml_TinyHtml.offset(elem);
5679
+
5680
+ offsetParent = elem.offsetParent || document.documentElement;
5681
+ const { position } = window.getComputedStyle(offsetParent);
5682
+
5683
+ while (
5684
+ offsetParent instanceof HTMLElement &&
5685
+ (offsetParent === document.body || offsetParent === document.documentElement) &&
5686
+ position === 'static'
5687
+ ) {
5688
+ offsetParent = offsetParent.parentNode;
5689
+ }
5690
+
5691
+ if (
5692
+ offsetParent instanceof HTMLElement &&
5693
+ offsetParent !== elem &&
5694
+ offsetParent.nodeType === 1
5695
+ ) {
5696
+ const { borderTopWidth, borderLeftWidth } = TinyHtml_TinyHtml.cssFloats(offsetParent, [
5697
+ 'borderTopWidth',
5698
+ 'borderLeftWidth',
5699
+ ]);
5700
+ parentOffset = TinyHtml_TinyHtml.offset(offsetParent);
5701
+ parentOffset.top += borderTopWidth;
5702
+ parentOffset.left += borderLeftWidth;
5703
+ }
5704
+ }
5705
+
5706
+ return {
5707
+ top: offset.top - parentOffset.top - TinyHtml_TinyHtml.cssFloat(elem, 'marginTop'),
5708
+ left: offset.left - parentOffset.left - TinyHtml_TinyHtml.cssFloat(elem, 'marginLeft'),
5709
+ };
5710
+ }
5711
+
5712
+ /**
5713
+ * Gets the position of the element relative to its offset parent.
5714
+ * @returns {{top: number, left: number}}
5715
+ */
5716
+ position() {
5717
+ return TinyHtml_TinyHtml.position(this);
5718
+ }
5719
+
5720
+ /**
5721
+ * Gets the closest positioned ancestor element.
5722
+ * @param {TinyHtmlElement} el - Target element.
5723
+ * @returns {HTMLElement} - Offset parent element.
5724
+ */
5725
+ static offsetParent(el) {
5726
+ const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'offsetParent');
5727
+ let offsetParent = elem.offsetParent;
5728
+
5729
+ while (
5730
+ offsetParent instanceof HTMLElement &&
5731
+ window.getComputedStyle(offsetParent).position === 'static'
5732
+ ) {
5733
+ offsetParent = offsetParent.offsetParent;
5734
+ }
5735
+
5736
+ // Fallback to document.documentElement
5737
+ return offsetParent instanceof HTMLElement ? offsetParent : document.documentElement;
5738
+ }
5739
+
5740
+ /**
5741
+ * Gets the closest positioned ancestor element.
5742
+ * @returns {HTMLElement} - Offset parent element.
5743
+ */
5744
+ offsetParent() {
5745
+ return TinyHtml_TinyHtml.offsetParent(this);
5746
+ }
5747
+
5748
+ /**
5749
+ * Gets the vertical scroll position.
5750
+ * @param {TinyElementAndWindow} el - Element or window.
5751
+ * @returns {number}
5752
+ */
5753
+ static scrollTop(el) {
5754
+ const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'scrollTop');
5755
+ if (TinyHtml_TinyHtml.isWindow(elem)) return elem.pageYOffset;
5756
+ // @ts-ignore
5757
+ if (elem.nodeType === 9) return elem.defaultView.pageYOffset;
5758
+ return elem.scrollTop;
5759
+ }
5760
+
5761
+ /**
5762
+ * Gets the vertical scroll position.
5763
+ * @returns {number}
5764
+ */
5765
+ scrollTop() {
5766
+ return TinyHtml_TinyHtml.scrollTop(this);
5767
+ }
5768
+
5769
+ /**
5770
+ * Gets the horizontal scroll position.
5771
+ * @param {TinyElementAndWindow} el - Element or window.
5772
+ * @returns {number}
5773
+ */
5774
+ static scrollLeft(el) {
5775
+ const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'scrollLeft');
5776
+ if (TinyHtml_TinyHtml.isWindow(elem)) return elem.pageXOffset;
5777
+ // @ts-ignore
5778
+ if (elem.nodeType === 9) return elem.defaultView.pageXOffset;
5779
+ return elem.scrollLeft;
5780
+ }
5781
+
5782
+ /**
5783
+ * Gets the horizontal scroll position.
5784
+ * @returns {number}
5785
+ */
5786
+ scrollLeft() {
5787
+ return TinyHtml_TinyHtml.scrollLeft(this);
5788
+ }
5789
+
5790
+ /**
5791
+ * Sets the vertical scroll position.
5792
+ * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
5793
+ * @param {number} value - Scroll top value.
5794
+ */
5795
+ static setScrollTop(el, value) {
5796
+ if (typeof value !== 'number') throw new TypeError('ScrollTop value must be a number.');
5797
+ TinyHtml_TinyHtml._preElemsAndWindow(el, 'setScrollTop').forEach((elem) => {
5798
+ if (TinyHtml_TinyHtml.isWindow(elem)) {
5799
+ elem.scrollTo(elem.pageXOffset, value);
5800
+ } else if (elem.nodeType === 9) {
5801
+ // @ts-ignore
5802
+ elem.defaultView.scrollTo(elem.defaultView.pageXOffset, value);
5803
+ } else {
5804
+ elem.scrollTop = value;
5805
+ }
5806
+ });
5807
+ }
5808
+
5809
+ /**
5810
+ * Sets the vertical scroll position.
5811
+ * @param {number} value - Scroll top value.
5812
+ */
5813
+ setScrollTop(value) {
5814
+ return TinyHtml_TinyHtml.setScrollTop(this, value);
5815
+ }
5816
+
5817
+ /**
5818
+ * Sets the horizontal scroll position.
5819
+ * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
5820
+ * @param {number} value - Scroll left value.
5821
+ */
5822
+ static setScrollLeft(el, value) {
5823
+ if (typeof value !== 'number') throw new TypeError('ScrollLeft value must be a number.');
5824
+ TinyHtml_TinyHtml._preElemsAndWindow(el, 'setScrollLeft').forEach((elem) => {
5825
+ if (TinyHtml_TinyHtml.isWindow(elem)) {
5826
+ elem.scrollTo(value, elem.pageYOffset);
5827
+ } else if (elem.nodeType === 9) {
5828
+ // @ts-ignore
5829
+ elem.defaultView.scrollTo(value, elem.defaultView.pageYOffset);
5830
+ } else {
5831
+ elem.scrollLeft = value;
5832
+ }
5833
+ });
5834
+ }
5835
+
5836
+ /**
5837
+ * Sets the horizontal scroll position.
5838
+ * @param {number} value - Scroll left value.
5839
+ */
5840
+ setScrollLeft(value) {
5841
+ return TinyHtml_TinyHtml.setScrollLeft(this, value);
5842
+ }
5843
+
5844
+ /**
5845
+ * Returns the total border width and individual sides from `border{Side}Width` CSS properties.
5846
+ *
5847
+ * @param {TinyElement} el - The target DOM element.
5848
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border widths, and each side individually.
5849
+ */
5850
+ static borderWidth(el) {
5851
+ const elem = TinyHtml_TinyHtml._preElem(el, 'borderWidth');
5852
+ const {
5853
+ borderLeftWidth: left,
5854
+ borderRightWidth: right,
5855
+ borderTopWidth: top,
5856
+ borderBottomWidth: bottom,
5857
+ } = TinyHtml_TinyHtml.cssFloats(elem, [
5858
+ 'borderLeftWidth',
5859
+ 'borderRightWidth',
5860
+ 'borderTopWidth',
5861
+ 'borderBottomWidth',
5862
+ ]);
5863
+ const x = left + right;
5864
+ const y = top + bottom;
5865
+
5866
+ return { x, y, left, right, top, bottom };
5867
+ }
5868
+
5869
+ /**
5870
+ * Returns the total border width and individual sides from `border{Side}Width` CSS properties.
5871
+ *
5872
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border widths, and each side individually.
5873
+ */
5874
+ borderWidth() {
5875
+ return TinyHtml_TinyHtml.borderWidth(this);
5876
+ }
5877
+
5878
+ /**
5879
+ * Returns the total border size and individual sides from `border{Side}` CSS properties.
5880
+ *
5881
+ * @param {TinyElement} el - The target DOM element.
5882
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border sizes, and each side individually.
5883
+ */
5884
+ static border(el) {
5885
+ const elem = TinyHtml_TinyHtml._preElem(el, 'border');
5886
+ const {
5887
+ borderLeft: left,
5888
+ borderRight: right,
5889
+ borderTop: top,
5890
+ borderBottom: bottom,
5891
+ } = TinyHtml_TinyHtml.cssFloats(elem, ['borderLeft', 'borderRight', 'borderTop', 'borderBottom']);
5892
+ const x = left + right;
5893
+ const y = top + bottom;
5894
+
5895
+ return { x, y, left, right, top, bottom };
5896
+ }
5897
+
5898
+ /**
5899
+ * Returns the total border size and individual sides from `border{Side}` CSS properties.
5900
+ *
5901
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border sizes, and each side individually.
5902
+ */
5903
+ border() {
5904
+ return TinyHtml_TinyHtml.border(this);
5905
+ }
5906
+
5907
+ /**
5908
+ * Returns the total margin and individual sides from `margin{Side}` CSS properties.
5909
+ *
5910
+ * @param {TinyElement} el - The target DOM element.
5911
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) margins, and each side individually.
5912
+ */
5913
+ static margin(el) {
5914
+ const elem = TinyHtml_TinyHtml._preElem(el, 'margin');
5915
+ const {
5916
+ marginLeft: left,
5917
+ marginRight: right,
5918
+ marginTop: top,
5919
+ marginBottom: bottom,
5920
+ } = TinyHtml_TinyHtml.cssFloats(elem, ['marginLeft', 'marginRight', 'marginTop', 'marginBottom']);
5921
+ const x = left + right;
5922
+ const y = top + bottom;
5923
+
5924
+ return { x, y, left, right, top, bottom };
5925
+ }
5926
+
5927
+ /**
5928
+ * Returns the total margin and individual sides from `margin{Side}` CSS properties.
5929
+ *
5930
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) margins, and each side individually.
5931
+ */
5932
+ margin() {
5933
+ return TinyHtml_TinyHtml.margin(this);
5934
+ }
5935
+
5936
+ /**
5937
+ * Returns the total padding and individual sides from `padding{Side}` CSS properties.
5938
+ *
5939
+ * @param {TinyElement} el - The target DOM element.
5940
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) paddings, and each side individually.
5941
+ */
5942
+ static padding(el) {
5943
+ const elem = TinyHtml_TinyHtml._preElem(el, 'padding');
5944
+ const {
5945
+ paddingLeft: left,
5946
+ paddingRight: right,
5947
+ paddingTop: top,
5948
+ paddingBottom: bottom,
5949
+ } = TinyHtml_TinyHtml.cssFloats(elem, ['paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom']);
5950
+ const x = left + right;
5951
+ const y = top + bottom;
5952
+
5953
+ return { x, y, left, right, top, bottom };
5954
+ }
5955
+
5956
+ /**
5957
+ * Returns the total padding and individual sides from `padding{Side}` CSS properties.
5958
+ *
5959
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) paddings, and each side individually.
5960
+ */
5961
+ padding() {
5962
+ return TinyHtml_TinyHtml.padding(this);
5963
+ }
5964
+
5965
+ /////////////////////////////////////////////
5966
+
5967
+ /**
5968
+ * Adds one or more CSS class names to the element.
5969
+ * @type {(el: TinyElement|TinyElement[], ...tokens: string[]) => void} - One or more class names to add.
5970
+ */
5971
+ static addClass(el, ...args) {
5972
+ TinyHtml_TinyHtml._preElems(el, 'addClass').forEach((elem) => elem.classList.add(...args));
5973
+ }
5974
+
5975
+ /**
5976
+ * Adds one or more CSS class names to the element.
5977
+ * @type {(...tokens: string[]) => void} - One or more class names to add.
5978
+ */
5979
+ addClass(...args) {
5980
+ return TinyHtml_TinyHtml.addClass(this, ...args);
5981
+ }
5982
+
5983
+ /**
5984
+ * Removes one or more CSS class names from the element.
5985
+ * @type {(el: TinyElement|TinyElement[], ...tokens: string[]) => void} - One or more class names to remove.
5986
+ */
5987
+ static removeClass(el, ...args) {
5988
+ TinyHtml_TinyHtml._preElems(el, 'removeClass').forEach((elem) => elem.classList.remove(...args));
5989
+ }
5990
+
5991
+ /**
5992
+ * Removes one or more CSS class names from the element.
5993
+ * @type {(...tokens: string[]) => void} - One or more class names to remove.
5994
+ */
5995
+ removeClass(...args) {
5996
+ return TinyHtml_TinyHtml.removeClass(this, ...args);
5997
+ }
5998
+
5999
+ /**
6000
+ * Replaces an existing class name with a new one.
6001
+ * @param {TinyElement|TinyElement[]} el - Target element.
6002
+ * @param {string} token - The class name to be replaced.
6003
+ * @param {string} newToken - The new class name to apply.
6004
+ * @returns {boolean[]} Whether the replacement was successful.
6005
+ * @throws {TypeError} If either argument is not a string.
6006
+ */
6007
+ static replaceClass(el, token, newToken) {
6008
+ if (typeof token !== 'string') throw new TypeError('The "token" parameter must be a string.');
6009
+ if (typeof newToken !== 'string')
6010
+ throw new TypeError('The "newToken" parameter must be a string.');
6011
+ /** @type {boolean[]} */
6012
+ const result = [];
6013
+ TinyHtml_TinyHtml._preElems(el, 'replaceClass').forEach((elem) =>
6014
+ result.push(elem.classList.replace(token, newToken)),
6015
+ );
6016
+ return result;
6017
+ }
6018
+
6019
+ /**
6020
+ * Replaces an existing class name with a new one.
6021
+ * @param {string} token - The class name to be replaced.
6022
+ * @param {string} newToken - The new class name to apply.
6023
+ * @returns {boolean} Whether the replacement was successful.
6024
+ * @throws {TypeError} If either argument is not a string.
6025
+ */
6026
+ replaceClass(token, newToken) {
6027
+ return TinyHtml_TinyHtml.replaceClass(this, token, newToken)[0];
6028
+ }
6029
+
6030
+ /**
6031
+ * Returns the class name at the specified index.
6032
+ * @param {TinyElement} el - Target element.
6033
+ * @param {number} index - The index of the class name.
6034
+ * @returns {string|null} The class name at the index or null if not found.
6035
+ * @throws {TypeError} If the index is not a number.
6036
+ */
6037
+ static classItem(el, index) {
6038
+ const elem = TinyHtml_TinyHtml._preElem(el, 'classItem');
6039
+ if (typeof index !== 'number') throw new TypeError('The "index" parameter must be a number.');
6040
+ return elem.classList.item(index);
6041
+ }
6042
+
6043
+ /**
6044
+ * Returns the class name at the specified index.
6045
+ * @param {number} index - The index of the class name.
6046
+ * @returns {string|null} The class name at the index or null if not found.
6047
+ * @throws {TypeError} If the index is not a number.
6048
+ */
6049
+ classItem(index) {
6050
+ return TinyHtml_TinyHtml.classItem(this, index);
6051
+ }
6052
+
6053
+ /**
6054
+ * Toggles a class name on the element with an optional force boolean.
6055
+ * @param {TinyElement|TinyElement[]} el - Target element.
6056
+ * @param {string} token - The class name to toggle.
6057
+ * @param {boolean} [force] - If true, adds the class; if false, removes it.
6058
+ * @returns {boolean[]} Whether the class is present after the toggle.
6059
+ * @throws {TypeError} If token is not a string or force is not a boolean.
6060
+ */
6061
+ static toggleClass(el, token, force) {
6062
+ if (typeof token !== 'string') throw new TypeError('The "token" parameter must be a string.');
6063
+ if (typeof force !== 'undefined' && typeof force !== 'boolean')
6064
+ throw new TypeError('The "force" parameter must be a boolean.');
6065
+ /** @type {boolean[]} */
6066
+ const result = [];
6067
+ TinyHtml_TinyHtml._preElems(el, 'toggleClass').forEach((elem) =>
6068
+ result.push(elem.classList.toggle(token, force)),
6069
+ );
6070
+ return result;
6071
+ }
6072
+
6073
+ /**
6074
+ * Toggles a class name on the element with an optional force boolean.
6075
+ * @param {string} token - The class name to toggle.
6076
+ * @param {boolean} force - If true, adds the class; if false, removes it.
6077
+ * @returns {boolean} Whether the class is present after the toggle.
6078
+ * @throws {TypeError} If token is not a string or force is not a boolean.
6079
+ */
6080
+ toggleClass(token, force) {
6081
+ return TinyHtml_TinyHtml.toggleClass(this, token, force)[0];
6082
+ }
6083
+
6084
+ /**
6085
+ * Checks if the element contains the given class name.
6086
+ * @param {TinyElement} el - Target element.
6087
+ * @param {string} token - The class name to check.
6088
+ * @returns {boolean} True if the class is present, false otherwise.
6089
+ * @throws {TypeError} If token is not a string.
6090
+ */
6091
+ static hasClass(el, token) {
6092
+ const elem = TinyHtml_TinyHtml._preElem(el, 'hasClass');
6093
+ if (typeof token !== 'string') throw new TypeError('The "token" parameter must be a string.');
6094
+ return elem.classList.contains(token);
6095
+ }
6096
+
6097
+ /**
6098
+ * Checks if the element contains the given class name.
6099
+ * @param {string} token - The class name to check.
6100
+ * @returns {boolean} True if the class is present, false otherwise.
6101
+ * @throws {TypeError} If token is not a string.
6102
+ */
6103
+ hasClass(token) {
6104
+ return TinyHtml_TinyHtml.hasClass(this, token);
6105
+ }
6106
+
6107
+ /**
6108
+ * Returns the number of classes applied to the element.
6109
+ * @param {TinyElement} el - Target element.
6110
+ * @returns {number} The number of classes.
6111
+ */
6112
+ static classLength(el) {
6113
+ const elem = TinyHtml_TinyHtml._preElem(el, 'classLength');
6114
+ return elem.classList.length;
6115
+ }
6116
+
6117
+ /**
6118
+ * Returns the number of classes applied to the element.
6119
+ * @returns {number} The number of classes.
6120
+ */
6121
+ classLength() {
6122
+ return TinyHtml_TinyHtml.classLength(this);
6123
+ }
6124
+
6125
+ /**
6126
+ * Returns all class names as an array of strings.
6127
+ * @param {TinyElement} el - Target element.
6128
+ * @returns {string[]} An array of class names.
6129
+ */
6130
+ static classList(el) {
6131
+ const elem = TinyHtml_TinyHtml._preElem(el, 'classList');
6132
+ return elem.classList.values().toArray();
6133
+ }
6134
+
6135
+ /**
6136
+ * Returns all class names as an array of strings.
6137
+ * @returns {string[]} An array of class names.
6138
+ */
6139
+ classList() {
6140
+ return TinyHtml_TinyHtml.classList(this);
6141
+ }
6142
+
6143
+ /////////////////////////////////////////
6144
+
6145
+ /**
6146
+ * Returns the tag name of the element.
6147
+ * @param {TinyElement} el - Target element.
6148
+ * @returns {string} The tag name in uppercase.
6149
+ */
6150
+ static tagName(el) {
6151
+ const elem = TinyHtml_TinyHtml._preElem(el, 'tagName');
6152
+ return elem.tagName;
6153
+ }
6154
+
6155
+ /**
6156
+ * Returns the tag name of the element.
6157
+ * @returns {string} The tag name in uppercase.
6158
+ */
6159
+ tagName() {
6160
+ return TinyHtml_TinyHtml.tagName(this);
6161
+ }
6162
+
6163
+ /**
6164
+ * Returns the ID of the element.
6165
+ * @param {TinyElement} el - Target element.
6166
+ * @returns {string} The element's ID.
6167
+ */
6168
+ static id(el) {
6169
+ const elem = TinyHtml_TinyHtml._preElem(el, 'id');
6170
+ return elem.id;
6171
+ }
6172
+
6173
+ /**
6174
+ * Returns the ID of the element.
6175
+ * @returns {string} The element's ID.
6176
+ */
6177
+ id() {
6178
+ return TinyHtml_TinyHtml.id(this);
6179
+ }
6180
+
6181
+ /**
6182
+ * Returns the text content of the element.
6183
+ * @param {TinyElement} el - Target element.
6184
+ * @returns {string|null} The text content or null if none.
6185
+ */
6186
+ static text(el) {
6187
+ const elem = TinyHtml_TinyHtml._preElem(el, 'text');
6188
+ return elem.textContent;
6189
+ }
6190
+
6191
+ /**
6192
+ * Returns the text content of the element.
6193
+ * @returns {string|null} The text content or null if none.
6194
+ */
6195
+ text() {
6196
+ return TinyHtml_TinyHtml.text(this);
6197
+ }
6198
+
6199
+ /**
6200
+ * Set text content of elements.
6201
+ * @param {TinyElement|TinyElement[]} el
6202
+ * @param {string} value
6203
+ */
6204
+ static setText(el, value) {
6205
+ if (typeof value !== 'string') throw new Error('Value is not a valid string.');
6206
+ TinyHtml_TinyHtml._preElems(el, 'setText').forEach((el) => (el.textContent = value));
6207
+ }
6208
+
6209
+ /**
6210
+ * Set text content of the element.
6211
+ * @param {string} value
6212
+ */
6213
+ setText(value) {
6214
+ return TinyHtml_TinyHtml.setText(this, value);
6215
+ }
6216
+
6217
+ /**
6218
+ * Remove all child nodes from each element.
6219
+ * @param {TinyElement|TinyElement[]} el
6220
+ */
6221
+ static empty(el) {
6222
+ TinyHtml_TinyHtml._preElems(el, 'empty').forEach((el) => (el.textContent = ''));
6223
+ }
6224
+
6225
+ /**
6226
+ * Remove all child nodes of the element.
6227
+ */
6228
+ empty() {
6229
+ return TinyHtml_TinyHtml.empty(this);
6230
+ }
6231
+
6232
+ /**
6233
+ * Get the innerHTML of the element.
6234
+ * @param {TinyElement|TinyElement[]} el
6235
+ * @returns {string}
6236
+ */
6237
+ static html(el) {
6238
+ const elem = TinyHtml_TinyHtml._preElem(el, 'html');
6239
+ return elem.innerHTML;
6240
+ }
6241
+
6242
+ /**
6243
+ * Get the innerHTML of the element.
6244
+ * @returns {string}
6245
+ */
6246
+ html() {
6247
+ return TinyHtml_TinyHtml.html(this);
6248
+ }
6249
+
6250
+ /**
6251
+ * Set the innerHTML of each element.
6252
+ * @param {TinyElement|TinyElement[]} el
6253
+ * @param {string} value
6254
+ */
6255
+ static setHtml(el, value) {
6256
+ if (typeof value !== 'string') throw new Error('Value is not a valid string.');
6257
+ TinyHtml_TinyHtml._preElems(el, 'setHtml').forEach((el) => (el.innerHTML = value));
6258
+ }
6259
+
6260
+ /**
6261
+ * Set the innerHTML of the element.
6262
+ * @param {string} value
6263
+ */
6264
+ setHtml(value) {
6265
+ return TinyHtml_TinyHtml.setHtml(this, value);
6266
+ }
6267
+
6268
+ /** @readonly */
6269
+ static _valHooks = {
6270
+ option: {
6271
+ /**
6272
+ * @param {HTMLOptionElement} elem
6273
+ * @returns {string|null}
6274
+ */
6275
+ get: (elem) => {
6276
+ const val = elem.getAttribute('value');
6277
+ return val != null ? val : elem.textContent;
6278
+ },
6279
+ },
6280
+
6281
+ select: {
6282
+ /**
6283
+ * @param {HTMLSelectElement} elem
6284
+ * @returns {(string | null)[] | string | null}
6285
+ */
6286
+ get: (elem) => {
6287
+ const options = elem.options;
6288
+ const index = elem.selectedIndex;
6289
+ const isSingle = elem.type === 'select-one';
6290
+ const max = isSingle ? index + 1 : options.length;
6291
+
6292
+ /** @type {(string | null)[] | null} */
6293
+ const values = [];
6294
+ let i = index < 0 ? max : isSingle ? index : 0;
6295
+
6296
+ for (; i < max; i++) {
6297
+ const option = options[i];
6298
+ /** @type {HTMLSelectElement|null} */
6299
+ // @ts-ignore
6300
+ const parentNode = option.parentNode;
6301
+ if (
6302
+ (option.selected || i === index) &&
6303
+ !option.disabled &&
6304
+ (!parentNode || !parentNode.disabled || parentNode.tagName !== 'OPTGROUP')
6305
+ ) {
6306
+ const val = TinyHtml_TinyHtml._valHooks.option.get(option);
6307
+ if (isSingle) return val;
6308
+ values.push(val);
6309
+ }
6310
+ }
6311
+
6312
+ return values;
6313
+ },
6314
+
6315
+ /**
6316
+ * @param {HTMLSelectElement} elem
6317
+ * @param {string[]|string} value
6318
+ */
6319
+ set: (elem, value) => {
6320
+ const options = elem.options;
6321
+ const values = Array.isArray(value) ? value.map(String) : [String(value)];
6322
+ let optionSet = false;
6323
+
6324
+ for (let i = 0; i < options.length; i++) {
6325
+ const option = options[i];
6326
+ const optionVal = TinyHtml_TinyHtml._valHooks.option.get(option);
6327
+ if (typeof optionVal === 'string' && (option.selected = values.includes(optionVal))) {
6328
+ optionSet = true;
6329
+ }
6330
+ }
6331
+
6332
+ if (!optionSet) {
6333
+ elem.selectedIndex = -1;
6334
+ }
6335
+
6336
+ return values;
6337
+ },
6338
+ },
6339
+
6340
+ radio: {
6341
+ /**
6342
+ * @param {HTMLInputElement} elem
6343
+ * @returns {string}
6344
+ */
6345
+ get(elem) {
6346
+ return elem.checked ? 'on' : 'off';
6347
+ },
6348
+ /**
6349
+ * @param {HTMLInputElement} elem
6350
+ * @param {string[]} value
6351
+ */
6352
+ set(elem, value) {
6353
+ if (typeof value === 'boolean') {
6354
+ const label = elem.closest('label');
6355
+ if (value && label) {
6356
+ const otherRadios = label.querySelectorAll('input[type="radio"]');
6357
+ otherRadios.forEach((otherRadio) => {
6358
+ if (otherRadio instanceof HTMLInputElement && otherRadio !== elem)
6359
+ otherRadio.checked = false;
6360
+ });
6361
+ }
6362
+ elem.checked = value;
6363
+ return value;
6364
+ }
6365
+ },
6366
+ },
6367
+
6368
+ checkbox: {
6369
+ /**
6370
+ * @param {HTMLInputElement} elem
6371
+ * @returns {string}
6372
+ */
6373
+ get(elem) {
6374
+ return elem.checked ? 'on' : 'off';
6375
+ },
6376
+ /**
6377
+ * @param {HTMLInputElement} elem
6378
+ * @param {boolean} value
6379
+ */
6380
+ set(elem, value) {
6381
+ if (typeof value === 'boolean') {
6382
+ elem.checked = value;
6383
+ return value;
6384
+ }
6385
+ },
6386
+ },
6387
+ };
6388
+
6389
+ /**
6390
+ * Sets the value of the current HTML value element (input, select, textarea, etc.).
6391
+ * Accepts strings, numbers, booleans or arrays of these values, or a callback function that computes them.
6392
+ *
6393
+ * @param {TinyInputElement|TinyInputElement[]} el - Target element.
6394
+ * @param {SetValueList|((el: InputElement, val: SetValueList) => SetValueList)} value - The value to assign or a function that returns it.
6395
+ * @throws {Error} If the computed value is not a valid string or boolean.
6396
+ */
6397
+ static setVal(el, value) {
6398
+ TinyHtml_TinyHtml._preInputElems(el, 'setVal').forEach((elem) => {
6399
+ /**
6400
+ * @param {SetValueBase[]} array
6401
+ * @param {(v: SetValueBase, i: number) => SetValueBase} callback
6402
+ */
6403
+ const mapArray = (array, callback) => {
6404
+ const result = [];
6405
+ for (let i = 0; i < array.length; i++) {
6406
+ result.push(callback(array[i], i));
6407
+ }
6408
+ return result;
6409
+ };
6410
+
6411
+ if (elem.nodeType !== 1) return;
6412
+ /** @type {SetValueList} */
6413
+ let valToSet = typeof value === 'function' ? value(elem, TinyHtml_TinyHtml.val(elem)) : value;
6414
+
6415
+ if (valToSet == null) {
6416
+ valToSet = '';
6417
+ } else if (typeof valToSet === 'number') {
6418
+ valToSet = String(valToSet);
6419
+ } else if (Array.isArray(valToSet)) {
6420
+ valToSet = mapArray(valToSet, (v) => (v == null ? '' : String(v)));
6421
+ }
6422
+
6423
+ // @ts-ignore
6424
+ const hook = TinyHtml_TinyHtml._valHooks[elem.type] || TinyHtml_TinyHtml._valHooks[elem.nodeName.toLowerCase()];
6425
+ if (
6426
+ !hook ||
6427
+ typeof hook.set !== 'function' ||
6428
+ hook.set(elem, valToSet, 'value') === undefined
6429
+ ) {
6430
+ if (typeof valToSet !== 'string' && typeof valToSet !== 'boolean')
6431
+ throw new Error(`Invalid setValue "${typeof valToSet}" value.`);
6432
+ if (typeof valToSet === 'string') elem.value = valToSet;
6433
+ }
6434
+ });
6435
+ }
6436
+
6437
+ /**
6438
+ * Sets the value of the current HTML value element (input, select, textarea, etc.).
6439
+ * Accepts strings, numbers, booleans or arrays of these values, or a callback function that computes them.
6440
+ *
6441
+ * @param {SetValueList|((el: InputElement, val: SetValueList) => SetValueList)} value - The value to assign or a function that returns it.
6442
+ * @throws {Error} If the computed value is not a valid string or boolean.
6443
+ */
6444
+ setVal(value) {
6445
+ return TinyHtml_TinyHtml.setVal(this, value);
6446
+ }
6447
+
6448
+ /**
6449
+ * Maps value types to their corresponding getter functions.
6450
+ * Each function extracts a value of a specific type from a compatible HTMLInputElement.
6451
+ * @readonly
6452
+ */
6453
+ static _valTypes = {
6454
+ /**
6455
+ * Gets the string value from any HTMLInputElement.
6456
+ * @type {(elem: HTMLInputElement) => string}
6457
+ */
6458
+ string: (elem) => elem.value,
6459
+
6460
+ /**
6461
+ * Gets the value as a Date object from supported input types.
6462
+ * Valid only for types: "date", "datetime-local", "month", "time", "week".
6463
+ * Returns `null` if the field is empty or invalid.
6464
+ * @type {(elem: HTMLInputElement & { type: "date" | "datetime-local" | "month" | "time" | "week" }) => Date | null}
6465
+ */
6466
+ date: (elem) => elem.valueAsDate,
6467
+
6468
+ /**
6469
+ * Gets the numeric value from supported input types.
6470
+ * Valid for types: "number", "range", "date", "time".
6471
+ * Returns `NaN` if the value is invalid or empty.
6472
+ * @type {(elem: HTMLInputElement & { type: "number" | "range" | "date" | "time" }) => number}
6473
+ */
6474
+ number: (elem) => elem.valueAsNumber,
6475
+ };
6476
+
6477
+ /**
6478
+ * Gets the value of an input element according to the specified type.
6479
+ *
6480
+ * @param {InputElement} elem - The input element to extract the value from.
6481
+ * @param {GetValueTypes} type - The type of value to retrieve ("string", "date", or "number").
6482
+ * @param {string} where - The context/method name using this validation.
6483
+ * @returns {any} The extracted value, depending on the type.
6484
+ * @throws {Error} If the element is not an HTMLInputElement or if the type handler is invalid.
6485
+ * @readonly
6486
+ */
6487
+ static _getValByType(elem, type, where) {
6488
+ if (typeof type !== 'string') throw new TypeError('The "type" must be a string.');
6489
+ if (typeof where !== 'string') throw new TypeError('The "where" must be a string.');
6490
+ if (!(elem instanceof HTMLInputElement))
6491
+ throw new Error(`Provided element is not an HTMLInputElement in ${where}().`);
6492
+ if (typeof TinyHtml_TinyHtml._valTypes[type] !== 'function')
6493
+ throw new Error(`No handler found for type "${type}" in ${where}().`);
6494
+ // @ts-ignore
6495
+ return TinyHtml_TinyHtml._valTypes[type](elem);
6496
+ }
6497
+
6498
+ /**
6499
+ * Retrieves the raw value from the HTML input element.
6500
+ * If a custom value hook exists, it will be used first.
6501
+ *
6502
+ * @param {TinyInputElement} el - Target element.
6503
+ * @param {GetValueTypes} type - The type of value to retrieve ("string", "date", or "number").
6504
+ * @param {string} where - The context/method name using this validation.
6505
+ * @returns {any} The raw value retrieved from the element or hook.
6506
+ * @readonly
6507
+ */
6508
+ static _val(el, where, type) {
6509
+ const elem = TinyHtml_TinyHtml._preInputElem(el, where);
6510
+ // @ts-ignore
6511
+ const hook = TinyHtml_TinyHtml._valHooks[elem.type] || TinyHtml_TinyHtml._valHooks[elem.nodeName.toLowerCase()];
6512
+ if (hook && typeof hook.get === 'function') {
6513
+ const ret = hook.get(elem, 'value', type);
6514
+ if (ret !== undefined) return typeof ret === 'string' ? ret.replace(/\r/g, '') : ret;
6515
+ }
6516
+
6517
+ return TinyHtml_TinyHtml._getValByType(elem, type, where);
6518
+ }
6519
+
6520
+ /**
6521
+ * Retrieves the raw value from the HTML input element.
6522
+ * If a custom value hook exists, it will be used first.
6523
+ *
6524
+ * @param {GetValueTypes} type - The type of value to retrieve ("string", "date", or "number").
6525
+ * @param {string} where - The context/method name using this validation.
6526
+ * @returns {any} The raw value retrieved from the element or hook.
6527
+ * @readonly
6528
+ */
6529
+ _val(where, type) {
6530
+ return TinyHtml_TinyHtml._val(this, where, type);
6531
+ }
6532
+
6533
+ /**
6534
+ * Gets the value of the current HTML value element.
6535
+ *
6536
+ * @param {TinyInputElement} el - Target element.
6537
+ * @returns {SetValueList} The normalized value, with carriage returns removed.
6538
+ */
6539
+ static val(el) {
6540
+ return /** @type {SetValueList} */ (TinyHtml_TinyHtml._val(el, 'val', 'string'));
6541
+ }
6542
+
6543
+ /**
6544
+ * Gets the value of the current HTML value element.
6545
+ *
6546
+ * @returns {SetValueList} The normalized value, with carriage returns removed.
6547
+ */
6548
+ val() {
6549
+ return TinyHtml_TinyHtml.val(this);
6550
+ }
6551
+
6552
+ /**
6553
+ * Gets the text of the current HTML value element (for text).
6554
+ *
6555
+ * @param {TinyInputElement} el - Target element.
6556
+ * @returns {string} The text value.
6557
+ * @throws {Error} If the element is not a string value.
6558
+ */
6559
+ static valTxt(el) {
6560
+ /** @type {string} */
6561
+ const ret = TinyHtml_TinyHtml._val(el, 'valTxt', 'string');
6562
+ if (typeof ret !== 'string' && ret !== null) throw new Error('Value is not a valid string.');
6563
+ return ret == null ? '' : typeof ret === 'string' ? ret.replace(/\r/g, '') : ret;
6564
+ }
6565
+
6566
+ /**
6567
+ * Gets the text of the current HTML value element (for text).
6568
+ *
6569
+ * @returns {string} The text value.
6570
+ * @throws {Error} If the element is not a string value.
6571
+ */
6572
+ valTxt() {
6573
+ return TinyHtml_TinyHtml.valTxt(this);
6574
+ }
6575
+
6576
+ /**
6577
+ * Internal helper to get a value from an input expected to return an array.
6578
+ *
6579
+ * @param {TinyInputElement} el - Target element.
6580
+ * @param {string} where - The method name or context using this validation (for error reporting).
6581
+ * @param {GetValueTypes} type - The type of value to retrieve ("string", "date", or "number").
6582
+ * @returns {SetValueBase[]} - The validated value as an array.
6583
+ * @throws {Error} If the returned value is not an array.
6584
+ * @readonly
6585
+ */
6586
+ static _valArr(el, where, type) {
6587
+ /** @type {SetValueBase[]} */
6588
+ const ret = TinyHtml_TinyHtml._val(el, where, type);
6589
+ if (!Array.isArray(ret)) throw new Error(`Value expected an array but got ${typeof ret}.`);
6590
+ return ret;
6591
+ }
6592
+
6593
+ /**
6594
+ * Internal helper to get a value from an input expected to return an array.
6595
+ *
6596
+ * @param {string} where - The method name or context using this validation (for error reporting).
6597
+ * @param {GetValueTypes} type - The type of value to retrieve ("string", "date", or "number").
6598
+ * @returns {SetValueBase[]} - The validated value as an array.
6599
+ * @throws {Error} If the returned value is not an array.
6600
+ * @readonly
6601
+ */
6602
+ _valArr(where, type) {
6603
+ return TinyHtml_TinyHtml._valArr(this, where, type);
6604
+ }
6605
+
6606
+ /**
6607
+ * Gets the raw value as a generic array of the current HTML value element (for select).
6608
+ *
6609
+ * @param {TinyInputElement} el - Target element.
6610
+ * @returns {SetValueBase[]} - The value cast as a generic array.
6611
+ * @throws {Error} If the value is not a valid array.
6612
+ */
6613
+ static valArr(el) {
6614
+ return TinyHtml_TinyHtml._valArr(el, 'valArr', 'string');
6615
+ }
6616
+
6617
+ /**
6618
+ * Gets the raw value as a generic array of the current HTML value element (for select).
6619
+ *
6620
+ * @returns {SetValueBase[]} - The value cast as a generic array.
6621
+ * @throws {Error} If the value is not a valid array.
6622
+ */
6623
+ valArr() {
6624
+ return TinyHtml_TinyHtml.valArr(this);
6625
+ }
6626
+
6627
+ /**
6628
+ * Gets the current value parsed as a number (for number/text).
6629
+ *
6630
+ * @param {TinyInputElement} el - Target element.
6631
+ * @returns {number} The numeric value.
6632
+ * @throws {Error} If the element is not a number-compatible input or value is NaN.
6633
+ */
6634
+ static valNb(el) {
6635
+ const elem = TinyHtml_TinyHtml._preInputElem(el, 'valNb');
6636
+ if (!(elem instanceof HTMLInputElement)) throw new Error('Element must be an input element.');
6637
+ /** @type {number} */
6638
+ const result = TinyHtml_TinyHtml._val(el, 'valNb', 'number');
6639
+ if (Number.isNaN(result)) throw new Error('Value is not a valid number.');
6640
+ return result;
6641
+ }
6642
+
6643
+ /**
6644
+ * Gets the current value parsed as a number (for number/text).
6645
+ *
6646
+ * @returns {number} The numeric value.
6647
+ * @throws {Error} If the element is not a number-compatible input or value is NaN.
6648
+ */
6649
+ valNb() {
6650
+ return TinyHtml_TinyHtml.valNb(this);
6651
+ }
6652
+
6653
+ /**
6654
+ * Gets the current value parsed as a Date (for time/date).
6655
+ *
6656
+ * @param {TinyInputElement} el - Target element.
6657
+ * @returns {Date} The date value.
6658
+ * @throws {Error} If the element is not a date-compatible input.
6659
+ */
6660
+ static valDate(el) {
6661
+ const elem = TinyHtml_TinyHtml._preInputElem(el, 'valDate');
6662
+ if (!(elem instanceof HTMLInputElement)) throw new Error('Element must be an input element.');
6663
+ /** @type {Date} */
6664
+ const result = TinyHtml_TinyHtml._val(el, 'valDate', 'date');
6665
+ if (!(result instanceof Date)) throw new Error('Value is not a valid date.');
6666
+ return result;
6667
+ }
6668
+
6669
+ /**
6670
+ * Gets the current value parsed as a Date (for time/date).
6671
+ *
6672
+ * @returns {Date} The date value.
6673
+ * @throws {Error} If the element is not a date-compatible input.
6674
+ */
6675
+ valDate() {
6676
+ return TinyHtml_TinyHtml.valDate(this);
6677
+ }
6678
+
6679
+ /**
6680
+ * Checks if the input element is boolean (for checkboxes/radios).
6681
+ *
6682
+ * @param {TinyInputElement} el - Target element.
6683
+ * @returns {boolean} True if the input is considered checked (value === "on"), false otherwise.
6684
+ * @throws {Error} If the element is not a checkbox/radio input.
6685
+ */
6686
+ static valBool(el) {
6687
+ const elem = TinyHtml_TinyHtml._preInputElem(el, 'valBool');
6688
+ if (!(elem instanceof HTMLInputElement)) throw new Error('Element must be an input element.');
6689
+ return TinyHtml_TinyHtml.val(elem) === 'on' ? true : false;
6690
+ }
6691
+
6692
+ /**
6693
+ * Checks if the input element is boolean (for checkboxes/radios).
6694
+ *
6695
+ * @returns {boolean} True if the input is considered checked (value === "on"), false otherwise.
6696
+ * @throws {Error} If the element is not a checkbox/radio input.
6697
+ */
6698
+ valBool() {
6699
+ return TinyHtml_TinyHtml.valBool(this);
6700
+ }
6701
+
6702
+ ////////////////////////////////////////////
6703
+
6704
+ /**
6705
+ * Registers an event listener on the specified element.
6706
+ *
6707
+ * @param {TinyEventTarget|TinyEventTarget[]} el - The target to listen on.
6708
+ * @param {string} event - The event type (e.g. 'click', 'keydown').
6709
+ * @param {EventRegistryHandle} handler - The callback function to run on event.
6710
+ * @param {EventRegistryOptions} [options] - Optional event listener options.
6711
+ */
6712
+ static on(el, event, handler, options) {
6713
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
6714
+ TinyHtml_TinyHtml._preEventTargetElems(el, 'on').forEach((elem) => {
6715
+ elem.addEventListener(event, handler, options);
6716
+
6717
+ if (!__eventRegistry.has(elem)) __eventRegistry.set(elem, {});
6718
+ const events = __eventRegistry.get(elem);
6719
+ if (!events) return;
6720
+ if (!Array.isArray(events[event])) events[event] = [];
6721
+ events[event].push({ handler, options });
6722
+ });
6723
+ }
6724
+
6725
+ /**
6726
+ * Registers an event listener on the specified element.
6727
+ *
6728
+ * @param {string} event - The event type (e.g. 'click', 'keydown').
6729
+ * @param {EventRegistryHandle} handler - The callback function to run on event.
6730
+ * @param {EventRegistryOptions} [options] - Optional event listener options.
6731
+ */
6732
+ on(event, handler, options) {
6733
+ return TinyHtml_TinyHtml.on(this, event, handler, options);
6734
+ }
6735
+
6736
+ /**
6737
+ * Registers an event listener that runs only once, then is removed.
6738
+ *
6739
+ * @param {TinyEventTarget|TinyEventTarget[]} el - The target to listen on.
6740
+ * @param {string} event - The event type (e.g. 'click', 'keydown').
6741
+ * @param {EventRegistryHandle} handler - The callback function to run on event.
6742
+ * @param {EventRegistryOptions} [options={}] - Optional event listener options.
6743
+ */
6744
+ static once(el, event, handler, options = {}) {
6745
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
6746
+ TinyHtml_TinyHtml._preEventTargetElems(el, 'once').forEach((elem) => {
6747
+ /** @type {EventRegistryHandle} e */
6748
+ const wrapped = (e) => {
6749
+ TinyHtml_TinyHtml.off(elem, event, wrapped);
6750
+ handler(e);
6751
+ };
6752
+
6753
+ TinyHtml_TinyHtml.on(
6754
+ elem,
6755
+ event,
6756
+ wrapped,
6757
+ typeof options === 'boolean' ? options : { ...options, once: true },
6758
+ );
6759
+ });
6760
+ }
6761
+
6762
+ /**
6763
+ * Registers an event listener that runs only once, then is removed.
6764
+ *
6765
+ * @param {string} event - The event type (e.g. 'click', 'keydown').
6766
+ * @param {EventRegistryHandle} handler - The callback function to run on event.
6767
+ * @param {EventRegistryOptions} [options={}] - Optional event listener options.
6768
+ */
6769
+ once(event, handler, options = {}) {
6770
+ return TinyHtml_TinyHtml.once(this, event, handler, options);
6771
+ }
6772
+
6773
+ /**
6774
+ * Removes a specific event listener from an element.
6775
+ *
6776
+ * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
6777
+ * @param {string} event - The event type.
6778
+ * @param {EventRegistryHandle} handler - The function originally bound to the event.
6779
+ * @param {boolean|EventListenerOptions} [options] - Optional listener options.
6780
+ */
6781
+ static off(el, event, handler, options) {
6782
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
6783
+ TinyHtml_TinyHtml._preEventTargetElems(el, 'off').forEach((elem) => {
6784
+ elem.removeEventListener(event, handler, options);
6785
+
6786
+ const events = __eventRegistry.get(elem);
6787
+ if (events && events[event]) {
6788
+ events[event] = events[event].filter((entry) => entry.handler !== handler);
6789
+ if (events[event].length === 0) delete events[event];
6790
+ }
6791
+ });
6792
+ }
6793
+
6794
+ /**
6795
+ * Removes a specific event listener from an element.
6796
+ *
6797
+ * @param {string} event - The event type.
6798
+ * @param {EventRegistryHandle} handler - The function originally bound to the event.
6799
+ * @param {boolean|EventListenerOptions} [options] - Optional listener options.
6800
+ */
6801
+ off(event, handler, options) {
6802
+ return TinyHtml_TinyHtml.off(this, event, handler, options);
6803
+ }
6804
+
6805
+ /**
6806
+ * Removes all event listeners of a specific type from the element.
6807
+ *
6808
+ * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
6809
+ * @param {string} event - The event type to remove (e.g. 'click').
6810
+ */
6811
+ static offAll(el, event) {
6812
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
6813
+ TinyHtml_TinyHtml._preEventTargetElems(el, 'offAll').forEach((elem) => {
6814
+ const events = __eventRegistry.get(elem);
6815
+ if (events && events[event]) {
6816
+ for (const entry of events[event]) {
6817
+ elem.removeEventListener(event, entry.handler, entry.options);
6818
+ }
6819
+ delete events[event];
6820
+ }
6821
+ });
6822
+ }
6823
+
6824
+ /**
6825
+ * Removes all event listeners of a specific type from the element.
6826
+ *
6827
+ * @param {string} event - The event type to remove (e.g. 'click').
6828
+ */
6829
+ offAll(event) {
6830
+ return TinyHtml_TinyHtml.offAll(this, event);
6831
+ }
6832
+
6833
+ /**
6834
+ * Removes all event listeners of all types from the element.
6835
+ *
6836
+ * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
6837
+ * @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
6838
+ * Optional filter function to selectively remove specific handlers.
6839
+ */
6840
+ static offAllTypes(el, filterFn = null) {
6841
+ if (filterFn !== null && typeof filterFn !== 'function')
6842
+ throw new TypeError('The "filterFn" must be a function.');
6843
+ TinyHtml_TinyHtml._preEventTargetElems(el, 'offAllTypes').forEach((elem) => {
6844
+ const events = __eventRegistry.get(elem);
6845
+ if (!events) return;
6846
+
6847
+ for (const event in events) {
6848
+ for (const entry of events[event]) {
6849
+ if (typeof filterFn !== 'function' || filterFn(entry.handler, event)) {
6850
+ elem.removeEventListener(event, entry.handler, entry.options);
6851
+ }
6852
+ }
6853
+ }
6854
+
6855
+ __eventRegistry.delete(elem);
6856
+ });
6857
+ }
6858
+
6859
+ /**
6860
+ * Removes all event listeners of all types from the element.
6861
+ *
6862
+ * @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
6863
+ * Optional filter function to selectively remove specific handlers.
6864
+ */
6865
+ offAllTypes(filterFn = null) {
6866
+ return TinyHtml_TinyHtml.offAllTypes(this, filterFn);
6867
+ }
6868
+
6869
+ /**
6870
+ * Triggers all handlers associated with a specific event on the given element.
6871
+ *
6872
+ * @param {TinyEventTarget|TinyEventTarget[]} el - Target element where the event should be triggered.
6873
+ * @param {string} event - Name of the event to trigger.
6874
+ * @param {Event|CustomEvent|CustomEventInit} [payload] - Optional event object or data to pass.
6875
+ */
6876
+ static trigger(el, event, payload = {}) {
6877
+ if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
6878
+ TinyHtml_TinyHtml._preEventTargetElems(el, 'trigger').forEach((elem) => {
6879
+ const evt =
6880
+ payload instanceof Event || payload instanceof CustomEvent
6881
+ ? payload
6882
+ : new CustomEvent(event, {
6883
+ bubbles: true,
6884
+ cancelable: true,
6885
+ detail: payload,
6886
+ });
6887
+
6888
+ elem.dispatchEvent(evt);
6889
+ });
6890
+ }
6891
+
6892
+ /**
6893
+ * Triggers all handlers associated with a specific event on the given element.
6894
+ *
6895
+ * @param {string} event - Name of the event to trigger.
6896
+ * @param {Event|CustomEvent|CustomEventInit} [payload] - Optional event object or data to pass.
6897
+ */
6898
+ trigger(event, payload = {}) {
6899
+ return TinyHtml_TinyHtml.trigger(this, event, payload);
6900
+ }
6901
+
6902
+ ///////////////////////////////////////////////////////////////
6903
+
6904
+ /**
6905
+ * Property name normalization similar to jQuery's propFix.
6906
+ * @readonly
6907
+ */
6908
+ static _propFix = {
6909
+ for: 'htmlFor',
6910
+ class: 'className',
6911
+ };
6912
+
6913
+ /**
6914
+ * Get an attribute on an element.
6915
+ * @param {TinyElement} el
6916
+ * @param {string} name
6917
+ * @returns {string|null}
6918
+ */
6919
+ static attr(el, name) {
6920
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
6921
+ const elem = TinyHtml_TinyHtml._preElem(el, 'attr');
6922
+ return elem.getAttribute(name);
6923
+ }
6924
+
6925
+ /**
6926
+ * Get an attribute on an element.
6927
+ * @param {string} name
6928
+ * @returns {string|null}
6929
+ */
6930
+ attr(name) {
6931
+ return TinyHtml_TinyHtml.attr(this, name);
6932
+ }
6933
+
6934
+ /**
6935
+ * Set an attribute on an element.
6936
+ * @param {TinyElement|TinyElement[]} el
6937
+ * @param {string} name
6938
+ * @param {string|null} [value=null]
6939
+ */
6940
+ static setAttr(el, name, value = null) {
6941
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
6942
+ if (value !== null && typeof value !== 'string')
6943
+ throw new TypeError('The "value" must be a string.');
6944
+ TinyHtml_TinyHtml._preElems(el, 'setAttr').forEach((elem) => {
6945
+ if (value === null) elem.removeAttribute(name);
6946
+ else elem.setAttribute(name, value);
6947
+ });
6948
+ }
6949
+
6950
+ /**
6951
+ * Set an attribute on an element.
6952
+ * @param {string} name
6953
+ * @param {string|null} [value=null]
6954
+ */
6955
+ setAttr(name, value) {
6956
+ return TinyHtml_TinyHtml.setAttr(this, name, value);
6957
+ }
6958
+
6959
+ /**
6960
+ * Remove attribute(s) from an element.
6961
+ * @param {TinyElement|TinyElement[]} el
6962
+ * @param {string} name Space-separated list of attributes.
6963
+ */
6964
+ static removeAttr(el, name) {
6965
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
6966
+ TinyHtml_TinyHtml._preElems(el, 'removeAttr').forEach((elem) => elem.removeAttribute(name));
6967
+ }
6968
+
6969
+ /**
6970
+ * Remove attribute(s) from an element.
6971
+ * @param {string} name Space-separated list of attributes.
6972
+ */
6973
+ removeAttr(name) {
6974
+ return TinyHtml_TinyHtml.removeAttr(this, name);
6975
+ }
6976
+
6977
+ /**
6978
+ * Check if an attribute exists on an element.
6979
+ * @param {TinyElement} el
6980
+ * @param {string} name
6981
+ * @returns {boolean}
6982
+ */
6983
+ static hasAttr(el, name) {
6984
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
6985
+ const elem = TinyHtml_TinyHtml._preElem(el, 'hasAttr');
6986
+ return elem.hasAttribute(name);
6987
+ }
6988
+
6989
+ /**
6990
+ * Check if an attribute exists on an element.
6991
+ * @param {string} name
6992
+ * @returns {boolean}
6993
+ */
6994
+ hasAttr(name) {
6995
+ return TinyHtml_TinyHtml.hasAttr(this, name);
6996
+ }
6997
+
6998
+ /**
6999
+ * Check if a property exists.
7000
+ * @param {TinyElement} el
7001
+ * @param {string} name
7002
+ * @returns {boolean}
7003
+ */
7004
+ static hasProp(el, name) {
7005
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
7006
+ const elem = TinyHtml_TinyHtml._preElem(el, 'hasProp');
7007
+ // @ts-ignore
7008
+ const propName = TinyHtml_TinyHtml._propFix[name] || name;
7009
+ // @ts-ignore
7010
+ return !!elem[propName];
7011
+ }
7012
+
7013
+ /**
7014
+ * Check if a property exists.
7015
+ * @param {string} name
7016
+ * @returns {boolean}
7017
+ */
7018
+ hasProp(name) {
7019
+ return TinyHtml_TinyHtml.hasProp(this, name);
7020
+ }
7021
+
7022
+ /**
7023
+ * Set a property on an element.
7024
+ * @param {TinyElement|TinyElement[]} el
7025
+ * @param {string} name
7026
+ */
7027
+ static addProp(el, name) {
7028
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
7029
+ TinyHtml_TinyHtml._preElems(el, 'addProp').forEach((elem) => {
7030
+ // @ts-ignore
7031
+ name = TinyHtml_TinyHtml._propFix[name] || name;
7032
+ // @ts-ignore
7033
+ elem[name] = true;
7034
+ });
7035
+ }
7036
+
7037
+ /**
7038
+ * Set a property on an element.
7039
+ * @param {string} name
7040
+ */
7041
+ addProp(name) {
7042
+ return TinyHtml_TinyHtml.addProp(this, name);
7043
+ }
7044
+
7045
+ /**
7046
+ * Remove a property from an element.
7047
+ * @param {TinyElement|TinyElement[]} el
7048
+ * @param {string} name
7049
+ */
7050
+ static removeProp(el, name) {
7051
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
7052
+ TinyHtml_TinyHtml._preElems(el, 'removeProp').forEach((elem) => {
7053
+ // @ts-ignore
7054
+ name = TinyHtml_TinyHtml._propFix[name] || name;
7055
+ // @ts-ignore
7056
+ elem[name] = false;
7057
+ });
7058
+ }
7059
+
7060
+ /**
7061
+ * Remove a property from an element.
7062
+ * @param {string} name
7063
+ */
7064
+ removeProp(name) {
7065
+ return TinyHtml_TinyHtml.removeProp(this, name);
7066
+ }
7067
+
7068
+ /**
7069
+ * Toggle a boolean property.
7070
+ * @param {TinyElement|TinyElement[]} el
7071
+ * @param {string} name
7072
+ * @param {boolean} [force]
7073
+ */
7074
+ static toggleProp(el, name, force) {
7075
+ if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
7076
+ if (typeof force !== 'undefined' && typeof force !== 'boolean')
7077
+ throw new TypeError('The "force" must be a boolean.');
7078
+ TinyHtml_TinyHtml._preElems(el, 'toggleProp').forEach((elem) => {
7079
+ // @ts-ignore
7080
+ const propName = TinyHtml_TinyHtml._propFix[name] || name;
7081
+ // @ts-ignore
7082
+ const shouldEnable = force === undefined ? !elem[propName] : force;
7083
+ // @ts-ignore
7084
+ if (shouldEnable) TinyHtml_TinyHtml.addProp(elem, name);
7085
+ else TinyHtml_TinyHtml.removeProp(elem, name);
7086
+ });
7087
+ }
7088
+
7089
+ /**
7090
+ * Toggle a boolean property.
7091
+ * @param {string} name
7092
+ * @param {boolean} [force]
7093
+ */
7094
+ toggleProp(name, force) {
7095
+ return TinyHtml_TinyHtml.toggleProp(this, name, force);
7096
+ }
7097
+
7098
+ /////////////////////////////////////////////////////
7099
+
7100
+ /**
7101
+ * Removes an element from the DOM.
7102
+ * @param {TinyElement|TinyElement[]} el - The DOM element or selector to remove.
7103
+ */
7104
+ static remove(el) {
7105
+ TinyHtml_TinyHtml._preElems(el, 'remove').forEach((elem) => elem.remove());
7106
+ }
7107
+
7108
+ /**
7109
+ * Removes the element from the DOM.
7110
+ */
7111
+ remove() {
7112
+ return TinyHtml_TinyHtml.remove(this);
7113
+ }
7114
+
7115
+ /**
7116
+ * Returns the index of the first element within its parent or relative to a selector/element.
7117
+ *
7118
+ * @param {TinyElement} el - The element target
7119
+ * @param {string|TinyElement|null} [el2] - Optional target to compare index against.
7120
+ * @returns {number}
7121
+ */
7122
+ static index(el, el2 = null) {
7123
+ const elem = TinyHtml_TinyHtml._preElem(el, 'index');
7124
+ if (!elem) return -1;
7125
+
7126
+ if (!el2) {
7127
+ return Array.prototype.indexOf.call(elem.parentNode?.children || [], elem);
7128
+ }
7129
+
7130
+ if (el2) {
7131
+ const matchEls =
7132
+ typeof el2 === 'string' ? document.querySelectorAll(el2) : TinyHtml_TinyHtml._preElems(el2, 'index');
7133
+ return Array.prototype.indexOf.call(matchEls, elem);
7134
+ }
7135
+
7136
+ return -1;
7137
+ }
7138
+
7139
+ /**
7140
+ * Returns the index of the first element within its parent or relative to a selector/element.
7141
+ *
7142
+ * @param {string|TinyElement|null} [elem] - Optional target to compare index against.
7143
+ * @returns {number}
7144
+ */
7145
+ index(elem) {
7146
+ return TinyHtml_TinyHtml.index(this, elem);
7147
+ }
7148
+
7149
+ ////////////////////////////////////////////////////////////////////
7150
+
7151
+ /**
7152
+ * Creates a new DOMRect object by copying the base rect and applying optional additional dimensions.
7153
+ *
7154
+ * @param {DOMRect} rect - The base rectangle to be cloned and extended.
7155
+ * @param {Partial<DOMRect>} extraRect - Additional dimensions to apply to the base rect (e.g., extra padding or offset).
7156
+ * @returns {DOMRect} - A new DOMRect object with the combined dimensions.
7157
+ * @readonly
7158
+ */
7159
+ static _getCustomRect(rect, extraRect) {
7160
+ /** @type {DOMRect} */
7161
+ const result = {
7162
+ height: 0,
7163
+ width: 0,
7164
+ x: 0,
7165
+ y: 0,
7166
+ bottom: 0,
7167
+ left: 0,
7168
+ right: 0,
7169
+ top: 0,
7170
+ toJSON: function () {
7171
+ throw new Error('Function not implemented.');
7172
+ },
7173
+ };
7174
+ for (const name in rect) {
7175
+ // @ts-ignore
7176
+ if (typeof rect[name] === 'number')
7177
+ // @ts-ignore
7178
+ result[name] = rect[name];
7179
+ }
7180
+
7181
+ if (typeof extraRect !== 'object' || extraRect === null || Array.isArray(extraRect))
7182
+ throw new Error('');
7183
+ const { height = 0, width = 0, top = 0, bottom = 0, left = 0, right = 0 } = extraRect;
7184
+ if (typeof height !== 'number') throw new Error('');
7185
+ if (typeof width !== 'number') throw new Error('');
7186
+ if (typeof top !== 'number') throw new Error('');
7187
+ if (typeof bottom !== 'number') throw new Error('');
7188
+ if (typeof left !== 'number') throw new Error('');
7189
+ if (typeof right !== 'number') throw new Error('');
7190
+
7191
+ // @ts-ignore
7192
+ result.height += height;
7193
+ // @ts-ignore
7194
+ result.width += width;
7195
+ // @ts-ignore
7196
+ result.top += top;
7197
+ // @ts-ignore
7198
+ result.bottom += bottom;
7199
+ // @ts-ignore
7200
+ result.left += left;
7201
+ // @ts-ignore
7202
+ result.right += right;
7203
+ return result;
7204
+ }
7205
+
7206
+ /**
7207
+ * Determines if two HTML elements are colliding, using a simple bounding box comparison.
7208
+ *
7209
+ * @param {TinyElement} el1 - The first element to compare.
7210
+ * @param {TinyElement} el2 - The second element to compare.
7211
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
7212
+ * @returns {boolean} - `true` if the elements are colliding, `false` otherwise.
7213
+ */
7214
+ static isCollWith(el1, el2, extraRect = {}) {
7215
+ const rect1 = TinyHtml_TinyHtml._getCustomRect(
7216
+ TinyHtml_TinyHtml._preElem(el1, 'isCollWith').getBoundingClientRect(),
7217
+ extraRect,
7218
+ );
7219
+ const rect2 = TinyHtml_TinyHtml._preElem(el2, 'isCollWith').getBoundingClientRect();
7220
+ return TinyHtml_areElsColliding(rect1, rect2);
7221
+ }
7222
+
7223
+ /**
7224
+ * Determines if two HTML elements are colliding, using a simple bounding box comparison.
7225
+ *
7226
+ * @param {TinyElement} el2 - The second element to compare.
7227
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
7228
+ * @returns {boolean} - `true` if the elements are colliding, `false` otherwise.
7229
+ */
7230
+ isCollWith(el2, extraRect) {
7231
+ return TinyHtml_TinyHtml.isCollWith(this, el2, extraRect);
7232
+ }
7233
+
7234
+ /**
7235
+ * Determines if two HTML elements are colliding using a pixel-perfect collision algorithm.
7236
+ *
7237
+ * @param {TinyElement} el1 - The first element to compare.
7238
+ * @param {TinyElement} el2 - The second element to compare.
7239
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
7240
+ * @returns {boolean} - `true` if the elements are colliding with higher precision, `false` otherwise.
7241
+ */
7242
+ static isCollPerfWith(el1, el2, extraRect = {}) {
7243
+ const rect1 = TinyHtml_TinyHtml._getCustomRect(
7244
+ TinyHtml_TinyHtml._preElem(el1, 'isCollPerfWith').getBoundingClientRect(),
7245
+ extraRect,
7246
+ );
7247
+ const rect2 = TinyHtml_TinyHtml._preElem(el2, 'isCollPerfWith').getBoundingClientRect();
7248
+ return TinyHtml_areElsPerfColliding(rect1, rect2);
7249
+ }
7250
+
7251
+ /**
7252
+ * Determines if two HTML elements are colliding using a pixel-perfect collision algorithm.
7253
+ *
7254
+ * @param {TinyElement} el2 - The second element to compare.
7255
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
7256
+ * @returns {boolean} - `true` if the elements are colliding with higher precision, `false` otherwise.
7257
+ */
7258
+ isCollPerfWith(el2, extraRect) {
7259
+ return TinyHtml_TinyHtml.isCollPerfWith(this, el2, extraRect);
7260
+ }
7261
+
7262
+ /**
7263
+ * Determines whether two elements are colliding with a directional lock mechanism.
7264
+ *
7265
+ * This function tracks the direction from which an element (`elem1`) initially collided with another,
7266
+ * and keeps the collision "locked" until the element exits the collision from the same direction.
7267
+ *
7268
+ * - If `isColliding` is true and no lock is stored yet, it saves the direction of entry.
7269
+ * - If `isColliding` is false but a previous lock exists, it checks if the element has exited
7270
+ * in the same direction it entered to remove the lock.
7271
+ *
7272
+ * @param {boolean} isColliding - Indicates whether `rect1` and `rect2` are currently colliding.
7273
+ * @param {DOMRect} rect1 - The bounding box of the first element.
7274
+ * @param {DOMRect} rect2 - The bounding box of the second element.
7275
+ * @param {Element} elem1 - The element to track collision state for.
7276
+ * @param {CollisionDirLock} lockDirection - The direction from which the collision was first detected.
7277
+ * @returns {boolean} Returns `true` if the element is still considered colliding (locked), otherwise `false`.
7278
+ * @readonly
7279
+ */
7280
+ static _isCollWithLock(isColliding, rect1, rect2, elem1, lockDirection) {
7281
+ const lockMap = __elemCollision[lockDirection];
7282
+
7283
+ if (isColliding) {
7284
+ // Save entry direction
7285
+ if (!lockMap.has(elem1)) {
7286
+ lockMap.set(elem1, true);
7287
+ }
7288
+ return true;
7289
+ }
7290
+
7291
+ // Handle unlock logic
7292
+ if (lockMap.has(elem1)) {
7293
+ let shouldUnlock = false;
7294
+
7295
+ switch (lockDirection) {
7296
+ case 'top':
7297
+ shouldUnlock = TinyHtml_areElsCollTop(rect1, rect2);
7298
+ break;
7299
+ case 'bottom':
7300
+ shouldUnlock = TinyHtml_areElsCollBottom(rect1, rect2);
7301
+ break;
7302
+ case 'left':
7303
+ shouldUnlock = TinyHtml_areElsCollLeft(rect1, rect2);
7304
+ break;
7305
+ case 'right':
7306
+ shouldUnlock = TinyHtml_areElsCollRight(rect1, rect2);
7307
+ break;
7308
+ }
7309
+
7310
+ if (shouldUnlock) lockMap.delete(elem1);
7311
+
7312
+ return lockMap.has(elem1); // still colliding (locked)
7313
+ }
7314
+
7315
+ return false;
7316
+ }
7317
+
7318
+ /**
7319
+ * Checks if two DOM elements are colliding on the screen, and locks the collision
7320
+ * until the element exits through the same side it entered.
7321
+ *
7322
+ * @param {TinyElement} el1 - First DOM element (e.g. draggable or moving element).
7323
+ * @param {TinyElement} el2 - Second DOM element (e.g. a container or boundary element).
7324
+ * @param {CollisionDirLock} lockDirection - Direction that must be respected to unlock the collision.
7325
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
7326
+ * @returns {boolean} True if collision is still active.
7327
+ */
7328
+ static isCollWithLock(el1, el2, lockDirection, extraRect = {}) {
7329
+ const elem1 = TinyHtml_TinyHtml._preElem(el1, 'isCollWithLock');
7330
+ const elem2 = TinyHtml_TinyHtml._preElem(el2, 'isCollWithLock');
7331
+ const rect1 = TinyHtml_TinyHtml._getCustomRect(elem1.getBoundingClientRect(), extraRect);
7332
+ const rect2 = elem2.getBoundingClientRect();
7333
+ const isColliding = TinyHtml_areElsColliding(rect1, rect2);
7334
+ return TinyHtml_TinyHtml._isCollWithLock(isColliding, rect1, rect2, elem1, lockDirection);
7335
+ }
7336
+
7337
+ /**
7338
+ * Checks if two DOM elements are colliding on the screen, and locks the collision
7339
+ * until the element exits through the same side it entered.
7340
+ *
7341
+ * @param {TinyElement} el2 - Second DOM element (e.g. a container or boundary element).
7342
+ * @param {CollisionDirLock} lockDirection - Direction that must be respected to unlock the collision.
7343
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
7344
+ * @returns {boolean} True if collision is still active.
7345
+ */
7346
+ isCollWithLock(el2, lockDirection, extraRect) {
7347
+ return TinyHtml_TinyHtml.isCollWithLock(this, el2, lockDirection, extraRect);
7348
+ }
7349
+
7350
+ /**
7351
+ * Checks if two DOM elements are colliding on the screen, and locks the collision
7352
+ * until the element exits through the same side it entered.
7353
+ *
7354
+ * @param {TinyElement} el1 - First DOM element (e.g. draggable or moving element).
7355
+ * @param {TinyElement} el2 - Second DOM element (e.g. a container or boundary element).
7356
+ * @param {CollisionDirLock} lockDirection - Direction that must be respected to unlock the collision.
7357
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
7358
+ * @returns {boolean} True if collision is still active.
7359
+ */
7360
+ static isCollPerfWithLock(el1, el2, lockDirection, extraRect = {}) {
7361
+ const elem1 = TinyHtml_TinyHtml._preElem(el1, 'isCollPerfWithLock');
7362
+ const elem2 = TinyHtml_TinyHtml._preElem(el2, 'isCollPerfWithLock');
7363
+ const rect1 = TinyHtml_TinyHtml._getCustomRect(elem1.getBoundingClientRect(), extraRect);
7364
+ const rect2 = elem2.getBoundingClientRect();
7365
+ const isColliding = TinyHtml_areElsPerfColliding(rect1, rect2);
7366
+ return TinyHtml_TinyHtml._isCollWithLock(isColliding, rect1, rect2, elem1, lockDirection);
7367
+ }
7368
+
7369
+ /**
7370
+ * Checks if two DOM elements are colliding on the screen, and locks the collision
7371
+ * until the element exits through the same side it entered.
7372
+ *
7373
+ * @param {TinyElement} el2 - Second DOM element (e.g. a container or boundary element).
7374
+ * @param {CollisionDirLock} lockDirection - Direction that must be respected to unlock the collision.
7375
+ * @param {Partial<ObjRect>} [extraRect] - Optional values to expand the size of the first element's rect.
7376
+ * @returns {boolean} True if collision is still active.
7377
+ */
7378
+ isCollPerfWithLock(el2, lockDirection, extraRect) {
7379
+ return TinyHtml_TinyHtml.isCollPerfWithLock(this, el2, lockDirection, extraRect);
7380
+ }
7381
+
7382
+ /**
7383
+ * Resets all collision locks for a specific element (for all directions).
7384
+ *
7385
+ * @param {TinyElement} el - The element whose locks should be removed.
7386
+ * @returns {boolean} True if at least one lock was removed.
7387
+ */
7388
+ static resetCollLock(el) {
7389
+ const elem = TinyHtml_TinyHtml._preElem(el, 'resetCollLock');
7390
+ let removed = false;
7391
+
7392
+ for (const dir of /** @type {CollisionDirLock[]} */ (['top', 'bottom', 'left', 'right'])) {
7393
+ if (__elemCollision[dir].has(elem)) {
7394
+ __elemCollision[dir].delete(elem);
7395
+ removed = true;
7396
+ }
7397
+ }
7398
+
7399
+ return removed;
7400
+ }
7401
+
7402
+ /**
7403
+ * Resets the collision lock for a specific element.
7404
+ *
7405
+ * This removes any previously stored collision direction for the given element,
7406
+ * effectively unlocking its collision state.
7407
+ *
7408
+ * @returns {boolean} Returns `true` if a lock was removed, `false` if no lock was present.
7409
+ */
7410
+ resetCollLock() {
7411
+ return TinyHtml_TinyHtml.resetCollLock(this);
7412
+ }
7413
+
7414
+ /**
7415
+ * Resets the collision lock for a specific element and direction.
7416
+ *
7417
+ * @param {TinyElement} el - The element whose lock should be removed.
7418
+ * @param {CollisionDirLock} direction - The direction to clear the lock from.
7419
+ * @returns {boolean} True if the lock was removed.
7420
+ */
7421
+ static resetCollLockDir(el, direction) {
7422
+ const elem = TinyHtml_TinyHtml._preElem(el, 'resetCollLockDir');
7423
+ const lockMap = __elemCollision[direction];
7424
+
7425
+ if (lockMap.has(elem)) {
7426
+ lockMap.delete(elem);
7427
+ return true;
7428
+ }
7429
+
7430
+ return false;
7431
+ }
7432
+
7433
+ /**
7434
+ * Resets the collision lock for a specific element and direction.
7435
+ *
7436
+ * @param {CollisionDirLock} direction - The direction to clear the lock from.
7437
+ * @returns {boolean} True if the lock was removed.
7438
+ */
7439
+ resetCollLockDir(direction) {
7440
+ return TinyHtml_TinyHtml.resetCollLockDir(this, direction);
7441
+ }
7442
+
7443
+ //////////////////////////////////////////////////////////////////////////////
7444
+
7445
+ /**
7446
+ * Checks if the given element is at least partially visible in the viewport.
7447
+ *
7448
+ * @param {TinyElement} el - The DOM element to check.
7449
+ * @returns {boolean} True if the element is partially in the viewport, false otherwise.
7450
+ */
7451
+ static isInViewport(el) {
7452
+ const elem = TinyHtml_TinyHtml._preElem(el, 'isInViewport');
7453
+ const elementTop = TinyHtml_TinyHtml.offset(elem).top;
7454
+ const elementBottom = elementTop + TinyHtml_TinyHtml.outerHeight(elem);
7455
+
7456
+ const viewportTop = TinyHtml_TinyHtml.scrollTop(window);
7457
+ const viewportBottom = viewportTop + TinyHtml_TinyHtml.height(window);
7458
+
7459
+ return elementBottom > viewportTop && elementTop < viewportBottom;
7460
+ }
7461
+
7462
+ /**
7463
+ * Checks if the given element is at least partially visible in the viewport.
7464
+ *
7465
+ * @returns {boolean} True if the element is partially in the viewport, false otherwise.
7466
+ */
7467
+ isInViewport() {
7468
+ return TinyHtml_TinyHtml.isInViewport(this);
7469
+ }
7470
+
7471
+ /**
7472
+ * Checks if the given element is fully visible in the viewport (top and bottom).
7473
+ *
7474
+ * @param {TinyElement} el - The DOM element to check.
7475
+ * @returns {boolean} True if the element is fully visible in the viewport, false otherwise.
7476
+ */
7477
+ static isScrolledIntoView(el) {
7478
+ const elem = TinyHtml_TinyHtml._preElem(el, 'isScrolledIntoView');
7479
+ const docViewTop = TinyHtml_TinyHtml.scrollTop(window);
7480
+ const docViewBottom = docViewTop + TinyHtml_TinyHtml.height(window);
7481
+
7482
+ const elemTop = TinyHtml_TinyHtml.offset(elem).top;
7483
+ const elemBottom = elemTop + TinyHtml_TinyHtml.height(elem);
7484
+
7485
+ return elemBottom <= docViewBottom && elemTop >= docViewTop;
7486
+ }
7487
+
7488
+ /**
7489
+ * Checks if the given element is fully visible in the viewport (top and bottom).
7490
+ *
7491
+ * @returns {boolean} True if the element is fully visible in the viewport, false otherwise.
7492
+ */
7493
+ isScrolledIntoView() {
7494
+ return TinyHtml_TinyHtml.isScrolledIntoView(this);
7495
+ }
7496
+ }
7497
+
7498
+ /* harmony default export */ const libs_TinyHtml = (TinyHtml_TinyHtml);
7499
+
7500
+ ;// ./src/v1/basics/html_deprecated.mjs
7501
+
7502
+
7503
+ /**
7504
+ * Checks if two DOM elements are colliding on the screen.
7505
+ *
7506
+ * @param {Element} elem1 - First DOM element.
7507
+ * @param {Element} elem2 - Second DOM element.
7508
+ * @returns {boolean} - Returns true if the elements are colliding.
7509
+ * @deprecated - Use TinyHtml.isCollWith instead.
7510
+ */
7511
+ function areHtmlElsColliding(elem1, elem2) {
7512
+ return libs_TinyHtml.isCollWith(elem1, elem2);
7513
+ }
7514
+
7515
+ /**
7516
+ * Checks if two DOM elements are colliding on the screen.
7517
+ *
7518
+ * @param {Element} elem1 - First DOM element.
7519
+ * @param {Element} elem2 - Second DOM element.
7520
+ * @returns {boolean} - Returns true if the elements are colliding.
7521
+ * @deprecated - Use TinyHtml.isCollPerfWith instead.
7522
+ */
7523
+ function areHtmlElsPerfColliding(elem1, elem2) {
7524
+ return TinyHtml.isCollPerfWith(elem1, elem2);
7525
+ }
7526
+
7527
+ ///////////////////////////////////////////////////////////////////////////
7528
+
7529
+ /**
7530
+ * @typedef {import('../libs/TinyHtml.mjs').HtmlElBoxSides} HtmlElBoxSides
7531
+ */
7532
+
7533
+ /**
7534
+ * Returns the total border width and individual sides from `border{Side}Width` CSS properties.
7535
+ *
7536
+ * @param {Element} el - The target DOM element.
7537
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border widths, and each side individually.
7538
+ * @deprecated - Use TinyHtml.borderWidth instead.
7539
+ */
7540
+ const getHtmlElBordersWidth = (el) => {
7541
+ return libs_TinyHtml.borderWidth(el);
7542
+ };
7543
+
7544
+ /**
7545
+ * Returns the total border size and individual sides from `border{Side}` CSS properties.
7546
+ *
7547
+ * @param {Element} el - The target DOM element.
7548
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) border sizes, and each side individually.
7549
+ * @deprecated - Use TinyHtml.border instead.
7550
+ */
7551
+ const getHtmlElBorders = (el) => {
7552
+ return libs_TinyHtml.border(el);
7553
+ };
7554
+
7555
+ /**
7556
+ * Returns the total margin and individual sides from `margin{Side}` CSS properties.
7557
+ *
7558
+ * @param {Element} el - The target DOM element.
7559
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) margins, and each side individually.
7560
+ * @deprecated - Use TinyHtml.margin instead.
7561
+ */
7562
+ const getHtmlElMargin = (el) => {
7563
+ return libs_TinyHtml.margin(el);
7564
+ };
7565
+
7566
+ /**
7567
+ * Returns the total padding and individual sides from `padding{Side}` CSS properties.
7568
+ *
7569
+ * @param {Element} el - The target DOM element.
7570
+ * @returns {HtmlElBoxSides} - Total horizontal (x) and vertical (y) paddings, and each side individually.
7571
+ * @deprecated - Use TinyHtml.padding instead.
7572
+ */
7573
+ const getHtmlElPadding = (el) => {
7574
+ return libs_TinyHtml.padding(el);
7575
+ };
7576
+
7577
+ /////////////////////////////////////////////////////////////
7578
+
7579
+ // The new version will receive great modifications, the deprecated code has been preserved for non-glitch designs that are using the original code.
7580
+
7581
+ /**
7582
+ * Checks if the given element is at least partially visible in the viewport.
7583
+ *
7584
+ * @param {HTMLElement} element - The DOM element to check.
7585
+ * @returns {boolean} True if the element is partially in the viewport, false otherwise.
7586
+ * @deprecated - Use TinyHtml.isInViewport instead.
7587
+ */
7588
+ function isInViewport(element) {
7589
+ const elementTop = element.offsetTop;
7590
+ const elementBottom = elementTop + element.offsetHeight;
7591
+
7592
+ const viewportTop = window.scrollY;
7593
+ const viewportBottom = viewportTop + window.innerHeight;
7594
+
7595
+ return elementBottom > viewportTop && elementTop < viewportBottom;
7596
+ }
7597
+
7598
+ /**
7599
+ * Checks if the given element is fully visible in the viewport (top and bottom).
7600
+ *
7601
+ * @param {HTMLElement} element - The DOM element to check.
7602
+ * @returns {boolean} True if the element is fully visible in the viewport, false otherwise.
7603
+ * @deprecated - Use TinyHtml.isScrolledIntoView instead.
7604
+ */
7605
+ function isScrolledIntoView(element) {
7606
+ const viewportTop = window.scrollY;
7607
+ const viewportBottom = viewportTop + window.innerHeight;
7608
+
7609
+ const elemTop = element.offsetTop;
7610
+ const elemBottom = elemTop + element.offsetHeight;
7611
+
7612
+ return elemBottom <= viewportBottom && elemTop >= viewportTop;
7613
+ }
7614
+
7615
+ ;// ./src/v1/basics/fullScreen.mjs
7616
+ /**
7617
+ * Checks if the document is currently in fullscreen mode.
7618
+ *
7619
+ * @returns {boolean}
7620
+ */
7621
+ const documentIsFullScreen = () =>
7622
+ !!(
7623
+ document.fullscreenElement ||
7624
+ // @ts-ignore
7625
+ document.webkitFullscreenElement ||
7626
+ // @ts-ignore
7627
+ document.mozFullScreenElement ||
7628
+ // @ts-ignore
7629
+ document.msFullscreenElement ||
7630
+ // @ts-ignore
7631
+ document.webkitIsFullScreen ||
7632
+ // @ts-ignore
7633
+ document.mozFullScreen
7634
+ );
7635
+
7636
+ /**
7637
+ * Checks if the window occupies the entire screen dimensions.
7638
+ *
7639
+ * @returns {boolean}
7640
+ */
7641
+ const isScreenFilled = () =>
7642
+ window.innerHeight === screen.height && window.innerWidth === screen.width;
7643
+
7644
+ /**
7645
+ * Checks if fullscreen mode is active either via document API or by matching screen dimensions.
7646
+ *
7647
+ * @returns {boolean}
7648
+ */
7649
+ const isFullScreenMode = () => documentIsFullScreen() || isScreenFilled();
7650
+
7651
+ /**
7652
+ * Requests fullscreen mode for the document.
7653
+ *
7654
+ * @param {FullscreenOptions} [ops]
7655
+ * @returns {Promise<void>}
7656
+ */
7657
+ const requestFullScreen = (ops) =>
7658
+ new Promise(async (resolve, reject) => {
7659
+ const docElm = document.documentElement;
7660
+ try {
7661
+ if (docElm.requestFullscreen) {
7662
+ await docElm.requestFullscreen(ops);
7663
+ // @ts-ignore
7664
+ } else if (docElm.mozRequestFullScreen) {
7665
+ // @ts-ignore
7666
+ docElm.mozRequestFullScreen(ops);
7667
+ // @ts-ignore
7668
+ } else if (docElm.webkitRequestFullScreen) {
7669
+ // @ts-ignore
7670
+ docElm.webkitRequestFullScreen(ops);
7671
+ // @ts-ignore
7672
+ } else if (docElm.msRequestFullscreen) {
7673
+ // @ts-ignore
7674
+ docElm.msRequestFullscreen(ops);
7675
+ }
7676
+ resolve();
7677
+ } catch (err) {
7678
+ reject(err);
7679
+ }
7680
+ });
7681
+
7682
+ /**
7683
+ * Exits fullscreen mode.
7684
+ *
7685
+ * @returns {Promise<void>}
7686
+ */
7687
+ const exitFullScreen = () =>
7688
+ new Promise((resolve, reject) => {
7689
+ if (document.exitFullscreen) {
7690
+ document.exitFullscreen().then(resolve).catch(reject);
7691
+ } else {
7692
+ try {
7693
+ // @ts-ignore
7694
+ if (document.mozCancelFullScreen) document.mozCancelFullScreen();
7695
+ // @ts-ignore
7696
+ else if (document.webkitCancelFullScreen) document.webkitCancelFullScreen();
7697
+ // @ts-ignore
7698
+ else if (document.msExitFullscreen) document.msExitFullscreen();
7699
+ else throw new Error('Fullscreen API is not supported');
7700
+ resolve();
7701
+ } catch (err) {
7702
+ reject(err);
7703
+ }
7704
+ }
7705
+ });
7706
+
7707
+ /** @type {readonly string[]} */
7708
+ const fullScreenEvents = [
7709
+ 'fullscreenchange',
7710
+ 'webkitfullscreenchange',
7711
+ 'mozfullscreenchange',
7712
+ 'MSFullscreenChange',
7713
+ ];
7714
+
7715
+ /**
7716
+ * Attaches a listener to fullscreen change events.
7717
+ *
7718
+ * @param {EventListenerOrEventListenerObject} listener
7719
+ * @param {boolean|AddEventListenerOptions} [ops]
7720
+ * @returns {void}
7721
+ */
7722
+ const onFullScreenChange = (listener, ops) => {
7723
+ fullScreenEvents.forEach((event) => {
7724
+ document.addEventListener(event, listener, ops);
7725
+ });
7726
+ };
7727
+
7728
+ /**
7729
+ * Removes a listener from fullscreen change events.
7730
+ *
7731
+ * @param {EventListenerOrEventListenerObject} listener
7732
+ * @param {boolean|AddEventListenerOptions} [ops]
7733
+ * @returns {void}
7734
+ */
7735
+ const offFullScreenChange = (listener, ops) => {
7736
+ fullScreenEvents.forEach((event) => {
7737
+ document.removeEventListener(event, listener, ops);
7738
+ });
7739
+ };
7740
+
7741
+ ;// ./src/v1/basics/simpleMath.mjs
7742
+ /**
7743
+ * Executes a Rule of Three calculation.
7744
+ *
7745
+ * @param {number} val1 - The first reference value (numerator in direct proportion, denominator in inverse).
7746
+ * @param {number} val2 - The second reference value (denominator in direct proportion, numerator in inverse).
7747
+ * @param {number} val3 - The third value (numerator in direct proportion, denominator in inverse).
7748
+ * @param {boolean} [inverse] - Whether the calculation should use inverse proportion (true for inverse, false for direct).
7749
+ * @returns {number} The result of the Rule of Three operation.
7750
+ *
7751
+ * Rule of Three Formula (Direct Proportion):
7752
+ * val1 / val2 = val3 / result
7753
+ *
7754
+ * For Inverse Proportion:
7755
+ * val1 / val3 = val2 / result
7756
+ *
7757
+ * Visual Representation:
7758
+ *
7759
+ * For Direct Proportion:
7760
+ * val1 val2
7761
+ * ----- = ------
7762
+ * val3 result
7763
+ *
7764
+ * For Inverse Proportion:
7765
+ * val1 val2
7766
+ * ----- = ------
7767
+ * val3 result
7768
+ *
7769
+ * @example
7770
+ * // Direct proportion:
7771
+ * ruleOfThree.execute(2, 6, 3, false); // → 9
7772
+ *
7773
+ * @example
7774
+ * // Inverse proportion:
7775
+ * ruleOfThree.execute(2, 6, 3, true); // → 4
7776
+ */
7777
+ function ruleOfThree(val1, val2, val3, inverse = false) {
7778
+ return inverse ? Number(val1 * val2) / val3 : Number(val3 * val2) / val1;
7779
+ }
7780
+
7781
+ /**
7782
+ * Calculates a percentage of a given base value.
7783
+ * @param {number} price - The base value.
7784
+ * @param {number} percentage - The percentage to apply.
7785
+ * @returns {number} The result of the percentage calculation.
7786
+ *
7787
+ * @example
7788
+ * getSimplePerc(200, 15); // 30
7789
+ */
7790
+ function getSimplePerc(price, percentage) {
7791
+ return price * (percentage / 100);
7792
+ }
7793
+
7794
+ /**
7795
+ * Calculates the age based on the given date.
7796
+ *
7797
+ * @param {number|string|Date} timeData - The birth date (can be a timestamp, ISO string, or Date object).
7798
+ * @param {Date|null} [now=null] - The Date object representing the current date. Defaults to the current date and time if not provided.
7799
+ * @returns {number|null} The age in years, or null if `timeData` is not provided or invalid.
7800
+ */
7801
+ function getAge(timeData = 0, now = null) {
7802
+ if (typeof timeData !== 'undefined' && timeData !== null && timeData !== 0) {
7803
+ const birthDate = new Date(timeData);
7804
+ if (Number.isNaN(birthDate.getTime())) return null;
7805
+
7806
+ const currentDate = now instanceof Date ? now : new Date();
7807
+
7808
+ let age = currentDate.getFullYear() - birthDate.getFullYear();
7809
+
7810
+ const currentMonth = currentDate.getMonth();
7811
+ const birthMonth = birthDate.getMonth();
7812
+
7813
+ const currentDay = currentDate.getDate();
7814
+ const birthDay = birthDate.getDate();
7815
+
7816
+ // Adjust if birthday hasn't occurred yet this year
7817
+ if (currentMonth < birthMonth || (currentMonth === birthMonth && currentDay < birthDay)) age--;
7818
+
7819
+ return Math.abs(age);
7820
+ }
7821
+
7822
+ return null;
7823
+ }
7824
+
7825
+ /**
7826
+ * @typedef {Object} FormattedByteResult
7827
+ * @property {string|null} unit - The resulting unit (e.g., 'MB', 'GB') or null if input is invalid.
7828
+ * @property {number|null} value - The numerical value in the chosen unit, or null if input is invalid.
7829
+ */
7830
+
7831
+ /**
7832
+ * Converts a byte value into a human-readable format with unit and value separated.
3520
7833
  *
3521
7834
  * @param {number} bytes - The number of bytes to format. Must be a non-negative number.
3522
7835
  * @param {number|null} [decimals=null] - The number of decimal places to include in the result. Defaults to null. If negative, it will be treated as 0. If null, no rounding is applied.
@@ -3733,6 +8046,8 @@ export default KeyPressHandler;
3733
8046
 
3734
8047
 
3735
8048
 
8049
+
8050
+
3736
8051
  })();
3737
8052
 
3738
8053
  window.TinyBasicsEs = __webpack_exports__;