tiny-essentials 1.16.2 → 1.17.1

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 (133) hide show
  1. package/dist/legacy/crypto/decrypt.cjs +1 -0
  2. package/dist/legacy/crypto/decrypt.d.mts +1 -0
  3. package/dist/legacy/crypto/decrypt.mjs +1 -0
  4. package/dist/legacy/crypto/encrypt.cjs +1 -0
  5. package/dist/legacy/crypto/encrypt.d.mts +1 -0
  6. package/dist/legacy/crypto/encrypt.mjs +1 -0
  7. package/dist/legacy/get/decimalColor.cjs +1 -0
  8. package/dist/legacy/get/decimalColor.d.mts +1 -0
  9. package/dist/legacy/get/decimalColor.mjs +1 -0
  10. package/dist/legacy/get/pagination.cjs +1 -0
  11. package/dist/legacy/get/pagination.d.mts +1 -0
  12. package/dist/legacy/get/pagination.mjs +1 -0
  13. package/dist/legacy/get/queryUrlByName.cjs +1 -0
  14. package/dist/legacy/get/queryUrlByName.d.mts +1 -0
  15. package/dist/legacy/get/queryUrlByName.mjs +1 -0
  16. package/dist/legacy/get/queryUrlJSON.cjs +1 -0
  17. package/dist/legacy/get/queryUrlJSON.d.mts +1 -0
  18. package/dist/legacy/get/queryUrlJSON.mjs +1 -0
  19. package/dist/legacy/get/super_string_filter.cjs +1 -0
  20. package/dist/legacy/get/super_string_filter.d.mts +1 -0
  21. package/dist/legacy/get/super_string_filter.mjs +1 -0
  22. package/dist/legacy/get/versionCheck.cjs +1 -0
  23. package/dist/legacy/get/versionCheck.d.mts +1 -0
  24. package/dist/legacy/get/versionCheck.mjs +1 -0
  25. package/dist/legacy/http/HTTP-1.0.cjs +2 -0
  26. package/dist/legacy/http/HTTP-1.0.mjs +2 -0
  27. package/dist/legacy/http/auth.cjs +1 -0
  28. package/dist/legacy/http/auth.d.mts +1 -0
  29. package/dist/legacy/http/auth.mjs +1 -0
  30. package/dist/legacy/http/check_domain.cjs +11 -0
  31. package/dist/legacy/http/check_domain.d.mts +3 -0
  32. package/dist/legacy/http/check_domain.mjs +11 -0
  33. package/dist/legacy/http/csrfTokenAnalyze.cjs +1 -0
  34. package/dist/legacy/http/csrfTokenAnalyze.d.mts +1 -0
  35. package/dist/legacy/http/csrfTokenAnalyze.mjs +1 -0
  36. package/dist/legacy/http/domainValidator.cjs +1 -0
  37. package/dist/legacy/http/domainValidator.d.mts +1 -0
  38. package/dist/legacy/http/domainValidator.mjs +1 -0
  39. package/dist/legacy/http/errorsCallback.cjs +1 -0
  40. package/dist/legacy/http/errorsCallback.d.mts +1 -0
  41. package/dist/legacy/http/errorsCallback.mjs +1 -0
  42. package/dist/legacy/http/fetch/json.cjs +1 -0
  43. package/dist/legacy/http/fetch/json.d.mts +1 -0
  44. package/dist/legacy/http/fetch/json.mjs +1 -0
  45. package/dist/legacy/http/fetch/text.cjs +1 -0
  46. package/dist/legacy/http/fetch/text.d.mts +1 -0
  47. package/dist/legacy/http/fetch/text.mjs +1 -0
  48. package/dist/legacy/http/fileCache.cjs +1 -0
  49. package/dist/legacy/http/fileCache.d.mts +1 -0
  50. package/dist/legacy/http/fileCache.mjs +1 -0
  51. package/dist/legacy/http/getDomainURL.cjs +1 -0
  52. package/dist/legacy/http/getDomainURL.d.mts +1 -0
  53. package/dist/legacy/http/getDomainURL.mjs +1 -0
  54. package/dist/legacy/http/userIP.cjs +1 -0
  55. package/dist/legacy/http/userIP.d.mts +1 -0
  56. package/dist/legacy/http/userIP.mjs +1 -0
  57. package/dist/legacy/libs/capitalize.cjs +1 -0
  58. package/dist/legacy/libs/capitalize.d.mts +1 -0
  59. package/dist/legacy/libs/capitalize.mjs +1 -0
  60. package/dist/legacy/libs/convertBytes.cjs +2 -0
  61. package/dist/legacy/libs/convertBytes.mjs +2 -0
  62. package/dist/legacy/libs/custom_module_loader.cjs +2 -0
  63. package/dist/legacy/libs/custom_module_loader.mjs +2 -0
  64. package/dist/legacy/libs/dice.cjs +2 -0
  65. package/dist/legacy/libs/dice.mjs +2 -0
  66. package/dist/legacy/libs/markdown.cjs +1 -0
  67. package/dist/legacy/libs/markdown.mjs +1 -0
  68. package/dist/legacy/libs/percentage.cjs +1 -0
  69. package/dist/legacy/libs/percentage.mjs +1 -0
  70. package/dist/legacy/libs/regex/getLetter.cjs +2 -0
  71. package/dist/legacy/libs/regex/getLetter.d.mts +2 -0
  72. package/dist/legacy/libs/regex/getLetter.mjs +2 -0
  73. package/dist/legacy/libs/rule3.cjs +1 -0
  74. package/dist/legacy/libs/rule3.mjs +1 -0
  75. package/dist/legacy/momentjs/getAge.cjs +1 -0
  76. package/dist/legacy/momentjs/getAge.d.mts +1 -0
  77. package/dist/legacy/momentjs/getAge.mjs +1 -0
  78. package/dist/legacy/momentjs/timeDuration.cjs +1 -0
  79. package/dist/legacy/momentjs/timeDuration.d.mts +1 -0
  80. package/dist/legacy/momentjs/timeDuration.mjs +1 -0
  81. package/dist/legacy/socket.io/antiFlood/install.cjs +1 -0
  82. package/dist/legacy/socket.io/antiFlood/install.d.mts +1 -0
  83. package/dist/legacy/socket.io/antiFlood/install.mjs +1 -0
  84. package/dist/legacy/socket.io/antiFlood/verify.cjs +1 -0
  85. package/dist/legacy/socket.io/antiFlood/verify.d.mts +1 -0
  86. package/dist/legacy/socket.io/antiFlood/verify.mjs +1 -0
  87. package/dist/legacy/socket.io/cookie-session.cjs +1 -0
  88. package/dist/legacy/socket.io/cookie-session.d.mts +1 -0
  89. package/dist/legacy/socket.io/cookie-session.mjs +1 -0
  90. package/dist/legacy/socket.io/discord.cjs +1 -0
  91. package/dist/legacy/socket.io/discord.d.mts +1 -0
  92. package/dist/legacy/socket.io/discord.mjs +1 -0
  93. package/dist/v1/TinyBasicsEs.js +729 -93
  94. package/dist/v1/TinyBasicsEs.min.js +1 -1
  95. package/dist/v1/TinyDragger.js +656 -69
  96. package/dist/v1/TinyDragger.min.js +1 -1
  97. package/dist/v1/TinyEssentials.js +2688 -116
  98. package/dist/v1/TinyEssentials.min.js +1 -1
  99. package/dist/v1/TinyHtml.js +656 -69
  100. package/dist/v1/TinyHtml.min.js +1 -1
  101. package/dist/v1/TinySmartScroller.js +6378 -0
  102. package/dist/v1/TinySmartScroller.min.js +1 -0
  103. package/dist/v1/TinyUploadClicker.js +1732 -118
  104. package/dist/v1/TinyUploadClicker.min.js +1 -1
  105. package/dist/v1/UltraRandomMsgGen.js +995 -0
  106. package/dist/v1/UltraRandomMsgGen.min.js +1 -0
  107. package/dist/v1/basics/html.cjs +74 -24
  108. package/dist/v1/basics/html.d.mts +38 -15
  109. package/dist/v1/basics/html.mjs +63 -20
  110. package/dist/v1/build/TinySmartScroller.cjs +7 -0
  111. package/dist/v1/build/TinySmartScroller.d.mts +3 -0
  112. package/dist/v1/build/TinySmartScroller.mjs +2 -0
  113. package/dist/v1/build/UltraRandomMsgGen.cjs +7 -0
  114. package/dist/v1/build/UltraRandomMsgGen.d.mts +3 -0
  115. package/dist/v1/build/UltraRandomMsgGen.mjs +2 -0
  116. package/dist/v1/index.cjs +4 -0
  117. package/dist/v1/index.d.mts +3 -1
  118. package/dist/v1/index.mjs +3 -1
  119. package/dist/v1/libs/TinyHtml.cjs +656 -69
  120. package/dist/v1/libs/TinyHtml.d.mts +440 -139
  121. package/dist/v1/libs/TinyHtml.mjs +591 -72
  122. package/dist/v1/libs/TinySmartScroller.cjs +976 -0
  123. package/dist/v1/libs/TinySmartScroller.d.mts +458 -0
  124. package/dist/v1/libs/TinySmartScroller.mjs +856 -0
  125. package/dist/v1/libs/UltraRandomMsgGen.cjs +956 -0
  126. package/dist/v1/libs/UltraRandomMsgGen.d.mts +390 -0
  127. package/dist/v1/libs/UltraRandomMsgGen.mjs +825 -0
  128. package/docs/v1/README.md +2 -0
  129. package/docs/v1/basics/html.md +17 -2
  130. package/docs/v1/libs/TinyHtml.md +296 -8
  131. package/docs/v1/libs/TinySmartScroller.md +523 -0
  132. package/docs/v1/libs/UltraRandomMsgGen.md +289 -0
  133. package/package.json +1 -1
@@ -3599,20 +3599,22 @@ function saveJsonFile(filename, data, spaces = 2) {
3599
3599
  }
3600
3600
 
3601
3601
  /**
3602
- * Loads and parses a JSON from a remote URL using Fetch API.
3603
- *
3604
- * @param {string} url - The full URL to fetch JSON from.
3605
- * @param {Object} [options] - Optional settings.
3606
- * @param {string} [options.method="GET"] - HTTP method to use (GET, POST, etc.).
3607
- * @param {any} [options.body] - Request body (only for methods like POST, PUT).
3608
- * @param {number} [options.timeout=0] - Timeout in milliseconds (ignored if signal is provided).
3609
- * @param {number} [options.retries=0] - Number of retry attempts (ignored if signal is provided).
3610
- * @param {Headers|Record<string, *>} [options.headers={}] - Additional headers.
3611
- * @param {AbortSignal|null} [options.signal] - External AbortSignal; disables timeout and retries.
3612
- * @returns {Promise<*>} Parsed JSON object.
3613
- * @throws {Error} Throws if fetch fails, times out, or returns invalid JSON.
3602
+ * @typedef {Object} FetchTemplateOptions
3603
+ * @property {string} [method="GET"] - HTTP method to use (GET, POST, etc.).
3604
+ * @property {any} [body] - Request body (only for methods like POST, PUT).
3605
+ * @property {number} [timeout=0] - Timeout in milliseconds (ignored if signal is provided).
3606
+ * @property {number} [retries=0] - Number of retry attempts (ignored if signal is provided).
3607
+ * @property {Headers|Record<string, *>} [headers={}] - Additional headers.
3608
+ * @property {AbortSignal|null} [signal] - External AbortSignal; disables timeout and retries.
3609
+ */
3610
+
3611
+ /**
3612
+ * @param {string} url - The full URL to fetch data from.
3613
+ * @param {FetchTemplateOptions} [options] - Optional settings.
3614
+ * @returns {Promise<Response>} Result data.
3615
+ * @throws {Error} Throws if fetch fails, times out.
3614
3616
  */
3615
- async function fetchJson(
3617
+ async function fetchTemplate(
3616
3618
  url,
3617
3619
  { method = 'GET', body, timeout = 0, retries = 0, headers = {}, signal = null } = {},
3618
3620
  ) {
@@ -3671,17 +3673,7 @@ async function fetchJson(
3671
3673
  if (timer) clearTimeout(timer);
3672
3674
 
3673
3675
  if (!response.ok) throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
3674
-
3675
- const contentType = response.headers.get('content-type') || '';
3676
- if (!contentType.includes('application/json'))
3677
- throw new Error(`Unexpected content-type: ${contentType}`);
3678
-
3679
- const data = await response.json();
3680
-
3681
- if (!Array.isArray(data) && !isJsonObject(data))
3682
- throw new Error('Received invalid data instead of valid JSON.');
3683
-
3684
- return data;
3676
+ return response;
3685
3677
  } catch (err) {
3686
3678
  lastError = /** @type {Error} */ (err);
3687
3679
  if (signal) break; // if an external signal came, it does not retry
@@ -3695,6 +3687,63 @@ async function fetchJson(
3695
3687
  );
3696
3688
  }
3697
3689
 
3690
+ /**
3691
+ * Loads and parses a JSON from a remote URL using Fetch API.
3692
+ *
3693
+ * @param {string} url - The full URL to fetch JSON from.
3694
+ * @param {Object} [options] - Optional settings.
3695
+ * @returns {Promise<any[] | Record<string | number | symbol, unknown>>} Parsed JSON object.
3696
+ * @throws {Error} Throws if fetch fails, times out, or returns invalid JSON.
3697
+ */
3698
+ async function fetchJson(url, options) {
3699
+ return new Promise((resolve, reject) => {
3700
+ fetchTemplate(url, options)
3701
+ .then(async (res) => {
3702
+ const contentType = res.headers.get('content-type') || '';
3703
+ if (!contentType.includes('application/json'))
3704
+ throw new Error(`Unexpected content-type: ${contentType}`);
3705
+
3706
+ const data = await res.json();
3707
+
3708
+ if (!Array.isArray(data) && !isJsonObject(data))
3709
+ throw new Error('Received invalid data instead of valid JSON.');
3710
+
3711
+ return resolve(data);
3712
+ })
3713
+ .catch(reject);
3714
+ });
3715
+ }
3716
+
3717
+ /**
3718
+ * Loads a remote file as a Blob using Fetch API.
3719
+ *
3720
+ * @param {string} url - The full URL to fetch the file from.
3721
+ * @param {Object} [options] - Optional fetch options.
3722
+ * @param {string[]} [allowedMimeTypes] - Optional list of accepted MIME types (e.g., ['image/jpeg']).
3723
+ * @returns {Promise<Blob>} - The fetched file as a Blob.
3724
+ * @throws {Error} Throws if fetch fails, response is not ok, or MIME type is not allowed.
3725
+ */
3726
+ async function fetchBlob(url, allowedMimeTypes, options) {
3727
+ return new Promise((resolve, reject) => {
3728
+ fetchTemplate(url, options)
3729
+ .then(async (res) => {
3730
+ const contentType = res.headers.get('content-type') || '';
3731
+
3732
+ if (
3733
+ Array.isArray(allowedMimeTypes) &&
3734
+ allowedMimeTypes.length > 0 &&
3735
+ !allowedMimeTypes.some((type) => contentType.includes(type))
3736
+ ) {
3737
+ throw new Error(`Blocked MIME type: ${contentType}`);
3738
+ }
3739
+
3740
+ const data = await res.blob();
3741
+ return resolve(data);
3742
+ })
3743
+ .catch(reject);
3744
+ });
3745
+ }
3746
+
3698
3747
  ///////////////////////////////////////////////////////////////////////////////
3699
3748
 
3700
3749
  /**
@@ -4226,6 +4275,19 @@ const {
4226
4275
  areElsCollRight: TinyHtml_areElsCollRight,
4227
4276
  } = collision_namespaceObject;
4228
4277
 
4278
+ /**
4279
+ * Callback invoked on each animation frame with the current scroll position,
4280
+ * normalized animation time (`0` to `1`), and a completion flag.
4281
+ *
4282
+ * @typedef {(progress: { x: number, y: number, isComplete: boolean, time: number }) => void} OnScrollAnimation
4283
+ */
4284
+
4285
+ /**
4286
+ * A list of supported easing function names for smooth animations.
4287
+ *
4288
+ * @typedef {'linear' | 'easeInQuad' | 'easeOutQuad' | 'easeInOutQuad' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic'} Easings
4289
+ */
4290
+
4229
4291
  /**
4230
4292
  * Represents a raw Node element or an instance of TinyHtml.
4231
4293
  * This type is used to abstract interactions with both plain elements
@@ -4281,6 +4343,21 @@ const {
4281
4343
  * @typedef {Element|Window} ElementAndWindow
4282
4344
  */
4283
4345
 
4346
+ /**
4347
+ * Represents a raw DOM element/window/document or an instance of TinyHtml.
4348
+ * This type is used to abstract interactions with both plain elements
4349
+ * and wrapped elements via the TinyHtml class.
4350
+ *
4351
+ * @typedef {ElementAndWinAndDoc|TinyHtml} TinyElementAndWinAndDoc
4352
+ */
4353
+
4354
+ /**
4355
+ * Represents a value that can be either a DOM Element, or the global Window object, or the document object.
4356
+ * Useful for functions that operate generically on scrollable or measurable targets.
4357
+ *
4358
+ * @typedef {Element|Window|Document} ElementAndWinAndDoc
4359
+ */
4360
+
4284
4361
  /**
4285
4362
  * A parameter type used for filtering or matching elements.
4286
4363
  * It can be:
@@ -4297,7 +4374,7 @@ const {
4297
4374
  * Elements accepted as constructor values for TinyHtml.
4298
4375
  * These include common DOM elements and root containers.
4299
4376
  *
4300
- * @typedef {Window|Element|Document} ConstructorElValues
4377
+ * @typedef {Window|Element|Document|Text} ConstructorElValues
4301
4378
  */
4302
4379
 
4303
4380
  /**
@@ -4418,40 +4495,114 @@ class TinyHtml_TinyHtml {
4418
4495
 
4419
4496
  static Utils = { ...collision_namespaceObject };
4420
4497
 
4498
+ /**
4499
+ * Creates a new DOM element with the specified tag name and options, then wraps it in a TinyHtml instance.
4500
+ *
4501
+ * @param {string} tagName - The tag name of the element to create (e.g., 'div', 'span').
4502
+ * @param {ElementCreationOptions} ops - Optional settings for creating the element.
4503
+ * @returns {TinyHtml} A TinyHtml instance wrapping the newly created DOM element.
4504
+ * @throws {TypeError} If tagName is not a string or ops is not an object.
4505
+ */
4506
+ static createElement(tagName, ops) {
4507
+ if (typeof tagName !== 'string')
4508
+ throw new TypeError(`[TinyHtml] createElement(): The tagName must be a string.`);
4509
+ if (typeof ops !== 'undefined' && typeof ops !== 'object')
4510
+ throw new TypeError(`[TinyHtml] createElement(): The ops must be a object.`);
4511
+ return new TinyHtml_TinyHtml(document.createElement(tagName, ops));
4512
+ }
4513
+
4514
+ /**
4515
+ * Creates a new TinyHtml instance that wraps a DOM TextNode.
4516
+ *
4517
+ * This method is useful when you want to insert raw text content into the DOM
4518
+ * without it being interpreted as HTML. The returned instance behaves like any
4519
+ * other TinyHtml element and can be appended or manipulated as needed.
4520
+ *
4521
+ * @param {string} value - The plain text content to be wrapped in a TextNode.
4522
+ * @returns {TinyHtml} A TinyHtml instance wrapping the newly created DOM TextNode.
4523
+ * @throws {TypeError} If the provided value is not a string.
4524
+ */
4525
+ static createTextNode(value) {
4526
+ if (typeof value !== 'string')
4527
+ throw new TypeError(`[TinyHtml] createTextNode(): The value must be a string.`);
4528
+ return new TinyHtml_TinyHtml(document.createTextNode(value));
4529
+ }
4530
+
4531
+ /**
4532
+ * Creates an HTMLElement or TextNode from an HTML string.
4533
+ * Supports both elements and plain text.
4534
+ *
4535
+ * @param {string} htmlString - The HTML string to convert.
4536
+ * @returns {TinyHtml} - A single HTMLElement or TextNode.
4537
+ */
4538
+ static createElementFromHTML(htmlString) {
4539
+ const template = document.createElement('template');
4540
+ htmlString = htmlString.trim();
4541
+
4542
+ // If it's only text (e.g., no "<" tag), return a TextNode
4543
+ if (!htmlString.startsWith('<')) {
4544
+ return TinyHtml_TinyHtml.createTextNode(htmlString);
4545
+ }
4546
+
4547
+ template.innerHTML = htmlString;
4548
+ if (!(template.content.firstChild instanceof Element)) throw new Error('');
4549
+ return new TinyHtml_TinyHtml(template.content.firstChild);
4550
+ }
4551
+
4421
4552
  /**
4422
4553
  * Queries the document for the first element matching the CSS selector and wraps it in a TinyHtml instance.
4423
4554
  *
4424
4555
  * @param {string} selector - A valid CSS selector string.
4425
- * @returns {TinyHtml} A TinyHtml instance wrapping the matched element.
4426
- * @throws {Error} If no element matches the selector.
4556
+ * @param {Document|Element} elem - Target element.
4557
+ * @returns {TinyHtml|null} A TinyHtml instance wrapping the matched element.
4427
4558
  */
4428
- static query(selector) {
4429
- const newEl = document.querySelector(selector);
4430
- if (!newEl) throw new Error(`[TinyHtml] query(): No element found for selector "${selector}".`);
4559
+ static query(selector, elem = document) {
4560
+ const newEl = elem.querySelector(selector);
4561
+ if (!newEl) return null;
4431
4562
  return new TinyHtml_TinyHtml(newEl);
4432
4563
  }
4433
4564
 
4565
+ /**
4566
+ * Queries the element for the first element matching the CSS selector and wraps it in a TinyHtml instance.
4567
+ *
4568
+ * @param {string} selector - A valid CSS selector string.
4569
+ * @returns {TinyHtml|null} A TinyHtml instance wrapping the matched element.
4570
+ */
4571
+ querySelector(selector) {
4572
+ return TinyHtml_TinyHtml.query(selector, TinyHtml_TinyHtml._preElem(this, 'query'));
4573
+ }
4574
+
4434
4575
  /**
4435
4576
  * Queries the document for all elements matching the CSS selector and wraps them in TinyHtml instances.
4436
4577
  *
4437
4578
  * @param {string} selector - A valid CSS selector string.
4579
+ * @param {Document|Element} elem - Target element.
4438
4580
  * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the matched elements.
4439
4581
  */
4440
- static queryAll(selector) {
4441
- const newEls = document.querySelectorAll(selector);
4582
+ static queryAll(selector, elem = document) {
4583
+ const newEls = elem.querySelectorAll(selector);
4442
4584
  return TinyHtml_TinyHtml.toTinyElm([...newEls]);
4443
4585
  }
4444
4586
 
4587
+ /**
4588
+ * Queries the element for all elements matching the CSS selector and wraps them in TinyHtml instances.
4589
+ *
4590
+ * @param {string} selector - A valid CSS selector string.
4591
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the matched elements.
4592
+ */
4593
+ querySelectorAll(selector) {
4594
+ return TinyHtml_TinyHtml.queryAll(selector, TinyHtml_TinyHtml._preElem(this, 'queryAll'));
4595
+ }
4596
+
4445
4597
  /**
4446
4598
  * Retrieves an element by its ID and wraps it in a TinyHtml instance.
4447
4599
  *
4448
4600
  * @param {string} selector - The ID of the element to retrieve.
4449
- * @returns {TinyHtml} A TinyHtml instance wrapping the found element.
4450
- * @throws {Error} If no element is found with the specified ID.
4601
+ * @returns {TinyHtml|null} A TinyHtml instance wrapping the found element.
4451
4602
  */
4452
4603
  static getById(selector) {
4453
4604
  const newEl = document.getElementById(selector);
4454
- if (!newEl) throw new Error(`[TinyHtml] getById(): No element found with ID "${selector}".`);
4605
+ if (!newEl) return null;
4455
4606
  return new TinyHtml_TinyHtml(newEl);
4456
4607
  }
4457
4608
 
@@ -4459,13 +4610,24 @@ class TinyHtml_TinyHtml {
4459
4610
  * Retrieves all elements with the specified class name and wraps them in TinyHtml instances.
4460
4611
  *
4461
4612
  * @param {string} selector - The class name to search for.
4613
+ * @param {Document|Element} elem - Target element.
4462
4614
  * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
4463
4615
  */
4464
- static getByClassName(selector) {
4465
- const newEls = document.getElementsByClassName(selector);
4616
+ static getByClassName(selector, elem = document) {
4617
+ const newEls = elem.getElementsByClassName(selector);
4466
4618
  return TinyHtml_TinyHtml.toTinyElm([...newEls]);
4467
4619
  }
4468
4620
 
4621
+ /**
4622
+ * Retrieves all elements with the specified class name and wraps them in TinyHtml instances.
4623
+ *
4624
+ * @param {string} selector - The class name to search for.
4625
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
4626
+ */
4627
+ getElementsByClassName(selector) {
4628
+ return TinyHtml_TinyHtml.getByClassName(selector, TinyHtml_TinyHtml._preElem(this, 'getByClassName'));
4629
+ }
4630
+
4469
4631
  /**
4470
4632
  * Retrieves all elements with the specified name attribute and wraps them in TinyHtml instances.
4471
4633
  *
@@ -4483,13 +4645,30 @@ class TinyHtml_TinyHtml {
4483
4645
  *
4484
4646
  * @param {string} localName - The local name (tag) of the elements to search for.
4485
4647
  * @param {string|null} [namespaceURI='http://www.w3.org/1999/xhtml'] - The namespace URI to search within.
4648
+ * @param {Document|Element} elem - Target element.
4486
4649
  * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
4487
4650
  */
4488
- static getByTagNameNS(localName, namespaceURI = 'http://www.w3.org/1999/xhtml') {
4489
- const newEls = document.getElementsByTagNameNS(namespaceURI, localName);
4651
+ static getByTagNameNS(localName, namespaceURI = 'http://www.w3.org/1999/xhtml', elem = document) {
4652
+ const newEls = elem.getElementsByTagNameNS(namespaceURI, localName);
4490
4653
  return TinyHtml_TinyHtml.toTinyElm([...newEls]);
4491
4654
  }
4492
4655
 
4656
+ /**
4657
+ * Retrieves all elements with the specified local tag name within the given namespace URI,
4658
+ * and wraps them in TinyHtml instances.
4659
+ *
4660
+ * @param {string} localName - The local name (tag) of the elements to search for.
4661
+ * @param {string|null} [namespaceURI='http://www.w3.org/1999/xhtml'] - The namespace URI to search within.
4662
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
4663
+ */
4664
+ getElementsByTagNameNS(localName, namespaceURI = 'http://www.w3.org/1999/xhtml') {
4665
+ return TinyHtml_TinyHtml.getByTagNameNS(
4666
+ localName,
4667
+ namespaceURI,
4668
+ TinyHtml_TinyHtml._preElem(this, 'getByTagNameNS'),
4669
+ );
4670
+ }
4671
+
4493
4672
  //////////////////////////////////////////////////////////////////
4494
4673
 
4495
4674
  /**
@@ -4770,6 +4949,45 @@ class TinyHtml_TinyHtml {
4770
4949
  return TinyHtml_TinyHtml._preElemTemplate(elems, where, [Element, Window], ['Element', 'Window']);
4771
4950
  }
4772
4951
 
4952
+ /**
4953
+ * Ensures the input is returned as an array.
4954
+ * Useful to normalize operations across multiple or single element/window/document elements.
4955
+ *
4956
+ * @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/window element or array of html elements.
4957
+ * @param {string} where - The method or context name where validation is being called.
4958
+ * @returns {ElementAndWindow[]} - Always returns an array of element/window elements.
4959
+ * @readonly
4960
+ */
4961
+ static _preElemsAndWinAndDoc(elems, where) {
4962
+ const result = TinyHtml_TinyHtml._preElemsTemplate(
4963
+ elems,
4964
+ where,
4965
+ [Element, Window, Document],
4966
+ ['Element', 'Window', 'Document'],
4967
+ );
4968
+ return result.map((elem) => (!(elem instanceof Document) ? elem : elem.documentElement));
4969
+ }
4970
+
4971
+ /**
4972
+ * Ensures the input is returned as an single element/window/document element.
4973
+ * Useful to normalize operations across multiple or single element/window/document elements.
4974
+ *
4975
+ * @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/window element or array of html elements.
4976
+ * @param {string} where - The method or context name where validation is being called.
4977
+ * @returns {ElementAndWindow} - Always returns an single element/window element.
4978
+ * @readonly
4979
+ */
4980
+ static _preElemAndWinAndDoc(elems, where) {
4981
+ const result = TinyHtml_TinyHtml._preElemTemplate(
4982
+ elems,
4983
+ where,
4984
+ [Element, Window, Document],
4985
+ ['Element', 'Window', 'Document'],
4986
+ );
4987
+ if (result instanceof Document) return result.documentElement;
4988
+ return result;
4989
+ }
4990
+
4773
4991
  /**
4774
4992
  * Normalizes and converts one or more DOM elements (or TinyHtml instances)
4775
4993
  * into an array of `TinyHtml` instances.
@@ -5116,12 +5334,13 @@ class TinyHtml_TinyHtml {
5116
5334
  * @param {string} key - The key under which the data will be stored.
5117
5335
  * @param {any} value - The value to store.
5118
5336
  * @param {boolean} [isPrivate=false] - Whether to store the data in the private store.
5119
- * @returns {void}
5337
+ * @returns {TinyElement}
5120
5338
  */
5121
5339
  static setData(el, key, value, isPrivate = false) {
5122
5340
  const data = TinyHtml_TinyHtml._dataSelector[!isPrivate ? 'public' : 'private']('setData', el);
5123
5341
  if (typeof key !== 'string') throw new TypeError('The key must be a string.');
5124
5342
  data[key] = value;
5343
+ return el;
5125
5344
  }
5126
5345
 
5127
5346
  /**
@@ -5130,7 +5349,7 @@ class TinyHtml_TinyHtml {
5130
5349
  * @param {string} key - The key under which the data will be stored.
5131
5350
  * @param {any} value - The value to store.
5132
5351
  * @param {boolean} [isPrivate=false] - Whether to store the data in the private store.
5133
- * @returns {void}
5352
+ * @returns {TinyElement}
5134
5353
  */
5135
5354
  setData(key, value, isPrivate = false) {
5136
5355
  return TinyHtml_TinyHtml.setData(this, key, value, isPrivate);
@@ -5477,18 +5696,21 @@ class TinyHtml_TinyHtml {
5477
5696
  /**
5478
5697
  * Appends child elements or strings to the end of the target element(s).
5479
5698
  *
5480
- * @param {TinyElement | TinyElement[]} el - The target element(s) to receive children.
5699
+ * @param {TinyElement} el - The target element(s) to receive children.
5481
5700
  * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to append.
5701
+ * @returns {TinyElement}
5482
5702
  */
5483
5703
  static append(el, ...children) {
5484
5704
  const elem = TinyHtml_TinyHtml._preElem(el, 'append');
5485
5705
  elem.append(...TinyHtml_TinyHtml._appendChecker('append', ...children));
5706
+ return el;
5486
5707
  }
5487
5708
 
5488
5709
  /**
5489
5710
  * Appends child elements or strings to the end of the target element(s).
5490
5711
  *
5491
5712
  * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to append.
5713
+ * @returns {TinyElement}
5492
5714
  */
5493
5715
  append(...children) {
5494
5716
  return TinyHtml_TinyHtml.append(this, ...children);
@@ -5497,18 +5719,21 @@ class TinyHtml_TinyHtml {
5497
5719
  /**
5498
5720
  * Prepends child elements or strings to the beginning of the target element(s).
5499
5721
  *
5500
- * @param {TinyElement | TinyElement[]} el - The target element(s) to receive children.
5722
+ * @param {TinyElement} el - The target element(s) to receive children.
5501
5723
  * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to prepend.
5724
+ * @returns {TinyElement}
5502
5725
  */
5503
5726
  static prepend(el, ...children) {
5504
5727
  const elem = TinyHtml_TinyHtml._preElem(el, 'prepend');
5505
5728
  elem.prepend(...TinyHtml_TinyHtml._appendChecker('prepend', ...children));
5729
+ return el;
5506
5730
  }
5507
5731
 
5508
5732
  /**
5509
5733
  * Prepends child elements or strings to the beginning of the target element(s).
5510
5734
  *
5511
5735
  * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to prepend.
5736
+ * @returns {TinyElement}
5512
5737
  */
5513
5738
  prepend(...children) {
5514
5739
  return TinyHtml_TinyHtml.prepend(this, ...children);
@@ -5517,18 +5742,21 @@ class TinyHtml_TinyHtml {
5517
5742
  /**
5518
5743
  * Inserts elements or strings immediately before the target element(s) in the DOM.
5519
5744
  *
5520
- * @param {TinyElement | TinyElement[]} el - The target element(s) before which new content is inserted.
5745
+ * @param {TinyElement} el - The target element(s) before which new content is inserted.
5521
5746
  * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert before the target.
5747
+ * @returns {TinyElement}
5522
5748
  */
5523
5749
  static before(el, ...children) {
5524
5750
  const elem = TinyHtml_TinyHtml._preElem(el, 'before');
5525
5751
  elem.before(...TinyHtml_TinyHtml._appendChecker('before', ...children));
5752
+ return el;
5526
5753
  }
5527
5754
 
5528
5755
  /**
5529
5756
  * Inserts elements or strings immediately before the target element(s) in the DOM.
5530
5757
  *
5531
5758
  * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert before the target.
5759
+ * @returns {TinyElement}
5532
5760
  */
5533
5761
  before(...children) {
5534
5762
  return TinyHtml_TinyHtml.before(this, ...children);
@@ -5537,18 +5765,21 @@ class TinyHtml_TinyHtml {
5537
5765
  /**
5538
5766
  * Inserts elements or strings immediately after the target element(s) in the DOM.
5539
5767
  *
5540
- * @param {TinyElement | TinyElement[]} el - The target element(s) after which new content is inserted.
5768
+ * @param {TinyElement} el - The target element(s) after which new content is inserted.
5541
5769
  * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert after the target.
5770
+ * @returns {TinyElement}
5542
5771
  */
5543
5772
  static after(el, ...children) {
5544
5773
  const elem = TinyHtml_TinyHtml._preElem(el, 'after');
5545
5774
  elem.after(...TinyHtml_TinyHtml._appendChecker('after', ...children));
5775
+ return el;
5546
5776
  }
5547
5777
 
5548
5778
  /**
5549
5779
  * Inserts elements or strings immediately after the target element(s) in the DOM.
5550
5780
  *
5551
5781
  * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert after the target.
5782
+ * @returns {TinyElement}
5552
5783
  */
5553
5784
  after(...children) {
5554
5785
  return TinyHtml_TinyHtml.after(this, ...children);
@@ -5557,18 +5788,21 @@ class TinyHtml_TinyHtml {
5557
5788
  /**
5558
5789
  * Replaces the target element(s) in the DOM with new elements or text.
5559
5790
  *
5560
- * @param {TinyElement | TinyElement[]} el - The element(s) to be replaced.
5791
+ * @param {TinyElement} el - The element(s) to be replaced.
5561
5792
  * @param {...(TinyNode | TinyNode[] | string)} newNodes - New elements or text to replace the target.
5793
+ * @returns {TinyElement}
5562
5794
  */
5563
5795
  static replaceWith(el, ...newNodes) {
5564
5796
  const elem = TinyHtml_TinyHtml._preElem(el, 'replaceWith');
5565
5797
  elem.replaceWith(...TinyHtml_TinyHtml._appendChecker('replaceWith', ...newNodes));
5798
+ return el;
5566
5799
  }
5567
5800
 
5568
5801
  /**
5569
5802
  * Replaces the target element(s) in the DOM with new elements or text.
5570
5803
  *
5571
5804
  * @param {...(TinyNode | TinyNode[] | string)} newNodes - New elements or text to replace the target.
5805
+ * @returns {TinyElement}
5572
5806
  */
5573
5807
  replaceWith(...newNodes) {
5574
5808
  return TinyHtml_TinyHtml.replaceWith(this, ...newNodes);
@@ -5579,6 +5813,7 @@ class TinyHtml_TinyHtml {
5579
5813
  *
5580
5814
  * @param {TinyNode | TinyNode[]} el - The element(s) to append.
5581
5815
  * @param {TinyNode | TinyNode[]} targets - Target element(s) where content will be appended.
5816
+ * @returns {TinyNode|TinyNode[]}
5582
5817
  */
5583
5818
  static appendTo(el, targets) {
5584
5819
  const elems = TinyHtml_TinyHtml._preNodeElems(el, 'appendTo');
@@ -5588,12 +5823,14 @@ class TinyHtml_TinyHtml {
5588
5823
  target.appendChild(i === tars.length - 1 ? elem : elem.cloneNode(true)),
5589
5824
  );
5590
5825
  });
5826
+ return el;
5591
5827
  }
5592
5828
 
5593
5829
  /**
5594
5830
  * Appends the given element(s) to each target element in sequence.
5595
5831
  *
5596
5832
  * @param {TinyNode | TinyNode[]} targets - Target element(s) where content will be appended.
5833
+ * @returns {TinyNode|TinyNode[]}
5597
5834
  */
5598
5835
  appendTo(targets) {
5599
5836
  return TinyHtml_TinyHtml.appendTo(this, targets);
@@ -5604,6 +5841,7 @@ class TinyHtml_TinyHtml {
5604
5841
  *
5605
5842
  * @param {TinyElement | TinyElement[]} el - The element(s) to prepend.
5606
5843
  * @param {TinyElement | TinyElement[]} targets - Target element(s) where content will be prepended.
5844
+ * @returns {TinyElement|TinyElement[]}
5607
5845
  */
5608
5846
  static prependTo(el, targets) {
5609
5847
  const elems = TinyHtml_TinyHtml._preElems(el, 'prependTo');
@@ -5614,12 +5852,14 @@ class TinyHtml_TinyHtml {
5614
5852
  .reverse()
5615
5853
  .forEach((elem) => target.prepend(i === tars.length - 1 ? elem : elem.cloneNode(true)));
5616
5854
  });
5855
+ return el;
5617
5856
  }
5618
5857
 
5619
5858
  /**
5620
5859
  * Prepends the given element(s) to each target element in sequence.
5621
5860
  *
5622
5861
  * @param {TinyElement | TinyElement[]} targets - Target element(s) where content will be prepended.
5862
+ * @returns {TinyElement|TinyElement[]}
5623
5863
  */
5624
5864
  prependTo(targets) {
5625
5865
  return TinyHtml_TinyHtml.prependTo(this, targets);
@@ -5631,6 +5871,7 @@ class TinyHtml_TinyHtml {
5631
5871
  * @param {TinyNode | TinyNode[]} el - The element(s) to insert.
5632
5872
  * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5633
5873
  * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert before, defaults to target.
5874
+ * @returns {TinyNode|TinyNode[]}
5634
5875
  */
5635
5876
  static insertBefore(el, target, child = null) {
5636
5877
  const elem = TinyHtml_TinyHtml._preNodeElem(el, 'insertBefore');
@@ -5638,6 +5879,7 @@ class TinyHtml_TinyHtml {
5638
5879
  const childNode = TinyHtml_TinyHtml._preNodeElemWithNull(child, 'insertBefore');
5639
5880
  if (!targ.parentNode) throw new Error('');
5640
5881
  targ.parentNode.insertBefore(elem, childNode || targ);
5882
+ return el;
5641
5883
  }
5642
5884
 
5643
5885
  /**
@@ -5645,6 +5887,7 @@ class TinyHtml_TinyHtml {
5645
5887
  *
5646
5888
  * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5647
5889
  * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert before, defaults to target.
5890
+ * @returns {TinyNode|TinyNode[]}
5648
5891
  */
5649
5892
  insertBefore(target, child) {
5650
5893
  return TinyHtml_TinyHtml.insertBefore(this, target, child);
@@ -5656,6 +5899,7 @@ class TinyHtml_TinyHtml {
5656
5899
  * @param {TinyNode | TinyNode[]} el - The element(s) to insert.
5657
5900
  * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5658
5901
  * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert after, defaults to target.
5902
+ * @returns {TinyNode|TinyNode[]}
5659
5903
  */
5660
5904
  static insertAfter(el, target, child = null) {
5661
5905
  const elem = TinyHtml_TinyHtml._preNodeElem(el, 'insertAfter');
@@ -5663,6 +5907,7 @@ class TinyHtml_TinyHtml {
5663
5907
  const childNode = TinyHtml_TinyHtml._preNodeElemWithNull(child, 'insertBefore');
5664
5908
  if (!targ.parentNode) throw new Error('');
5665
5909
  targ.parentNode.insertBefore(elem, childNode || targ.nextSibling);
5910
+ return el;
5666
5911
  }
5667
5912
 
5668
5913
  /**
@@ -5670,6 +5915,7 @@ class TinyHtml_TinyHtml {
5670
5915
  *
5671
5916
  * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5672
5917
  * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert after, defaults to target.
5918
+ * @returns {TinyNode|TinyNode[]}
5673
5919
  */
5674
5920
  insertAfter(target, child) {
5675
5921
  return TinyHtml_TinyHtml.insertAfter(this, target, child);
@@ -5681,6 +5927,7 @@ class TinyHtml_TinyHtml {
5681
5927
  *
5682
5928
  * @param {TinyNode | TinyNode[]} el - The new element(s) to insert.
5683
5929
  * @param {TinyNode | TinyNode[]} targets - The elements to be replaced.
5930
+ * @returns {TinyNode|TinyNode[]}
5684
5931
  */
5685
5932
  static replaceAll(el, targets) {
5686
5933
  const elems = TinyHtml_TinyHtml._preNodeElems(el, 'replaceAll');
@@ -5692,6 +5939,7 @@ class TinyHtml_TinyHtml {
5692
5939
  parent.replaceChild(i === tars.length - 1 ? elem : elem.cloneNode(true), target);
5693
5940
  });
5694
5941
  });
5942
+ return el;
5695
5943
  }
5696
5944
 
5697
5945
  /**
@@ -5699,6 +5947,7 @@ class TinyHtml_TinyHtml {
5699
5947
  * If multiple targets exist, the inserted elements are cloned accordingly.
5700
5948
  *
5701
5949
  * @param {TinyNode | TinyNode[]} targets - The elements to be replaced.
5950
+ * @returns {TinyNode|TinyNode[]}
5702
5951
  */
5703
5952
  replaceAll(targets) {
5704
5953
  return TinyHtml_TinyHtml.replaceAll(this, targets);
@@ -5722,7 +5971,12 @@ class TinyHtml_TinyHtml {
5722
5971
  throw new Error(
5723
5972
  `[TinyHtml] You are trying to put a TinyHtml inside another TinyHtml in constructor.`,
5724
5973
  );
5725
- if (!(el instanceof Element) && !(el instanceof Window) && !(el instanceof Document))
5974
+ if (
5975
+ !(el instanceof Element) &&
5976
+ !(el instanceof Window) &&
5977
+ !(el instanceof Document) &&
5978
+ !(el instanceof Text)
5979
+ )
5726
5980
  throw new Error(`[TinyHtml] Invalid Target in constructor.`);
5727
5981
  this.#el = el;
5728
5982
  }
@@ -6172,6 +6426,7 @@ class TinyHtml_TinyHtml {
6172
6426
  * @param {TinyHtmlElement|TinyHtmlElement[]} el - The element to inspect.
6173
6427
  * @param {string|Object} prop - The property name or an object with key-value pairs
6174
6428
  * @param {string|null} [value=null] - The value to set (if `prop` is a string)
6429
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6175
6430
  */
6176
6431
  static setStyle(el, prop, value = null) {
6177
6432
  TinyHtml_TinyHtml._preHtmlElems(el, 'setStyle').forEach((elem) => {
@@ -6184,6 +6439,7 @@ class TinyHtml_TinyHtml {
6184
6439
  }
6185
6440
  } else elem.style.setProperty(TinyHtml_TinyHtml.toStyleKc(prop), value);
6186
6441
  });
6442
+ return el;
6187
6443
  }
6188
6444
 
6189
6445
  /**
@@ -6194,6 +6450,7 @@ class TinyHtml_TinyHtml {
6194
6450
  *
6195
6451
  * @param {string|Object} prop - The property name or an object with key-value pairs
6196
6452
  * @param {string|null} [value=null] - The value to set (if `prop` is a string)
6453
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6197
6454
  */
6198
6455
  setStyle(prop, value) {
6199
6456
  return TinyHtml_TinyHtml.setStyle(this, prop, value);
@@ -6289,6 +6546,7 @@ class TinyHtml_TinyHtml {
6289
6546
  *
6290
6547
  * @param {TinyHtmlElement|TinyHtmlElement[]} el - A single element or an array of elements.
6291
6548
  * @param {string|string[]} prop - A property name or an array of property names to remove.
6549
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6292
6550
  */
6293
6551
  static removeStyle(el, prop) {
6294
6552
  TinyHtml_TinyHtml._preHtmlElems(el, 'removeStyle').forEach((elem) => {
@@ -6298,12 +6556,14 @@ class TinyHtml_TinyHtml {
6298
6556
  }
6299
6557
  } else elem.style.removeProperty(TinyHtml_TinyHtml.toStyleKc(prop));
6300
6558
  });
6559
+ return el;
6301
6560
  }
6302
6561
 
6303
6562
  /**
6304
6563
  * Removes one or more inline CSS properties from the given element(s).
6305
6564
  *
6306
6565
  * @param {string|string[]} prop - A property name or an array of property names to remove.
6566
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6307
6567
  */
6308
6568
  removeStyle(prop) {
6309
6569
  return TinyHtml_TinyHtml.removeStyle(this, prop);
@@ -6318,6 +6578,7 @@ class TinyHtml_TinyHtml {
6318
6578
  * @param {string} prop - The CSS property to toggle.
6319
6579
  * @param {string} val1 - The first value (used as "current" check).
6320
6580
  * @param {string} val2 - The second value (used as the "alternative").
6581
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6321
6582
  */
6322
6583
  static toggleStyle(el, prop, val1, val2) {
6323
6584
  TinyHtml_TinyHtml._preHtmlElems(el, 'toggleStyle').forEach((elem) => {
@@ -6325,6 +6586,7 @@ class TinyHtml_TinyHtml {
6325
6586
  const newVal = current === TinyHtml_TinyHtml.toStyleKc(val1) ? val2 : val1;
6326
6587
  TinyHtml_TinyHtml.setStyle(elem, prop, newVal);
6327
6588
  });
6589
+ return el;
6328
6590
  }
6329
6591
 
6330
6592
  /**
@@ -6335,6 +6597,7 @@ class TinyHtml_TinyHtml {
6335
6597
  * @param {string} prop - The CSS property to toggle.
6336
6598
  * @param {string} val1 - The first value (used as "current" check).
6337
6599
  * @param {string} val2 - The second value (used as the "alternative").
6600
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6338
6601
  */
6339
6602
  toggleStyle(prop, val1, val2) {
6340
6603
  return TinyHtml_TinyHtml.toggleStyle(this, prop, val1, val2);
@@ -6344,14 +6607,16 @@ class TinyHtml_TinyHtml {
6344
6607
  * Removes all inline styles (`style` attribute) from the given element(s).
6345
6608
  *
6346
6609
  * @param {TinyElement|TinyElement[]} el - A single element or an array of elements.
6610
+ * @returns {TinyElement|TinyElement[]}
6347
6611
  */
6348
6612
  static clearStyle(el) {
6349
6613
  TinyHtml_TinyHtml._preElems(el, 'clearStyle').forEach((elem) => elem.removeAttribute('style'));
6614
+ return el;
6350
6615
  }
6351
6616
 
6352
6617
  /**
6353
6618
  * Removes all inline styles (`style` attribute) from the given element(s).
6354
- *
6619
+ * @returns {TinyElement|TinyElement[]}
6355
6620
  */
6356
6621
  clearStyle() {
6357
6622
  return TinyHtml_TinyHtml.clearStyle(this);
@@ -6363,14 +6628,17 @@ class TinyHtml_TinyHtml {
6363
6628
  * Focus the element.
6364
6629
  *
6365
6630
  * @param {TinyHtmlElement} el - The element or a selector string.
6631
+ * @returns {TinyHtmlElement}
6366
6632
  */
6367
6633
  static focus(el) {
6368
6634
  const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'focus');
6369
6635
  elem.focus();
6636
+ return el;
6370
6637
  }
6371
6638
 
6372
6639
  /**
6373
6640
  * Focus the element.
6641
+ * @returns {TinyHtmlElement}
6374
6642
  */
6375
6643
  focus() {
6376
6644
  return TinyHtml_TinyHtml.focus(this);
@@ -6380,19 +6648,46 @@ class TinyHtml_TinyHtml {
6380
6648
  * Blur the element.
6381
6649
  *
6382
6650
  * @param {TinyHtmlElement} el - The element or a selector string.
6651
+ * @returns {TinyHtmlElement}
6383
6652
  */
6384
6653
  static blur(el) {
6385
6654
  const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'blur');
6386
6655
  elem.blur();
6656
+ return el;
6387
6657
  }
6388
6658
 
6389
6659
  /**
6390
6660
  * Blur the element.
6661
+ * @returns {TinyHtmlElement}
6391
6662
  */
6392
6663
  blur() {
6393
6664
  return TinyHtml_TinyHtml.blur(this);
6394
6665
  }
6395
6666
 
6667
+ /**
6668
+ * Interprets a value as a boolean `true` if it matches a common truthy representation.
6669
+ *
6670
+ * This method checks if the input is any of the common forms used to represent `true`,
6671
+ * such as the string `'true'`, `'1'`, `'on'`, the boolean `true`, or the number `1`.
6672
+ *
6673
+ * @param {string|boolean|number} [value] - The value to interpret as boolean.
6674
+ * @returns {boolean} `true` if the value represents a truthy state, otherwise `false`.
6675
+ */
6676
+ static boolCheck(value) {
6677
+ if (
6678
+ typeof value !== 'undefined' &&
6679
+ (value === 'true' ||
6680
+ value === '1' ||
6681
+ value === true ||
6682
+ value === 'on' ||
6683
+ (typeof value === 'number' && value > 0))
6684
+ ) {
6685
+ return true;
6686
+ } else {
6687
+ return false;
6688
+ }
6689
+ }
6690
+
6396
6691
  //////////////////////////////////////////////////////////////////////
6397
6692
 
6398
6693
  /**
@@ -6401,7 +6696,7 @@ class TinyHtml_TinyHtml {
6401
6696
  */
6402
6697
  static setWinScrollTop(value) {
6403
6698
  if (typeof value !== 'number') throw new TypeError('The value must be a number.');
6404
- window.scrollTo({ top: value });
6699
+ TinyHtml_TinyHtml.setScrollTop(window, value);
6405
6700
  }
6406
6701
 
6407
6702
  /**
@@ -6410,7 +6705,7 @@ class TinyHtml_TinyHtml {
6410
6705
  */
6411
6706
  static setWinScrollLeft(value) {
6412
6707
  if (typeof value !== 'number') throw new TypeError('The value must be a number.');
6413
- window.scrollTo({ left: value });
6708
+ TinyHtml_TinyHtml.setScrollLeft(window, value);
6414
6709
  }
6415
6710
 
6416
6711
  /**
@@ -6445,16 +6740,40 @@ class TinyHtml_TinyHtml {
6445
6740
  return window.innerWidth || document.documentElement.clientWidth;
6446
6741
  }
6447
6742
 
6743
+ /**
6744
+ * Checks if the page is currently scrolled to the very top.
6745
+ *
6746
+ * This method compares the current vertical scroll position with the total document height.
6747
+ * It's useful for detecting if the user has reached the top of the page.
6748
+ *
6749
+ * @returns {boolean} `true` if the page is scrolled to the top, otherwise `false`.
6750
+ */
6751
+ static isPageTop() {
6752
+ return window.scrollY === 0;
6753
+ }
6754
+
6755
+ /**
6756
+ * Checks if the page is currently scrolled to the very bottom.
6757
+ *
6758
+ * This method uses the `scrollY` and `innerHeight` properties to determine if the
6759
+ * user has reached the end of the document.
6760
+ *
6761
+ * @returns {boolean} `true` if the page is scrolled to the bottom, otherwise `false`.
6762
+ */
6763
+ static isPageBottom() {
6764
+ return window.innerHeight + window.scrollY >= document.body.offsetHeight;
6765
+ }
6766
+
6448
6767
  /**
6449
6768
  * Gets the width or height of an element based on the box model.
6450
- * @param {TinyElementAndWindow} el - The element or window.
6769
+ * @param {TinyElementAndWinAndDoc} el - The element or window.
6451
6770
  * @param {"width"|"height"} type - Dimension type.
6452
6771
  * @param {"content"|"padding"|"border"|"margin"} [extra='content'] - Box model context.
6453
6772
  * @returns {number} - Computed dimension.
6454
6773
  * @throws {TypeError} If `type` or `extra` is not a string.
6455
6774
  */
6456
6775
  static getDimension(el, type, extra = 'content') {
6457
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'getDimension');
6776
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'getDimension');
6458
6777
  if (typeof type !== 'string') throw new TypeError('The type must be a string.');
6459
6778
  if (typeof extra !== 'string') throw new TypeError('The extra must be a string.');
6460
6779
 
@@ -6542,17 +6861,20 @@ class TinyHtml_TinyHtml {
6542
6861
  * @param {TinyHtmlElement} el - Target element.
6543
6862
  * @param {string|number} value - Height value.
6544
6863
  * @throws {TypeError} If `value` is neither a string nor number.
6864
+ * @returns {TinyHtmlElement}
6545
6865
  */
6546
6866
  static setHeight(el, value) {
6547
6867
  const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'setHeight');
6548
6868
  if (typeof value !== 'number' && typeof value !== 'string')
6549
6869
  throw new TypeError('The value must be a string or number.');
6550
6870
  elem.style.height = typeof value === 'number' ? `${value}px` : value;
6871
+ return el;
6551
6872
  }
6552
6873
 
6553
6874
  /**
6554
6875
  * Sets the height of the element.
6555
6876
  * @param {string|number} value - Height value.
6877
+ * @returns {TinyHtmlElement}
6556
6878
  */
6557
6879
  setHeight(value) {
6558
6880
  return TinyHtml_TinyHtml.setHeight(this, value);
@@ -6563,17 +6885,20 @@ class TinyHtml_TinyHtml {
6563
6885
  * @param {TinyHtmlElement} el - Target element.
6564
6886
  * @param {string|number} value - Width value.
6565
6887
  * @throws {TypeError} If `value` is neither a string nor number.
6888
+ * @returns {TinyHtmlElement}
6566
6889
  */
6567
6890
  static setWidth(el, value) {
6568
6891
  const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'setWidth');
6569
6892
  if (typeof value !== 'number' && typeof value !== 'string')
6570
6893
  throw new TypeError('The value must be a string or number.');
6571
6894
  elem.style.width = typeof value === 'number' ? `${value}px` : value;
6895
+ return el;
6572
6896
  }
6573
6897
 
6574
6898
  /**
6575
6899
  * Sets the width of the element.
6576
6900
  * @param {string|number} value - Width value.
6901
+ * @returns {TinyHtmlElement}
6577
6902
  */
6578
6903
  setWidth(value) {
6579
6904
  return TinyHtml_TinyHtml.setWidth(this, value);
@@ -6581,11 +6906,11 @@ class TinyHtml_TinyHtml {
6581
6906
 
6582
6907
  /**
6583
6908
  * Returns content box height.
6584
- * @param {TinyElementAndWindow} el - Target element.
6909
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6585
6910
  * @returns {number}
6586
6911
  */
6587
6912
  static height(el) {
6588
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'height');
6913
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'height');
6589
6914
  return TinyHtml_TinyHtml.getDimension(elem, 'height', 'content');
6590
6915
  }
6591
6916
 
@@ -6599,11 +6924,11 @@ class TinyHtml_TinyHtml {
6599
6924
 
6600
6925
  /**
6601
6926
  * Returns content box width.
6602
- * @param {TinyElementAndWindow} el - Target element.
6927
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6603
6928
  * @returns {number}
6604
6929
  */
6605
6930
  static width(el) {
6606
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'width');
6931
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'width');
6607
6932
  return TinyHtml_TinyHtml.getDimension(elem, 'width', 'content');
6608
6933
  }
6609
6934
 
@@ -6617,11 +6942,11 @@ class TinyHtml_TinyHtml {
6617
6942
 
6618
6943
  /**
6619
6944
  * Returns padding box height.
6620
- * @param {TinyElementAndWindow} el - Target element.
6945
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6621
6946
  * @returns {number}
6622
6947
  */
6623
6948
  static innerHeight(el) {
6624
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'innerHeight');
6949
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'innerHeight');
6625
6950
  return TinyHtml_TinyHtml.getDimension(elem, 'height', 'padding');
6626
6951
  }
6627
6952
 
@@ -6635,11 +6960,11 @@ class TinyHtml_TinyHtml {
6635
6960
 
6636
6961
  /**
6637
6962
  * Returns padding box width.
6638
- * @param {TinyElementAndWindow} el - Target element.
6963
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6639
6964
  * @returns {number}
6640
6965
  */
6641
6966
  static innerWidth(el) {
6642
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'innerWidth');
6967
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'innerWidth');
6643
6968
  return TinyHtml_TinyHtml.getDimension(elem, 'width', 'padding');
6644
6969
  }
6645
6970
 
@@ -6653,14 +6978,14 @@ class TinyHtml_TinyHtml {
6653
6978
 
6654
6979
  /**
6655
6980
  * Returns outer height of the element, optionally including margin.
6656
- * @param {TinyElementAndWindow} el - Target element.
6981
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6657
6982
  * @param {boolean} [includeMargin=false] - Whether to include margin.
6658
6983
  * @returns {number}
6659
6984
  */
6660
6985
  static outerHeight(el, includeMargin = false) {
6661
6986
  if (typeof includeMargin !== 'boolean')
6662
6987
  throw new TypeError('The "includeMargin" must be a boolean.');
6663
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'outerHeight');
6988
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'outerHeight');
6664
6989
  return TinyHtml_TinyHtml.getDimension(elem, 'height', includeMargin ? 'margin' : 'border');
6665
6990
  }
6666
6991
 
@@ -6675,14 +7000,14 @@ class TinyHtml_TinyHtml {
6675
7000
 
6676
7001
  /**
6677
7002
  * Returns outer width of the element, optionally including margin.
6678
- * @param {TinyElementAndWindow} el - Target element.
7003
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6679
7004
  * @param {boolean} [includeMargin=false] - Whether to include margin.
6680
7005
  * @returns {number}
6681
7006
  */
6682
7007
  static outerWidth(el, includeMargin = false) {
6683
7008
  if (typeof includeMargin !== 'boolean')
6684
7009
  throw new TypeError('The "includeMargin" must be a boolean.');
6685
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'outerWidth');
7010
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'outerWidth');
6686
7011
  return TinyHtml_TinyHtml.getDimension(elem, 'width', includeMargin ? 'margin' : 'border');
6687
7012
  }
6688
7013
 
@@ -6697,6 +7022,30 @@ class TinyHtml_TinyHtml {
6697
7022
 
6698
7023
  //////////////////////////////////////////////////
6699
7024
 
7025
+ /**
7026
+ * Applies an animation to one or multiple TinyElement instances.
7027
+ *
7028
+ * @param {TinyElement|TinyElement[]} el - A single TinyElement or an array of TinyElements to animate.
7029
+ * @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes - The keyframes used to define the animation.
7030
+ * @param {number | KeyframeAnimationOptions} [ops] - Timing or configuration options for the animation.
7031
+ * @returns {TinyElement|TinyElement[]}
7032
+ */
7033
+ static animate(el, keyframes, ops) {
7034
+ TinyHtml_TinyHtml._preElems(el, 'animate').forEach((elem) => elem.animate(keyframes, ops));
7035
+ return el;
7036
+ }
7037
+
7038
+ /**
7039
+ * Applies an animation to one or multiple TinyElement instances.
7040
+ *
7041
+ * @param {Keyframe[] | PropertyIndexedKeyframes | null} keyframes - The keyframes used to define the animation.
7042
+ * @param {number | KeyframeAnimationOptions} [ops] - Timing or configuration options for the animation.
7043
+ * @returns {TinyElement|TinyElement[]}
7044
+ */
7045
+ animate(keyframes, ops) {
7046
+ return TinyHtml_TinyHtml.animate(this, keyframes, ops);
7047
+ }
7048
+
6700
7049
  /**
6701
7050
  * Gets the offset of the element relative to the document.
6702
7051
  * @param {TinyElement} el - Target element.
@@ -6852,27 +7201,151 @@ class TinyHtml_TinyHtml {
6852
7201
  }
6853
7202
 
6854
7203
  /**
6855
- * Sets the vertical scroll position.
6856
- * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
6857
- * @param {number} value - Scroll top value.
7204
+ * Collection of easing functions used for scroll and animation calculations.
7205
+ * Each function receives a normalized time value (`t` from 0 to 1) and returns the eased progress.
7206
+ *
7207
+ * @type {Record<string, (t: number) => number>}
6858
7208
  */
6859
- static setScrollTop(el, value) {
6860
- if (typeof value !== 'number') throw new TypeError('ScrollTop value must be a number.');
6861
- TinyHtml_TinyHtml._preElemsAndWindow(el, 'setScrollTop').forEach((elem) => {
6862
- if (TinyHtml_TinyHtml.isWindow(elem)) {
6863
- elem.scrollTo(elem.pageXOffset, value);
7209
+ static easings = {
7210
+ linear: (t) => t,
7211
+ easeInQuad: (t) => t * t,
7212
+ easeOutQuad: (t) => t * (2 - t),
7213
+ easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
7214
+ easeInCubic: (t) => t * t * t,
7215
+ easeOutCubic: (t) => --t * t * t + 1,
7216
+ easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1),
7217
+ };
7218
+
7219
+ /**
7220
+ * Smoothly scrolls one or more elements (or the window) to the specified X and Y coordinates
7221
+ * using a custom duration and easing function.
7222
+ *
7223
+ * If `duration` or a valid `easing` is not provided, the scroll will be performed immediately.
7224
+ *
7225
+ * @param {TinyElementAndWindow | TinyElementAndWindow[]} el - A single element, array of elements, or the window to scroll.
7226
+ * @param {Object} [settings={}] - Configuration object for the scroll animation.
7227
+ * @param {number} [settings.targetX] - The horizontal scroll target in pixels.
7228
+ * @param {number} [settings.targetY] - The vertical scroll target in pixels.
7229
+ * @param {number} [settings.duration] - The duration of the animation in milliseconds.
7230
+ * @param {Easings} [settings.easing] - The easing function name to use for the scroll animation.
7231
+ * @param {OnScrollAnimation} [settings.onAnimation] - Optional callback invoked on each animation
7232
+ * frame with the current scroll position, normalized animation time (`0` to `1`), and a completion flag.
7233
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
7234
+ * @throws {TypeError} If `el` is not a valid element, array, or window.
7235
+ * @throws {TypeError} If `targetX` or `targetY` is defined but not a number.
7236
+ * @throws {TypeError} If `duration` is defined but not a number.
7237
+ * @throws {TypeError} If `easing` is defined but not a valid easing function name.
7238
+ * @throws {TypeError} If `onAnimation` is defined but not a function.
7239
+ */
7240
+ static scrollToXY(el, { targetX, targetY, duration, easing, onAnimation } = {}) {
7241
+ if (targetX !== undefined && typeof targetX !== 'number')
7242
+ throw new TypeError('`targetX` must be a number if provided.');
7243
+ if (targetY !== undefined && typeof targetY !== 'number')
7244
+ throw new TypeError('`targetY` must be a number if provided.');
7245
+ if (duration !== undefined && typeof duration !== 'number')
7246
+ throw new TypeError('`duration` must be a number if provided.');
7247
+ if (easing !== undefined && typeof easing !== 'string')
7248
+ throw new TypeError('`easing` must be a string if provided.');
7249
+ if (easing !== undefined && typeof TinyHtml_TinyHtml.easings[easing] !== 'function')
7250
+ throw new TypeError(`Unknown easing function: "${easing}".`);
7251
+ if (onAnimation !== undefined && typeof onAnimation !== 'function')
7252
+ throw new TypeError('`onAnimation` must be a function if provided.');
7253
+
7254
+ /**
7255
+ * Performs an instant scroll to the given coordinates.
7256
+ *
7257
+ * @param {ElementAndWindow} elem - The element or window to scroll.
7258
+ * @param {number} newX - The final horizontal scroll position.
7259
+ * @param {number} newY - The final vertical scroll position.
7260
+ * @param {number} time - Normalized progress value.
7261
+ */
7262
+ const executeScroll = (elem, newX, newY, time) => {
7263
+ if (elem instanceof Window) {
7264
+ window.scrollTo(newX, newY);
6864
7265
  } else if (elem.nodeType === 9) {
6865
7266
  // @ts-ignore
6866
- elem.defaultView.scrollTo(elem.defaultView.pageXOffset, value);
7267
+ elem.defaultView.scrollTo(newX, newY);
6867
7268
  } else {
6868
- elem.scrollTop = value;
7269
+ const startX = elem instanceof Window ? window.scrollX : elem.scrollLeft;
7270
+ const startY = elem instanceof Window ? window.scrollY : elem.scrollTop;
7271
+ if (startX !== newX) elem.scrollLeft = newX;
7272
+ if (startY !== newY) elem.scrollTop = newY;
7273
+ }
7274
+ if (typeof onAnimation === 'function')
7275
+ onAnimation({ x: newX, y: newY, isComplete: time >= 1, time });
7276
+ };
7277
+
7278
+ TinyHtml_TinyHtml._preElemsAndWindow(el, 'scrollToXY').forEach((elem) => {
7279
+ const startX = elem instanceof Window ? window.scrollX : elem.scrollLeft;
7280
+ const startY = elem instanceof Window ? window.scrollY : elem.scrollTop;
7281
+ const targX = targetX ?? startX;
7282
+ const targY = targetY ?? startY;
7283
+
7284
+ const changeX = targX - startX;
7285
+ const changeY = targY - startY;
7286
+
7287
+ const ease = (typeof easing === 'string' && TinyHtml_TinyHtml.easings[easing]) || null;
7288
+ if (typeof duration !== 'number' || typeof ease !== 'function')
7289
+ return executeScroll(elem, targX, targY, 1);
7290
+ const startTime = performance.now();
7291
+ const dur = duration ?? 0;
7292
+
7293
+ /**
7294
+ * Animates the scroll position based on easing and time.
7295
+ *
7296
+ * @param {number} currentTime - Timestamp provided by requestAnimationFrame.
7297
+ */
7298
+ function animateScroll(currentTime) {
7299
+ if (typeof ease !== 'function') return;
7300
+ const time = Math.min(1, (currentTime - startTime) / dur);
7301
+ const easedTime = ease(time);
7302
+
7303
+ const newX = startX + changeX * easedTime;
7304
+ const newY = startY + changeY * easedTime;
7305
+ executeScroll(elem, newX, newY, time);
7306
+
7307
+ if (time < 1) requestAnimationFrame(animateScroll);
6869
7308
  }
7309
+
7310
+ requestAnimationFrame(animateScroll);
6870
7311
  });
7312
+ return el;
7313
+ }
7314
+
7315
+ /**
7316
+ * Smoothly scrolls one or more elements (or the window) to the specified X and Y coordinates
7317
+ * using a custom duration and easing function.
7318
+ *
7319
+ * If `duration` or a valid `easing` is not provided, the scroll will be performed immediately.
7320
+ *
7321
+ * @param {Object} [settings={}] - Configuration object for the scroll animation.
7322
+ * @param {number} [settings.targetX] - The horizontal scroll target in pixels.
7323
+ * @param {number} [settings.targetY] - The vertical scroll target in pixels.
7324
+ * @param {number} [settings.duration] - The duration of the animation in milliseconds.
7325
+ * @param {Easings} [settings.easing] - The easing function name to use for the scroll animation.
7326
+ * @param {OnScrollAnimation} [settings.onAnimation] - Optional callback invoked on each animation
7327
+ * frame with the current scroll position, normalized animation time (`0` to `1`), and a completion flag.
7328
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
7329
+ */
7330
+ scrollToXY({ targetX, targetY, duration, easing, onAnimation } = {}) {
7331
+ return TinyHtml_TinyHtml.scrollToXY(this, { targetX, targetY, duration, easing, onAnimation });
7332
+ }
7333
+
7334
+ /**
7335
+ * Sets the vertical scroll position.
7336
+ * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
7337
+ * @param {number} value - Scroll top value.
7338
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
7339
+ */
7340
+ static setScrollTop(el, value) {
7341
+ if (typeof value !== 'number') throw new TypeError('ScrollTop value must be a number.');
7342
+ return TinyHtml_TinyHtml.scrollToXY(el, { targetY: value });
6871
7343
  }
6872
7344
 
6873
7345
  /**
6874
7346
  * Sets the vertical scroll position.
6875
7347
  * @param {number} value - Scroll top value.
7348
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
6876
7349
  */
6877
7350
  setScrollTop(value) {
6878
7351
  return TinyHtml_TinyHtml.setScrollTop(this, value);
@@ -6882,24 +7355,17 @@ class TinyHtml_TinyHtml {
6882
7355
  * Sets the horizontal scroll position.
6883
7356
  * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
6884
7357
  * @param {number} value - Scroll left value.
7358
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
6885
7359
  */
6886
7360
  static setScrollLeft(el, value) {
6887
7361
  if (typeof value !== 'number') throw new TypeError('ScrollLeft value must be a number.');
6888
- TinyHtml_TinyHtml._preElemsAndWindow(el, 'setScrollLeft').forEach((elem) => {
6889
- if (TinyHtml_TinyHtml.isWindow(elem)) {
6890
- elem.scrollTo(value, elem.pageYOffset);
6891
- } else if (elem.nodeType === 9) {
6892
- // @ts-ignore
6893
- elem.defaultView.scrollTo(value, elem.defaultView.pageYOffset);
6894
- } else {
6895
- elem.scrollLeft = value;
6896
- }
6897
- });
7362
+ return TinyHtml_TinyHtml.scrollToXY(el, { targetX: value });
6898
7363
  }
6899
7364
 
6900
7365
  /**
6901
7366
  * Sets the horizontal scroll position.
6902
7367
  * @param {number} value - Scroll left value.
7368
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
6903
7369
  */
6904
7370
  setScrollLeft(value) {
6905
7371
  return TinyHtml_TinyHtml.setScrollLeft(this, value);
@@ -7030,15 +7496,16 @@ class TinyHtml_TinyHtml {
7030
7496
 
7031
7497
  /**
7032
7498
  * Adds one or more CSS class names to the element.
7033
- * @type {(el: TinyElement|TinyElement[], ...tokens: string[]) => void} - One or more class names to add.
7499
+ * @type {(el: TinyElement|TinyElement[], ...tokens: string[]) => (TinyElement|TinyElement[])} - One or more class names to add.
7034
7500
  */
7035
7501
  static addClass(el, ...args) {
7036
7502
  TinyHtml_TinyHtml._preElems(el, 'addClass').forEach((elem) => elem.classList.add(...args));
7503
+ return el;
7037
7504
  }
7038
7505
 
7039
7506
  /**
7040
7507
  * Adds one or more CSS class names to the element.
7041
- * @type {(...tokens: string[]) => void} - One or more class names to add.
7508
+ * @type {(...tokens: string[]) => (TinyElement|TinyElement[])} - One or more class names to add.
7042
7509
  */
7043
7510
  addClass(...args) {
7044
7511
  return TinyHtml_TinyHtml.addClass(this, ...args);
@@ -7046,15 +7513,16 @@ class TinyHtml_TinyHtml {
7046
7513
 
7047
7514
  /**
7048
7515
  * Removes one or more CSS class names from the element.
7049
- * @type {(el: TinyElement|TinyElement[], ...tokens: string[]) => void} - One or more class names to remove.
7516
+ * @type {(el: TinyElement|TinyElement[], ...tokens: string[]) => (TinyElement|TinyElement[])} - One or more class names to remove.
7050
7517
  */
7051
7518
  static removeClass(el, ...args) {
7052
7519
  TinyHtml_TinyHtml._preElems(el, 'removeClass').forEach((elem) => elem.classList.remove(...args));
7520
+ return el;
7053
7521
  }
7054
7522
 
7055
7523
  /**
7056
7524
  * Removes one or more CSS class names from the element.
7057
- * @type {(...tokens: string[]) => void} - One or more class names to remove.
7525
+ * @type {(...tokens: string[]) => (TinyElement|TinyElement[])} - One or more class names to remove.
7058
7526
  */
7059
7527
  removeClass(...args) {
7060
7528
  return TinyHtml_TinyHtml.removeClass(this, ...args);
@@ -7264,15 +7732,18 @@ class TinyHtml_TinyHtml {
7264
7732
  * Set text content of elements.
7265
7733
  * @param {TinyElement|TinyElement[]} el
7266
7734
  * @param {string} value
7735
+ * @returns {TinyElement|TinyElement[]}
7267
7736
  */
7268
7737
  static setText(el, value) {
7269
7738
  if (typeof value !== 'string') throw new Error('Value is not a valid string.');
7270
7739
  TinyHtml_TinyHtml._preElems(el, 'setText').forEach((el) => (el.textContent = value));
7740
+ return el;
7271
7741
  }
7272
7742
 
7273
7743
  /**
7274
7744
  * Set text content of the element.
7275
7745
  * @param {string} value
7746
+ * @returns {TinyElement|TinyElement[]}
7276
7747
  */
7277
7748
  setText(value) {
7278
7749
  return TinyHtml_TinyHtml.setText(this, value);
@@ -7281,13 +7752,16 @@ class TinyHtml_TinyHtml {
7281
7752
  /**
7282
7753
  * Remove all child nodes from each element.
7283
7754
  * @param {TinyElement|TinyElement[]} el
7755
+ * @returns {TinyElement|TinyElement[]}
7284
7756
  */
7285
7757
  static empty(el) {
7286
7758
  TinyHtml_TinyHtml._preElems(el, 'empty').forEach((el) => (el.textContent = ''));
7759
+ return el;
7287
7760
  }
7288
7761
 
7289
7762
  /**
7290
7763
  * Remove all child nodes of the element.
7764
+ * @returns {TinyElement|TinyElement[]}
7291
7765
  */
7292
7766
  empty() {
7293
7767
  return TinyHtml_TinyHtml.empty(this);
@@ -7295,35 +7769,40 @@ class TinyHtml_TinyHtml {
7295
7769
 
7296
7770
  /**
7297
7771
  * Get the innerHTML of the element.
7298
- * @param {TinyElement|TinyElement[]} el
7772
+ * @param {TinyElement} el
7773
+ * @param {GetHTMLOptions} [ops]
7299
7774
  * @returns {string}
7300
7775
  */
7301
- static html(el) {
7776
+ static html(el, ops) {
7302
7777
  const elem = TinyHtml_TinyHtml._preElem(el, 'html');
7303
- return elem.innerHTML;
7778
+ return elem.getHTML(ops);
7304
7779
  }
7305
7780
 
7306
7781
  /**
7307
7782
  * Get the innerHTML of the element.
7783
+ * @param {GetHTMLOptions} [ops]
7308
7784
  * @returns {string}
7309
7785
  */
7310
- html() {
7311
- return TinyHtml_TinyHtml.html(this);
7786
+ html(ops) {
7787
+ return TinyHtml_TinyHtml.html(this, ops);
7312
7788
  }
7313
7789
 
7314
7790
  /**
7315
7791
  * Set the innerHTML of each element.
7316
7792
  * @param {TinyElement|TinyElement[]} el
7317
7793
  * @param {string} value
7794
+ * @returns {TinyElement|TinyElement[]}
7318
7795
  */
7319
7796
  static setHtml(el, value) {
7320
7797
  if (typeof value !== 'string') throw new Error('Value is not a valid string.');
7321
7798
  TinyHtml_TinyHtml._preElems(el, 'setHtml').forEach((el) => (el.innerHTML = value));
7799
+ return el;
7322
7800
  }
7323
7801
 
7324
7802
  /**
7325
7803
  * Set the innerHTML of the element.
7326
7804
  * @param {string} value
7805
+ * @returns {TinyElement|TinyElement[]}
7327
7806
  */
7328
7807
  setHtml(value) {
7329
7808
  return TinyHtml_TinyHtml.setHtml(this, value);
@@ -7457,6 +7936,7 @@ class TinyHtml_TinyHtml {
7457
7936
  * @param {TinyInputElement|TinyInputElement[]} el - Target element.
7458
7937
  * @param {SetValueList|((el: InputElement, val: SetValueList) => SetValueList)} value - The value to assign or a function that returns it.
7459
7938
  * @throws {Error} If the computed value is not a valid string or boolean.
7939
+ * @returns {TinyInputElement|TinyInputElement[]}
7460
7940
  */
7461
7941
  static setVal(el, value) {
7462
7942
  TinyHtml_TinyHtml._preInputElems(el, 'setVal').forEach((elem) => {
@@ -7496,6 +7976,7 @@ class TinyHtml_TinyHtml {
7496
7976
  if (typeof valToSet === 'string') elem.value = valToSet;
7497
7977
  }
7498
7978
  });
7979
+ return el;
7499
7980
  }
7500
7981
 
7501
7982
  /**
@@ -7503,6 +7984,7 @@ class TinyHtml_TinyHtml {
7503
7984
  * Accepts strings, numbers, booleans or arrays of these values, or a callback function that computes them.
7504
7985
  *
7505
7986
  * @param {SetValueList|((el: InputElement, val: SetValueList) => SetValueList)} value - The value to assign or a function that returns it.
7987
+ * @returns {TinyInputElement|TinyInputElement[]}
7506
7988
  * @throws {Error} If the computed value is not a valid string or boolean.
7507
7989
  */
7508
7990
  setVal(value) {
@@ -7772,6 +8254,7 @@ class TinyHtml_TinyHtml {
7772
8254
  * @param {string} event - The event type (e.g. 'click', 'keydown').
7773
8255
  * @param {EventRegistryHandle} handler - The callback function to run on event.
7774
8256
  * @param {EventRegistryOptions} [options] - Optional event listener options.
8257
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7775
8258
  */
7776
8259
  static on(el, event, handler, options) {
7777
8260
  if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
@@ -7784,6 +8267,7 @@ class TinyHtml_TinyHtml {
7784
8267
  if (!Array.isArray(events[event])) events[event] = [];
7785
8268
  events[event].push({ handler, options });
7786
8269
  });
8270
+ return el;
7787
8271
  }
7788
8272
 
7789
8273
  /**
@@ -7792,6 +8276,7 @@ class TinyHtml_TinyHtml {
7792
8276
  * @param {string} event - The event type (e.g. 'click', 'keydown').
7793
8277
  * @param {EventRegistryHandle} handler - The callback function to run on event.
7794
8278
  * @param {EventRegistryOptions} [options] - Optional event listener options.
8279
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7795
8280
  */
7796
8281
  on(event, handler, options) {
7797
8282
  return TinyHtml_TinyHtml.on(this, event, handler, options);
@@ -7804,6 +8289,7 @@ class TinyHtml_TinyHtml {
7804
8289
  * @param {string} event - The event type (e.g. 'click', 'keydown').
7805
8290
  * @param {EventRegistryHandle} handler - The callback function to run on event.
7806
8291
  * @param {EventRegistryOptions} [options={}] - Optional event listener options.
8292
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7807
8293
  */
7808
8294
  static once(el, event, handler, options = {}) {
7809
8295
  if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
@@ -7821,6 +8307,7 @@ class TinyHtml_TinyHtml {
7821
8307
  typeof options === 'boolean' ? options : { ...options, once: true },
7822
8308
  );
7823
8309
  });
8310
+ return el;
7824
8311
  }
7825
8312
 
7826
8313
  /**
@@ -7829,6 +8316,7 @@ class TinyHtml_TinyHtml {
7829
8316
  * @param {string} event - The event type (e.g. 'click', 'keydown').
7830
8317
  * @param {EventRegistryHandle} handler - The callback function to run on event.
7831
8318
  * @param {EventRegistryOptions} [options={}] - Optional event listener options.
8319
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7832
8320
  */
7833
8321
  once(event, handler, options = {}) {
7834
8322
  return TinyHtml_TinyHtml.once(this, event, handler, options);
@@ -7841,6 +8329,7 @@ class TinyHtml_TinyHtml {
7841
8329
  * @param {string} event - The event type.
7842
8330
  * @param {EventRegistryHandle} handler - The function originally bound to the event.
7843
8331
  * @param {boolean|EventListenerOptions} [options] - Optional listener options.
8332
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7844
8333
  */
7845
8334
  static off(el, event, handler, options) {
7846
8335
  if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
@@ -7853,6 +8342,7 @@ class TinyHtml_TinyHtml {
7853
8342
  if (events[event].length === 0) delete events[event];
7854
8343
  }
7855
8344
  });
8345
+ return el;
7856
8346
  }
7857
8347
 
7858
8348
  /**
@@ -7861,6 +8351,7 @@ class TinyHtml_TinyHtml {
7861
8351
  * @param {string} event - The event type.
7862
8352
  * @param {EventRegistryHandle} handler - The function originally bound to the event.
7863
8353
  * @param {boolean|EventListenerOptions} [options] - Optional listener options.
8354
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7864
8355
  */
7865
8356
  off(event, handler, options) {
7866
8357
  return TinyHtml_TinyHtml.off(this, event, handler, options);
@@ -7871,6 +8362,7 @@ class TinyHtml_TinyHtml {
7871
8362
  *
7872
8363
  * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
7873
8364
  * @param {string} event - The event type to remove (e.g. 'click').
8365
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7874
8366
  */
7875
8367
  static offAll(el, event) {
7876
8368
  if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
@@ -7883,12 +8375,14 @@ class TinyHtml_TinyHtml {
7883
8375
  delete events[event];
7884
8376
  }
7885
8377
  });
8378
+ return el;
7886
8379
  }
7887
8380
 
7888
8381
  /**
7889
8382
  * Removes all event listeners of a specific type from the element.
7890
8383
  *
7891
8384
  * @param {string} event - The event type to remove (e.g. 'click').
8385
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7892
8386
  */
7893
8387
  offAll(event) {
7894
8388
  return TinyHtml_TinyHtml.offAll(this, event);
@@ -7900,6 +8394,7 @@ class TinyHtml_TinyHtml {
7900
8394
  * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
7901
8395
  * @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
7902
8396
  * Optional filter function to selectively remove specific handlers.
8397
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7903
8398
  */
7904
8399
  static offAllTypes(el, filterFn = null) {
7905
8400
  if (filterFn !== null && typeof filterFn !== 'function')
@@ -7918,6 +8413,7 @@ class TinyHtml_TinyHtml {
7918
8413
 
7919
8414
  __eventRegistry.delete(elem);
7920
8415
  });
8416
+ return el;
7921
8417
  }
7922
8418
 
7923
8419
  /**
@@ -7925,6 +8421,7 @@ class TinyHtml_TinyHtml {
7925
8421
  *
7926
8422
  * @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
7927
8423
  * Optional filter function to selectively remove specific handlers.
8424
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7928
8425
  */
7929
8426
  offAllTypes(filterFn = null) {
7930
8427
  return TinyHtml_TinyHtml.offAllTypes(this, filterFn);
@@ -7936,6 +8433,7 @@ class TinyHtml_TinyHtml {
7936
8433
  * @param {TinyEventTarget|TinyEventTarget[]} el - Target element where the event should be triggered.
7937
8434
  * @param {string} event - Name of the event to trigger.
7938
8435
  * @param {Event|CustomEvent|CustomEventInit} [payload] - Optional event object or data to pass.
8436
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7939
8437
  */
7940
8438
  static trigger(el, event, payload = {}) {
7941
8439
  if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
@@ -7951,6 +8449,7 @@ class TinyHtml_TinyHtml {
7951
8449
 
7952
8450
  elem.dispatchEvent(evt);
7953
8451
  });
8452
+ return el;
7954
8453
  }
7955
8454
 
7956
8455
  /**
@@ -7958,6 +8457,7 @@ class TinyHtml_TinyHtml {
7958
8457
  *
7959
8458
  * @param {string} event - Name of the event to trigger.
7960
8459
  * @param {Event|CustomEvent|CustomEventInit} [payload] - Optional event object or data to pass.
8460
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7961
8461
  */
7962
8462
  trigger(event, payload = {}) {
7963
8463
  return TinyHtml_TinyHtml.trigger(this, event, payload);
@@ -8000,6 +8500,7 @@ class TinyHtml_TinyHtml {
8000
8500
  * @param {TinyElement|TinyElement[]} el
8001
8501
  * @param {string} name
8002
8502
  * @param {string|null} [value=null]
8503
+ * @returns {TinyElement|TinyElement[]}
8003
8504
  */
8004
8505
  static setAttr(el, name, value = null) {
8005
8506
  if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
@@ -8009,12 +8510,14 @@ class TinyHtml_TinyHtml {
8009
8510
  if (value === null) elem.removeAttribute(name);
8010
8511
  else elem.setAttribute(name, value);
8011
8512
  });
8513
+ return el;
8012
8514
  }
8013
8515
 
8014
8516
  /**
8015
8517
  * Set an attribute on an element.
8016
8518
  * @param {string} name
8017
8519
  * @param {string|null} [value=null]
8520
+ * @returns {TinyElement|TinyElement[]}
8018
8521
  */
8019
8522
  setAttr(name, value) {
8020
8523
  return TinyHtml_TinyHtml.setAttr(this, name, value);
@@ -8024,15 +8527,18 @@ class TinyHtml_TinyHtml {
8024
8527
  * Remove attribute(s) from an element.
8025
8528
  * @param {TinyElement|TinyElement[]} el
8026
8529
  * @param {string} name Space-separated list of attributes.
8530
+ * @returns {TinyElement|TinyElement[]}
8027
8531
  */
8028
8532
  static removeAttr(el, name) {
8029
8533
  if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
8030
8534
  TinyHtml_TinyHtml._preElems(el, 'removeAttr').forEach((elem) => elem.removeAttribute(name));
8535
+ return el;
8031
8536
  }
8032
8537
 
8033
8538
  /**
8034
8539
  * Remove attribute(s) from an element.
8035
8540
  * @param {string} name Space-separated list of attributes.
8541
+ * @returns {TinyElement|TinyElement[]}
8036
8542
  */
8037
8543
  removeAttr(name) {
8038
8544
  return TinyHtml_TinyHtml.removeAttr(this, name);
@@ -8087,6 +8593,7 @@ class TinyHtml_TinyHtml {
8087
8593
  * Set a property on an element.
8088
8594
  * @param {TinyElement|TinyElement[]} el
8089
8595
  * @param {string} name
8596
+ * @returns {TinyElement|TinyElement[]}
8090
8597
  */
8091
8598
  static addProp(el, name) {
8092
8599
  if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
@@ -8096,11 +8603,13 @@ class TinyHtml_TinyHtml {
8096
8603
  // @ts-ignore
8097
8604
  elem[name] = true;
8098
8605
  });
8606
+ return el;
8099
8607
  }
8100
8608
 
8101
8609
  /**
8102
8610
  * Set a property on an element.
8103
8611
  * @param {string} name
8612
+ * @returns {TinyElement|TinyElement[]}
8104
8613
  */
8105
8614
  addProp(name) {
8106
8615
  return TinyHtml_TinyHtml.addProp(this, name);
@@ -8110,6 +8619,7 @@ class TinyHtml_TinyHtml {
8110
8619
  * Remove a property from an element.
8111
8620
  * @param {TinyElement|TinyElement[]} el
8112
8621
  * @param {string} name
8622
+ * @returns {TinyElement|TinyElement[]}
8113
8623
  */
8114
8624
  static removeProp(el, name) {
8115
8625
  if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
@@ -8119,11 +8629,13 @@ class TinyHtml_TinyHtml {
8119
8629
  // @ts-ignore
8120
8630
  elem[name] = false;
8121
8631
  });
8632
+ return el;
8122
8633
  }
8123
8634
 
8124
8635
  /**
8125
8636
  * Remove a property from an element.
8126
8637
  * @param {string} name
8638
+ * @returns {TinyElement|TinyElement[]}
8127
8639
  */
8128
8640
  removeProp(name) {
8129
8641
  return TinyHtml_TinyHtml.removeProp(this, name);
@@ -8164,13 +8676,16 @@ class TinyHtml_TinyHtml {
8164
8676
  /**
8165
8677
  * Removes an element from the DOM.
8166
8678
  * @param {TinyElement|TinyElement[]} el - The DOM element or selector to remove.
8679
+ * @returns {TinyElement|TinyElement[]}
8167
8680
  */
8168
8681
  static remove(el) {
8169
8682
  TinyHtml_TinyHtml._preElems(el, 'remove').forEach((elem) => elem.remove());
8683
+ return el;
8170
8684
  }
8171
8685
 
8172
8686
  /**
8173
8687
  * Removes the element from the DOM.
8688
+ * @returns {TinyElement|TinyElement[]}
8174
8689
  */
8175
8690
  remove() {
8176
8691
  return TinyHtml_TinyHtml.remove(this);
@@ -8514,6 +9029,14 @@ class TinyHtml_TinyHtml {
8514
9029
  */
8515
9030
  static isInViewport(el) {
8516
9031
  const elem = TinyHtml_TinyHtml._preElem(el, 'isInViewport');
9032
+ if (
9033
+ !elem.checkVisibility({
9034
+ contentVisibilityAuto: false,
9035
+ opacityProperty: false,
9036
+ visibilityProperty: false,
9037
+ })
9038
+ )
9039
+ return false;
8517
9040
  const elementTop = TinyHtml_TinyHtml.offset(elem).top;
8518
9041
  const elementBottom = elementTop + TinyHtml_TinyHtml.outerHeight(elem);
8519
9042
 
@@ -8540,6 +9063,14 @@ class TinyHtml_TinyHtml {
8540
9063
  */
8541
9064
  static isScrolledIntoView(el) {
8542
9065
  const elem = TinyHtml_TinyHtml._preElem(el, 'isScrolledIntoView');
9066
+ if (
9067
+ !elem.checkVisibility({
9068
+ contentVisibilityAuto: false,
9069
+ opacityProperty: false,
9070
+ visibilityProperty: false,
9071
+ })
9072
+ )
9073
+ return false;
8543
9074
  const docViewTop = TinyHtml_TinyHtml.scrollTop(window);
8544
9075
  const docViewBottom = docViewTop + TinyHtml_TinyHtml.height(window);
8545
9076
 
@@ -8557,36 +9088,141 @@ class TinyHtml_TinyHtml {
8557
9088
  isScrolledIntoView() {
8558
9089
  return TinyHtml_TinyHtml.isScrolledIntoView(this);
8559
9090
  }
8560
- }
8561
9091
 
8562
- /* harmony default export */ const libs_TinyHtml = (TinyHtml_TinyHtml);
9092
+ /**
9093
+ * Checks if the given element is at least partially visible within the boundaries of a container.
9094
+ *
9095
+ * @param {TinyElement} el - The DOM element to check.
9096
+ * @param {TinyElement} cont - The container element acting as the viewport.
9097
+ * @returns {boolean} True if the element is partially visible within the container, false otherwise.
9098
+ */
9099
+ static isInContainer(el, cont) {
9100
+ const elem = TinyHtml_TinyHtml._preElem(el, 'isInContainer');
9101
+ const container = TinyHtml_TinyHtml._preElem(cont, 'isInContainer');
8563
9102
 
8564
- ;// ./src/v1/basics/html_deprecated.mjs
9103
+ if (
9104
+ !elem.checkVisibility({
9105
+ contentVisibilityAuto: false,
9106
+ opacityProperty: false,
9107
+ visibilityProperty: false,
9108
+ })
9109
+ )
9110
+ return false;
9111
+ const elemRect = elem.getBoundingClientRect();
9112
+ const containerRect = container.getBoundingClientRect();
8565
9113
 
9114
+ const verticallyVisible =
9115
+ elemRect.bottom > containerRect.top && elemRect.top < containerRect.bottom;
8566
9116
 
8567
- /**
8568
- * Checks if two DOM elements are colliding on the screen.
8569
- *
8570
- * @param {Element} elem1 - First DOM element.
8571
- * @param {Element} elem2 - Second DOM element.
8572
- * @returns {boolean} - Returns true if the elements are colliding.
8573
- * @deprecated - Use TinyHtml.isCollWith instead.
8574
- */
8575
- function areHtmlElsColliding(elem1, elem2) {
8576
- return TinyHtml.isCollWith(elem1, elem2);
8577
- }
9117
+ const horizontallyVisible =
9118
+ elemRect.right > containerRect.left && elemRect.left < containerRect.right;
8578
9119
 
8579
- /**
8580
- * Checks if two DOM elements are colliding on the screen.
8581
- *
8582
- * @param {Element} elem1 - First DOM element.
8583
- * @param {Element} elem2 - Second DOM element.
8584
- * @returns {boolean} - Returns true if the elements are colliding.
8585
- * @deprecated - Use TinyHtml.isCollPerfWith instead.
8586
- */
8587
- function areHtmlElsPerfColliding(elem1, elem2) {
8588
- return TinyHtml.isCollPerfWith(elem1, elem2);
8589
- }
9120
+ return verticallyVisible && horizontallyVisible;
9121
+ }
9122
+
9123
+ /**
9124
+ * Checks if the given element is at least partially visible within the boundaries of a container.
9125
+ *
9126
+ * @param {TinyElement} cont - The container element acting as the viewport.
9127
+ * @returns {boolean} True if the element is partially visible within the container, false otherwise.
9128
+ */
9129
+ isInContainer(cont) {
9130
+ return TinyHtml_TinyHtml.isInContainer(this, cont);
9131
+ }
9132
+
9133
+ /**
9134
+ * Checks if the given element is fully visible within the boundaries of a container (top and bottom).
9135
+ *
9136
+ * @param {TinyElement} el - The DOM element to check.
9137
+ * @param {TinyElement} cont - The container element acting as the viewport.
9138
+ * @returns {boolean} True if the element is fully visible within the container, false otherwise.
9139
+ */
9140
+ static isFullyInContainer(el, cont) {
9141
+ const elem = TinyHtml_TinyHtml._preElem(el, 'isScrolledIntoView');
9142
+ const container = TinyHtml_TinyHtml._preElem(cont, 'isInContainer');
9143
+
9144
+ if (
9145
+ !elem.checkVisibility({
9146
+ contentVisibilityAuto: false,
9147
+ opacityProperty: false,
9148
+ visibilityProperty: false,
9149
+ })
9150
+ )
9151
+ return false;
9152
+ const elemRect = elem.getBoundingClientRect();
9153
+ const containerRect = container.getBoundingClientRect();
9154
+
9155
+ const isFullyVisible =
9156
+ elemRect.top >= containerRect.top &&
9157
+ elemRect.bottom <= containerRect.bottom &&
9158
+ elemRect.left >= containerRect.left &&
9159
+ elemRect.right <= containerRect.right;
9160
+
9161
+ return isFullyVisible;
9162
+ }
9163
+
9164
+ /**
9165
+ * Checks if the given element is fully visible within the boundaries of a container (top and bottom).
9166
+ *
9167
+ * @param {TinyElement} cont - The container element acting as the viewport.
9168
+ * @returns {boolean} True if the element is fully visible within the container, false otherwise.
9169
+ */
9170
+ isFullyInContainer(cont) {
9171
+ return TinyHtml_TinyHtml.isFullyInContainer(this, cont);
9172
+ }
9173
+
9174
+ /**
9175
+ * Checks if an element has scrollable content.
9176
+ *
9177
+ * @param {TinyElement} el - The DOM element to check.
9178
+ * @returns {{ v: boolean, h: boolean }} - True if scroll is needed in that direction.
9179
+ */
9180
+ static hasScroll(el) {
9181
+ const elem = TinyHtml_TinyHtml._preElem(el, 'hasScroll');
9182
+ return {
9183
+ v: elem.scrollHeight > elem.clientHeight,
9184
+ h: elem.scrollWidth > elem.clientWidth,
9185
+ };
9186
+ }
9187
+
9188
+ /**
9189
+ * Checks if an element has scrollable content.
9190
+ *
9191
+ * @returns {{ v: boolean, h: boolean }} - True if scroll is needed in that direction.
9192
+ */
9193
+ hasScroll() {
9194
+ return TinyHtml_TinyHtml.hasScroll(this);
9195
+ }
9196
+ }
9197
+
9198
+ /* harmony default export */ const libs_TinyHtml = (TinyHtml_TinyHtml);
9199
+
9200
+ ;// ./src/v1/basics/html_deprecated.mjs
9201
+
9202
+
9203
+ /**
9204
+ * Checks if two DOM elements are colliding on the screen.
9205
+ *
9206
+ * @param {Element} elem1 - First DOM element.
9207
+ * @param {Element} elem2 - Second DOM element.
9208
+ * @returns {boolean} - Returns true if the elements are colliding.
9209
+ * @deprecated - Use TinyHtml.isCollWith instead.
9210
+ */
9211
+ function areHtmlElsColliding(elem1, elem2) {
9212
+ return TinyHtml.isCollWith(elem1, elem2);
9213
+ }
9214
+
9215
+ /**
9216
+ * Checks if two DOM elements are colliding on the screen.
9217
+ *
9218
+ * @param {Element} elem1 - First DOM element.
9219
+ * @param {Element} elem2 - Second DOM element.
9220
+ * @returns {boolean} - Returns true if the elements are colliding.
9221
+ * @deprecated - Use TinyHtml.isCollPerfWith instead.
9222
+ */
9223
+ function areHtmlElsPerfColliding(elem1, elem2) {
9224
+ return TinyHtml.isCollPerfWith(elem1, elem2);
9225
+ }
8590
9226
 
8591
9227
  ///////////////////////////////////////////////////////////////////////////
8592
9228
 
@@ -10327,6 +10963,982 @@ class TinyDragger {
10327
10963
 
10328
10964
  /* harmony default export */ const libs_TinyDragger = ((/* unused pure expression or super */ null && (TinyDragger)));
10329
10965
 
10966
+ ;// ./src/v1/libs/TinySmartScroller.mjs
10967
+
10968
+
10969
+
10970
+ /**
10971
+ * Represents the dimensions of a DOM element.
10972
+ *
10973
+ * @typedef {Object} NodeSizes
10974
+ * @property {number} height - The height of the element in pixels.
10975
+ * @property {number} width - The width of the element in pixels.
10976
+ */
10977
+
10978
+ /**
10979
+ * A callback function that receives node size change data and optionally returns a modified NodeSizes object.
10980
+ *
10981
+ * @callback NodeSizesEvent
10982
+ * @param {Element} elem - The DOM element whose size is being tracked.
10983
+ * @param {{ old: NodeSizes, now: NodeSizes }} sizes - The old and new size measurements of the element.
10984
+ * @param {{ old: number, now: number }} elemAmount - The number of matching elements before and after the update.
10985
+ * @returns {NodeSizes|undefined} A modified NodeSizes object to override the default measurement, or undefined to use the original.
10986
+ */
10987
+
10988
+ /**
10989
+ * A generic scroll-related event listener callback function.
10990
+ *
10991
+ * @callback ScrollListenersFunc
10992
+ * @param {any} payload - The data payload passed when the scroll event is triggered. The type may vary depending on the event.
10993
+ * @returns {void}
10994
+ */
10995
+
10996
+ /**
10997
+ * TinySmartScroller is a utility class designed to enhance and manage scroll behaviors within containers or the window.
10998
+ *
10999
+ * It enables advanced scroll monitoring, auto-scrolling to bottom, preserving scroll position during DOM changes,
11000
+ * and detecting visibility changes of elements. This is particularly useful for dynamic UIs like chat applications,
11001
+ * feed viewers, or live content containers.
11002
+ *
11003
+ * Features:
11004
+ * - Detects when the scroll reaches the top, bottom, or custom boundaries
11005
+ * - Supports automatic scrolling to the bottom unless the user scrolls away
11006
+ * - Observes DOM mutations and resizes, and adapts scroll position accordingly
11007
+ * - Emits scroll-related events such as 'onScrollBoundary', 'onAutoScroll', and 'onScrollPause'
11008
+ * - Includes customizable scroll correction filters for layout shift mitigation
11009
+ * - Handles media element load events (e.g. `<img>`, `<iframe>`, `<video>`) to prevent sudden scroll jumps
11010
+ *
11011
+ * This class is **not framework-dependent** and works with vanilla DOM elements and the window object.
11012
+ */
11013
+ class TinySmartScroller {
11014
+ static Utils = { ...collision_namespaceObject, TinyHtml: libs_TinyHtml };
11015
+
11016
+ /** @type {WeakMap<Element, NodeSizes>} */
11017
+ #oldSizes = new WeakMap();
11018
+ /** @type {WeakMap<Element, NodeSizes>} */
11019
+ #newSizes = new WeakMap();
11020
+
11021
+ /** @type {WeakMap<Element, boolean>} */
11022
+ #newVisibles = new WeakMap();
11023
+ /** @type {WeakMap<Element, boolean>} */
11024
+ #oldVisibles = new WeakMap();
11025
+
11026
+ /** @type {WeakMap<Element, boolean>} */
11027
+ #newVisiblesByTime = new WeakMap();
11028
+ /** @type {WeakMap<Element, boolean>} */
11029
+ #oldVisiblesByTime = new WeakMap();
11030
+
11031
+ /** @type {Record<string, ScrollListenersFunc[]>} */
11032
+ #scrollListeners = {};
11033
+
11034
+ /** @type {ResizeObserver|null} */
11035
+ #resizeObserver = null;
11036
+ /** @type {MutationObserver|null} */
11037
+ #mutationObserver = null;
11038
+
11039
+ /** @type {Set<string>} */
11040
+ #loadTags = new Set(['IMG', 'IFRAME', 'VIDEO']);
11041
+
11042
+ /** @type {null|EventListenerOrEventListenerObject} */
11043
+ #handler = null;
11044
+
11045
+ #isAtBottom = false;
11046
+ #isAtTop = false;
11047
+ #isAtCustomTop = false;
11048
+ #isAtCustomBottom = false;
11049
+
11050
+ #querySelector = '';
11051
+ #useWindow = false;
11052
+ #destroyed = false;
11053
+ #scrollPaused = false;
11054
+ #autoScrollBottom = false;
11055
+ #observeMutations = false;
11056
+ #preserveScrollOnLayoutShift = false;
11057
+ #debounceTime = 0;
11058
+ #elemAmount = 0;
11059
+ #elemOldAmount = 0;
11060
+ #lastKnownScrollBottomOffset = 0;
11061
+ #extraScrollBoundary = 0;
11062
+
11063
+ /** @type {Set<string>} */
11064
+ #attributeFilter;
11065
+
11066
+ /** @type {Element} */
11067
+ #target;
11068
+
11069
+ /** @type {Set<NodeSizesEvent>} */
11070
+ #sizeFilter = new Set();
11071
+
11072
+ /**
11073
+ * Creates a new instance of TinySmartScroller, attaching scroll and resize observers to manage
11074
+ * automatic scroll behaviors, layout shift correction, and visibility tracking.
11075
+ *
11076
+ * @param {Element|Window} target - The scroll container to monitor. Can be an element or `window`.
11077
+ * @param {Object} [options={}] - Optional settings to configure scroll behavior.
11078
+ * @param {number} [options.extraScrollBoundary=0] - Extra margin in pixels to extend scroll boundary detection.
11079
+ * @param {boolean} [options.autoScrollBottom=true] - Whether to auto-scroll to bottom on layout updates.
11080
+ * @param {boolean} [options.observeMutations=true] - Enables MutationObserver to detect DOM changes.
11081
+ * @param {boolean} [options.preserveScrollOnLayoutShift=true] - Prevents scroll jumps when layout changes.
11082
+ * @param {number} [options.debounceTime=100] - Debounce time in milliseconds for scroll events.
11083
+ * @param {string|null} [options.querySelector=null] - Optional CSS selector to filter observed child nodes.
11084
+ * @param {string[]|Set<string>|null} [options.attributeFilter=['class', 'style', 'src', 'data-*', 'height', 'width']]
11085
+ * - Which attributes to observe for changes.
11086
+ */
11087
+ constructor(
11088
+ target,
11089
+ {
11090
+ extraScrollBoundary = 0,
11091
+ autoScrollBottom = true,
11092
+ observeMutations = true,
11093
+ preserveScrollOnLayoutShift = true,
11094
+ debounceTime = 100,
11095
+ querySelector = null,
11096
+ attributeFilter = ['class', 'style', 'src', 'data-*', 'height', 'width'],
11097
+ } = {},
11098
+ ) {
11099
+ // === target ===
11100
+ if (!(target instanceof Element || target === window))
11101
+ throw new TypeError(
11102
+ `TinySmartScroller: 'target' must be a DOM Element or 'window', but got ${typeof target}`,
11103
+ );
11104
+ // === extraScrollBoundary ===
11105
+ if (typeof extraScrollBoundary !== 'number' || Number.isNaN(extraScrollBoundary))
11106
+ throw new TypeError(
11107
+ `TinySmartScroller: 'extraScrollBoundary' must be a valid number, received ${extraScrollBoundary}`,
11108
+ );
11109
+ // === autoScrollBottom ===
11110
+ if (typeof autoScrollBottom !== 'boolean')
11111
+ throw new TypeError(
11112
+ `TinySmartScroller: 'autoScrollBottom' must be a boolean, received ${typeof autoScrollBottom}`,
11113
+ );
11114
+ // === observeMutations ===
11115
+ if (typeof observeMutations !== 'boolean')
11116
+ throw new TypeError(
11117
+ `TinySmartScroller: 'observeMutations' must be a boolean, received ${typeof observeMutations}`,
11118
+ );
11119
+ // === preserveScrollOnLayoutShift ===
11120
+ if (typeof preserveScrollOnLayoutShift !== 'boolean')
11121
+ throw new TypeError(
11122
+ `TinySmartScroller: 'preserveScrollOnLayoutShift' must be a boolean, received ${typeof preserveScrollOnLayoutShift}`,
11123
+ );
11124
+ // === debounceTime ===
11125
+ if (typeof debounceTime !== 'number' || debounceTime < 0 || Number.isNaN(debounceTime))
11126
+ throw new TypeError(
11127
+ `TinySmartScroller: 'debounceTime' must be a non-negative number, received ${debounceTime}`,
11128
+ );
11129
+ // === querySelector ===
11130
+ if (querySelector !== null && typeof querySelector !== 'string')
11131
+ throw new TypeError(
11132
+ `TinySmartScroller: 'querySelector' must be a string or null, received ${typeof querySelector}`,
11133
+ );
11134
+ // === attributeFilter ===
11135
+ const isValidAttrList =
11136
+ attributeFilter === null || Array.isArray(attributeFilter) || attributeFilter instanceof Set;
11137
+ if (!isValidAttrList)
11138
+ throw new TypeError(
11139
+ `TinySmartScroller: 'attributeFilter' must be an array, Set, or null. Got ${typeof attributeFilter}`,
11140
+ );
11141
+
11142
+ // Start values
11143
+ this.#target = target instanceof Window ? document.documentElement : target;
11144
+ this.#useWindow = target instanceof Window;
11145
+ this.#autoScrollBottom = autoScrollBottom;
11146
+ this.#observeMutations = observeMutations;
11147
+ this.#preserveScrollOnLayoutShift = preserveScrollOnLayoutShift;
11148
+ this.#debounceTime = debounceTime;
11149
+ this.#extraScrollBoundary = extraScrollBoundary;
11150
+ this.#querySelector = querySelector || '';
11151
+ this.#attributeFilter = new Set(attributeFilter || undefined);
11152
+
11153
+ // Bind scroll
11154
+ /** @type {NodeJS.Timeout} */
11155
+ let timeout;
11156
+ this.#handler = () => {
11157
+ this._scrollDataUpdater();
11158
+ clearTimeout(timeout);
11159
+ timeout = setTimeout(() => this._onScroll(), this.#debounceTime);
11160
+ };
11161
+ (this.#useWindow ? window : this.#target).addEventListener('scroll', this.#handler, {
11162
+ passive: true,
11163
+ });
11164
+
11165
+ // Mutations
11166
+ if (this.#observeMutations) {
11167
+ this._observeMutations();
11168
+ this._observeResizes(this.#target.children);
11169
+ }
11170
+
11171
+ this._scrollDataUpdater();
11172
+ }
11173
+
11174
+ /**
11175
+ * Returns a size difference callback that only reacts when height changes, filtered by tag name.
11176
+ *
11177
+ * @param {string[]} filter - List of tag names to allow. If empty, all tags are accepted.
11178
+ * @returns {NodeSizesEvent} A function that compares previous and current height, returning height delta.
11179
+ */
11180
+ getSimpleOnHeight(filter = []) {
11181
+ if (!Array.isArray(filter))
11182
+ throw new TypeError('getSimpleOnHeight(filter): filter must be an array of tag names');
11183
+ return (elem, sizes, amounts) => {
11184
+ if ((filter.length > 0 && !filter.includes(elem.tagName)) || amounts.now !== amounts.old)
11185
+ return;
11186
+ const oldSize = sizes.old;
11187
+ const newSize = sizes.now;
11188
+ const height = newSize.height - oldSize.height;
11189
+ return { height, width: 0 };
11190
+ };
11191
+ }
11192
+
11193
+ /**
11194
+ * Adds a height difference callback to the size filter system.
11195
+ *
11196
+ * @param {string[]} filter - List of tag names to allow.
11197
+ * @returns {NodeSizesEvent} The added size difference callback.
11198
+ */
11199
+ addSimpleOnHeight(filter) {
11200
+ if (!Array.isArray(filter))
11201
+ throw new TypeError('addSimpleOnHeight(filter): filter must be an array of tag names');
11202
+ const result = this.getSimpleOnHeight(filter);
11203
+ this.onSize(result);
11204
+ return result;
11205
+ }
11206
+
11207
+ /**
11208
+ * Returns a list of all currently tracked load tags.
11209
+ *
11210
+ * @returns {string[]} Array of tag names.
11211
+ */
11212
+ getLoadTags() {
11213
+ return Array.from(this.#loadTags);
11214
+ }
11215
+
11216
+ /**
11217
+ * Adds a new tag to the set of load tags.
11218
+ *
11219
+ * @param {string} tag - The tag name to add (e.g., 'IMG').
11220
+ */
11221
+ addLoadTag(tag) {
11222
+ if (typeof tag !== 'string') throw new TypeError('addLoadTag(tag): tag must be a string');
11223
+ this.#loadTags.add(tag.toUpperCase());
11224
+ }
11225
+
11226
+ /**
11227
+ * Removes a tag from the set of load tags.
11228
+ *
11229
+ * @param {string} tag - The tag name to remove.
11230
+ */
11231
+ removeLoadTag(tag) {
11232
+ if (typeof tag !== 'string') throw new TypeError('removeLoadTag(tag): tag must be a string');
11233
+ this.#loadTags.delete(tag.toUpperCase());
11234
+ }
11235
+
11236
+ /**
11237
+ * Checks whether a tag is tracked as a load tag.
11238
+ *
11239
+ * @param {string} tag - The tag name to check.
11240
+ * @returns {boolean} True if the tag is being tracked.
11241
+ */
11242
+ hasLoadTag(tag) {
11243
+ if (typeof tag !== 'string') throw new TypeError('hasLoadTag(tag): tag must be a string');
11244
+ return this.#loadTags.has(tag.toUpperCase());
11245
+ }
11246
+
11247
+ /**
11248
+ * Clears the set of load tags. If `addDefault` is true, it will reset to the default tags: 'IMG', 'IFRAME', and 'VIDEO'.
11249
+ *
11250
+ * @param {boolean} [addDefault=false] - Whether to restore the default tags after clearing.
11251
+ * @throws {TypeError} If `addDefault` is not a boolean.
11252
+ */
11253
+ resetLoadTags(addDefault = false) {
11254
+ if (typeof addDefault !== 'boolean')
11255
+ throw new TypeError('resetLoadTags(addDefault): addDefault must be a boolean');
11256
+ this.#loadTags.clear();
11257
+ if (!addDefault) return;
11258
+ this.#loadTags.add('IMG');
11259
+ this.#loadTags.add('IFRAME');
11260
+ this.#loadTags.add('VIDEO');
11261
+ }
11262
+
11263
+ /**
11264
+ * Returns a list of all currently tracked attribute filters.
11265
+ *
11266
+ * @returns {string[]} Array of attribute names.
11267
+ */
11268
+ getAttributeFilters() {
11269
+ return Array.from(this.#attributeFilter);
11270
+ }
11271
+
11272
+ /**
11273
+ * Adds an attribute to the filter list.
11274
+ *
11275
+ * @param {string} attr - The attribute name to add.
11276
+ */
11277
+ addAttributeFilter(attr) {
11278
+ if (typeof attr !== 'string')
11279
+ throw new TypeError('addAttributeFilter(attr): attr must be a string');
11280
+ this.#attributeFilter.add(attr);
11281
+ }
11282
+
11283
+ /**
11284
+ * Removes an attribute from the filter list.
11285
+ *
11286
+ * @param {string} attr - The attribute name to remove.
11287
+ */
11288
+ removeAttributeFilter(attr) {
11289
+ if (typeof attr !== 'string')
11290
+ throw new TypeError('removeAttributeFilter(attr): attr must be a string');
11291
+ this.#attributeFilter.delete(attr);
11292
+ }
11293
+
11294
+ /**
11295
+ * Checks whether a specific attribute is being filtered.
11296
+ *
11297
+ * @param {string} attr - The attribute name to check.
11298
+ * @returns {boolean} True if the attribute is being filtered.
11299
+ */
11300
+ hasAttributeFilter(attr) {
11301
+ if (typeof attr !== 'string')
11302
+ throw new TypeError('hasAttributeFilter(attr): attr must be a string');
11303
+ return this.#attributeFilter.has(attr);
11304
+ }
11305
+
11306
+ /**
11307
+ * Clears the set of observed attribute filters. If `addDefault` is true, it will reset to the default attributes:
11308
+ * 'class', 'style', 'src', 'data-*', 'height', and 'width'.
11309
+ *
11310
+ * @param {boolean} [addDefault=false] - Whether to restore the default attribute filters after clearing.
11311
+ * @throws {TypeError} If `addDefault` is not a boolean.
11312
+ */
11313
+ resetAttributeFilters(addDefault = false) {
11314
+ if (typeof addDefault !== 'boolean')
11315
+ throw new TypeError('resetAttributeFilters(addDefault): addDefault must be a boolean');
11316
+ this.#attributeFilter.clear();
11317
+ if (!addDefault) return;
11318
+ ['class', 'style', 'src', 'data-*', 'height', 'width'].forEach((attr) =>
11319
+ this.#attributeFilter.add(attr),
11320
+ );
11321
+ }
11322
+
11323
+ /**
11324
+ * Registers a custom node size change handler to the internal size filter set.
11325
+ *
11326
+ * @param {NodeSizesEvent} handler - Function that compares old and new sizes.
11327
+ */
11328
+ onSize(handler) {
11329
+ if (this.#destroyed) return;
11330
+ if (typeof handler !== 'function')
11331
+ throw new TypeError('onSize(handler): handler must be a function');
11332
+ this.#sizeFilter.add(handler);
11333
+ }
11334
+
11335
+ /**
11336
+ * Unregisters a previously registered size handler from the internal filter set.
11337
+ *
11338
+ * @param {NodeSizesEvent} handler - The handler function to remove.
11339
+ */
11340
+ offSize(handler) {
11341
+ if (this.#destroyed) return;
11342
+ if (typeof handler !== 'function')
11343
+ throw new TypeError('offSize(handler): handler must be a function');
11344
+ this.#sizeFilter.delete(handler);
11345
+ }
11346
+
11347
+ /**
11348
+ * Adds a scroll-related event listener.
11349
+ *
11350
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
11351
+ * @param {ScrollListenersFunc} handler - Callback function to be called when event fires.
11352
+ */
11353
+ on(event, handler) {
11354
+ if (this.#destroyed) return;
11355
+ if (typeof event !== 'string')
11356
+ throw new TypeError('on(event, handler): event name must be a string');
11357
+ if (typeof handler !== 'function')
11358
+ throw new TypeError('on(event, handler): handler must be a function');
11359
+
11360
+ if (!this.#scrollListeners[event]) this.#scrollListeners[event] = [];
11361
+ this.#scrollListeners[event].push(handler);
11362
+ }
11363
+
11364
+ /**
11365
+ * Removes a previously registered scroll-related event listener.
11366
+ *
11367
+ * @param {string} event - The name of the event to remove the handler from.
11368
+ * @param {ScrollListenersFunc} handler - The specific callback function to remove.
11369
+ */
11370
+ off(event, handler) {
11371
+ if (this.#destroyed) return;
11372
+ if (typeof event !== 'string')
11373
+ throw new TypeError('off(event, handler): event name must be a string');
11374
+ if (typeof handler !== 'function')
11375
+ throw new TypeError('off(event, handler): handler must be a function');
11376
+
11377
+ const listeners = this.#scrollListeners[event];
11378
+ if (!Array.isArray(listeners)) return;
11379
+
11380
+ const index = listeners.indexOf(handler);
11381
+ if (index !== -1) listeners.splice(index, 1);
11382
+
11383
+ // Optionally clean up empty arrays (optional)
11384
+ if (listeners.length === 0) delete this.#scrollListeners[event];
11385
+ }
11386
+
11387
+ /**
11388
+ * Checks which elements inside the target are currently visible and updates internal maps.
11389
+ *
11390
+ * @returns {Map<Element, { oldIsVisible: boolean; isVisible: boolean }>} Visibility comparison results.
11391
+ */
11392
+ _scrollDataUpdater() {
11393
+ const results = new Map();
11394
+ this.#target.querySelectorAll(this.#querySelector || '*').forEach((target) => {
11395
+ const oldIsVisible = this.#newVisibles.get(target) ?? false;
11396
+ this.#oldVisibles.set(target, oldIsVisible);
11397
+
11398
+ const isVisible = libs_TinyHtml.isInContainer(this.#target, target);
11399
+ this.#newVisibles.set(target, isVisible);
11400
+
11401
+ results.set(target, { oldIsVisible, isVisible });
11402
+ });
11403
+ return results;
11404
+ }
11405
+
11406
+ /**
11407
+ * Emits a scroll-related event to all registered listeners.
11408
+ *
11409
+ * @param {string} event - Event name.
11410
+ * @param {*} [payload] - Optional event data payload.
11411
+ */
11412
+ _emit(event, payload) {
11413
+ if (this.#destroyed) return;
11414
+ if (typeof event !== 'string')
11415
+ throw new TypeError('_emit(event, payload): event name must be a string');
11416
+ (this.#scrollListeners[event] || []).forEach((fn) => fn(payload));
11417
+ }
11418
+
11419
+ /**
11420
+ * Handles scroll events, calculates position-related statuses, and emits appropriate events.
11421
+ */
11422
+ _onScroll() {
11423
+ if (this.#destroyed) return;
11424
+ // Get values
11425
+ const scrollCache = this._scrollDataUpdater();
11426
+ const el = this.#target;
11427
+ const scrollTop = el.scrollTop;
11428
+ const scrollHeight = el.scrollHeight;
11429
+ const clientHeight = el.clientHeight;
11430
+
11431
+ // Prepare sroll values
11432
+ const scrollResult = { scrollTop, scrollHeight, clientHeight };
11433
+ let atResult = null;
11434
+ let atCustomResult = null;
11435
+
11436
+ const atTop = scrollTop === 0;
11437
+ const atBottom = scrollTop + clientHeight >= scrollHeight - 1;
11438
+
11439
+ const atCustomTop = scrollTop <= 0 + this.#extraScrollBoundary;
11440
+ const atCustomBottom = scrollTop + clientHeight >= scrollHeight - 1 - this.#extraScrollBoundary;
11441
+
11442
+ // Scroll results
11443
+ if (atTop && atBottom) atResult = 'all';
11444
+ else if (atTop) atResult = 'top';
11445
+ else if (atBottom) atResult = 'bottom';
11446
+
11447
+ if (atCustomTop && atCustomBottom) atCustomResult = 'all';
11448
+ else if (atCustomTop) atCustomResult = 'top';
11449
+ else if (atCustomBottom) atCustomResult = 'bottom';
11450
+
11451
+ this.#isAtTop = atTop;
11452
+ this.#isAtBottom = atBottom;
11453
+
11454
+ this.#isAtCustomTop = atCustomTop;
11455
+ this.#isAtCustomBottom = atCustomBottom;
11456
+
11457
+ this.#scrollPaused = !(this.#autoScrollBottom && this.#isAtBottom);
11458
+
11459
+ this.#lastKnownScrollBottomOffset = scrollHeight - scrollTop - clientHeight;
11460
+
11461
+ // Send results
11462
+ this._emit('onScrollBoundary', { status: atResult, ...scrollResult, scrollCache });
11463
+ this._emit('onExtraScrollBoundary', { status: atCustomResult, ...scrollResult, scrollCache });
11464
+
11465
+ if (!this.#scrollPaused) {
11466
+ this._emit('onAutoScroll', { ...scrollResult, scrollCache });
11467
+ } else {
11468
+ this._emit('onScrollPause', { ...scrollResult, scrollCache });
11469
+ }
11470
+ }
11471
+
11472
+ /**
11473
+ * Attempts to correct the scroll position when layout shifts happen, preserving the user position if needed.
11474
+ *
11475
+ * @param {Element[]} [targets=[]] - List of elements involved in the size change.
11476
+ */
11477
+ _fixScroll(targets = []) {
11478
+ if (this.#destroyed) return;
11479
+ // === Validation ===
11480
+ if (!Array.isArray(targets))
11481
+ throw new TypeError('_fixScroll: targets must be an array of Elements');
11482
+
11483
+ // Get Scroll data
11484
+ const prevScrollHeight = this.#target.scrollHeight;
11485
+ const prevScrollTop = this.#target.scrollTop;
11486
+ const prevBottomOffset =
11487
+ this.#target.scrollHeight - this.#target.scrollTop - this.#target.clientHeight;
11488
+
11489
+ // Get new size
11490
+ const newScrollHeight = this.#target.scrollHeight;
11491
+ const heightDelta = newScrollHeight - prevScrollHeight;
11492
+
11493
+ /** @type {() => NodeSizes} */
11494
+ const calculateScrollSize = () => {
11495
+ // Run size getter
11496
+ const scrollSize = { height: 0, width: 0 };
11497
+ for (const target of targets) {
11498
+ const tgOs = this.#oldSizes.get(target) || { height: 0, width: 0 };
11499
+ const tgNs = this.#newSizes.get(target) || { height: 0, width: 0 };
11500
+ this.#sizeFilter.forEach((fn) => {
11501
+ /** @type {NodeSizes| undefined} */
11502
+ const sizes = fn(
11503
+ target,
11504
+ { old: tgOs, now: tgNs },
11505
+ { old: this.#elemOldAmount, now: this.#elemAmount },
11506
+ );
11507
+
11508
+ // Fix size
11509
+ if (this.#newVisibles.get(target) || this.#newVisiblesByTime.get(target)) {
11510
+ if (typeof sizes !== 'undefined' && typeof sizes !== 'object')
11511
+ throw new Error('_fixScroll: size filter must return an object or undefined');
11512
+ if (typeof sizes === 'undefined') return;
11513
+ scrollSize.height = sizes.height;
11514
+ scrollSize.width = sizes.width;
11515
+ }
11516
+ });
11517
+ }
11518
+
11519
+ // Checker
11520
+ if (typeof scrollSize.height !== 'number' && scrollSize.height < 0)
11521
+ throw new Error('_fixScroll: invalid scrollSize.height value');
11522
+ if (typeof scrollSize.width !== 'number' && scrollSize.width < 0)
11523
+ throw new Error('_fixScroll: invalid scrollSize.width value');
11524
+
11525
+ if (scrollSize.height !== 0 || scrollSize.width !== 0) {
11526
+ for (const target of targets) {
11527
+ this.#newVisiblesByTime.set(target, this.#newVisibles.get(target) ?? false);
11528
+ this.#oldVisiblesByTime.set(target, this.#oldVisibles.get(target) ?? false);
11529
+ }
11530
+ }
11531
+
11532
+ return scrollSize;
11533
+ };
11534
+
11535
+ // Fix scroll size
11536
+ if (
11537
+ this.#elemOldAmount > 0 &&
11538
+ libs_TinyHtml.hasScroll(this.#target).v &&
11539
+ this.#autoScrollBottom &&
11540
+ this.#preserveScrollOnLayoutShift &&
11541
+ !this.#isAtBottom &&
11542
+ !this.#isAtTop
11543
+ ) {
11544
+ const scrollSize = calculateScrollSize();
11545
+ // Complete
11546
+ this.#target.scrollTop = prevScrollTop + heightDelta + scrollSize.height;
11547
+ if (scrollSize.width > 0)
11548
+ this.#target.scrollLeft = this.#target.scrollLeft + scrollSize.width;
11549
+ }
11550
+
11551
+ // Normal stuff
11552
+ else if (!this.#scrollPaused && this.#autoScrollBottom) {
11553
+ calculateScrollSize();
11554
+ this.scrollToBottom();
11555
+ } else if (!this.#autoScrollBottom && !this.#isAtBottom) {
11556
+ calculateScrollSize();
11557
+ this.#target.scrollTop =
11558
+ this.#target.scrollHeight - this.#target.clientHeight - prevBottomOffset;
11559
+ }
11560
+ }
11561
+ /**
11562
+ * Sets up a MutationObserver to watch for DOM changes and react accordingly to maintain scroll consistency.
11563
+ */
11564
+ _observeMutations() {
11565
+ this.#mutationObserver = new MutationObserver((mutations) => {
11566
+ if (this.#destroyed) return;
11567
+ this._scrollDataUpdater();
11568
+ this.#elemOldAmount = this.#elemAmount;
11569
+ this.#elemAmount = this.#target.childElementCount;
11570
+
11571
+ mutations.forEach((mutation) => {
11572
+ mutation.addedNodes.forEach((node) => {
11573
+ if (!(node instanceof Element) || node.nodeType !== 1) return;
11574
+
11575
+ this._observeResizes([node]);
11576
+ this._listenLoadEvents(node);
11577
+
11578
+ if (this.#querySelector) {
11579
+ const children = node.querySelectorAll(this.#querySelector);
11580
+ this._observeResizes(children);
11581
+ this._listenLoadEvents(children);
11582
+ }
11583
+ });
11584
+ });
11585
+
11586
+ this._fixScroll();
11587
+ });
11588
+
11589
+ // Install observer
11590
+ this.#mutationObserver.observe(this.#target, {
11591
+ childList: true,
11592
+ subtree: true,
11593
+ attributes: true,
11594
+ attributeFilter:
11595
+ this.#attributeFilter.size > 0 ? Array.from(this.#attributeFilter) : undefined,
11596
+ });
11597
+ }
11598
+
11599
+ /**
11600
+ * Adds a ResizeObserver to monitor elements' size changes and trigger layout adjustments.
11601
+ *
11602
+ * @param {NodeListOf<Element>|Element[]|HTMLCollection} elements - Elements to observe.
11603
+ */
11604
+ _observeResizes(elements) {
11605
+ // Add resize observer
11606
+ if (!this.#resizeObserver) {
11607
+ this.#resizeObserver = new ResizeObserver((entries) => {
11608
+ if (this.#destroyed) return;
11609
+ this._scrollDataUpdater();
11610
+ /** @type {Element[]} */
11611
+ const targets = [];
11612
+ for (const entry of entries) {
11613
+ // Target
11614
+ const target = entry.target;
11615
+
11616
+ // Update old size
11617
+ const oldSize = this.#newSizes.get(target);
11618
+ if (oldSize) this.#oldSizes.set(target, oldSize);
11619
+
11620
+ // Set new size
11621
+ const { width, height } = entry.contentRect;
11622
+ this.#newSizes.set(target, { width, height });
11623
+ targets.push(target);
11624
+ }
11625
+
11626
+ // Animation frame
11627
+ this._fixScroll(targets);
11628
+ });
11629
+ }
11630
+
11631
+ // Execute observer
11632
+ Array.from(elements).forEach((el) => {
11633
+ if (!this.#resizeObserver)
11634
+ throw new Error('_observeResizes: ResizeObserver instance is not initialized');
11635
+ this.#resizeObserver.observe(el);
11636
+ });
11637
+ }
11638
+
11639
+ /**
11640
+ * Listens for media/content load events (e.g., images, iframes, videos) to trigger scroll updates.
11641
+ *
11642
+ * @param {NodeListOf<Element>|Element} elements - Target element(s) to listen on.
11643
+ */
11644
+ _listenLoadEvents(elements) {
11645
+ if (this.#destroyed) return;
11646
+ const list = elements instanceof NodeList ? Array.from(elements) : [elements];
11647
+
11648
+ list.forEach((el) => {
11649
+ if (this.#loadTags.has(el.tagName)) {
11650
+ // @ts-ignore
11651
+ if (!el.complete) {
11652
+ el.addEventListener('load', () => {
11653
+ this._scrollDataUpdater();
11654
+ if (!this.#scrollPaused && this.#autoScrollBottom) {
11655
+ this.scrollToBottom();
11656
+ }
11657
+ });
11658
+ }
11659
+ }
11660
+ });
11661
+ }
11662
+
11663
+ /**
11664
+ * Returns the internal scroll container element being monitored.
11665
+ *
11666
+ * @returns {Element} The DOM element used as the scroll container target.
11667
+ */
11668
+ get target() {
11669
+ return this.#target;
11670
+ }
11671
+
11672
+ /**
11673
+ * Returns the previous size of a given element, or undefined if not tracked.
11674
+ *
11675
+ * @param {Element} el - The DOM element to query.
11676
+ * @returns {NodeSizes|null} The old size, or undefined.
11677
+ */
11678
+ getOldSize(el) {
11679
+ return this.#oldSizes.get(el) ?? null;
11680
+ }
11681
+
11682
+ /**
11683
+ * Returns the current size of a given element, or undefined if not tracked.
11684
+ *
11685
+ * @param {Element} el - The DOM element to query.
11686
+ * @returns {NodeSizes|null} The new size, or undefined.
11687
+ */
11688
+ getNewSize(el) {
11689
+ return this.#newSizes.get(el) ?? null;
11690
+ }
11691
+
11692
+ /**
11693
+ * Returns whether the given element was visible in the last scroll update.
11694
+ *
11695
+ * @param {Element} el - The DOM element to check.
11696
+ * @returns {boolean} True if visible, false if not, or undefined if not tracked.
11697
+ */
11698
+ wasVisible(el) {
11699
+ return this.#oldVisibles.get(el) ?? false;
11700
+ }
11701
+
11702
+ /**
11703
+ * Returns whether the given element is currently visible.
11704
+ *
11705
+ * @param {Element} el - The DOM element to check.
11706
+ * @returns {boolean} True if visible, false if not, or undefined if not tracked.
11707
+ */
11708
+ isVisible(el) {
11709
+ return this.#newVisibles.get(el) ?? false;
11710
+ }
11711
+
11712
+ /**
11713
+ * Returns whether the element was visible in the last time-based visibility check.
11714
+ *
11715
+ * @param {Element} el - The DOM element to check.
11716
+ * @returns {boolean} Visibility state from the previous timed check.
11717
+ */
11718
+ wasTimedVisible(el) {
11719
+ return this.#oldVisiblesByTime.get(el) ?? false;
11720
+ }
11721
+
11722
+ /**
11723
+ * Returns whether the element is currently visible in the time-based check.
11724
+ *
11725
+ * @param {Element} el - The DOM element to check.
11726
+ * @returns {boolean} Visibility state from the current timed check.
11727
+ */
11728
+ isTimedVisible(el) {
11729
+ return this.#newVisiblesByTime.get(el) ?? false;
11730
+ }
11731
+
11732
+ /**
11733
+ * Sets the extra scroll boundary margin used when determining if the user is at a "custom" bottom or top.
11734
+ *
11735
+ * @param {number} value - Pixels of additional margin to use.
11736
+ */
11737
+ setExtraScrollBoundary(value) {
11738
+ if (typeof value !== 'number' || Number.isNaN(value))
11739
+ throw new TypeError('setExtraScrollBoundary(value): value must be a valid number');
11740
+ this.#extraScrollBoundary = value;
11741
+ }
11742
+
11743
+ /**
11744
+ * Returns the current extra scroll boundary setting.
11745
+ *
11746
+ * @returns {number}
11747
+ */
11748
+ getExtraScrollBoundary() {
11749
+ return this.#extraScrollBoundary;
11750
+ }
11751
+
11752
+ /**
11753
+ * Returns the last known distance (in pixels) from the bottom of the scroll container.
11754
+ *
11755
+ * @returns {number}
11756
+ */
11757
+ getLastKnownScrollBottomOffset() {
11758
+ return this.#lastKnownScrollBottomOffset;
11759
+ }
11760
+
11761
+ /**
11762
+ * Forces the scroll position to move to the very bottom of the target.
11763
+ */
11764
+ scrollToBottom() {
11765
+ this.#target.scrollTop = this.#target.scrollHeight;
11766
+ }
11767
+
11768
+ /**
11769
+ * Forces the scroll position to move to the very top of the target.
11770
+ */
11771
+ scrollToTop() {
11772
+ this.#target.scrollTop = 0;
11773
+ }
11774
+
11775
+ /**
11776
+ * Checks if the user is within the defined extra scroll boundary from the bottom.
11777
+ *
11778
+ * @returns {boolean}
11779
+ */
11780
+ isUserAtCustomBottom() {
11781
+ return this.#isAtCustomBottom;
11782
+ }
11783
+
11784
+ /**
11785
+ * Checks if the user is within the defined extra scroll boundary from the top.
11786
+ *
11787
+ * @returns {boolean}
11788
+ */
11789
+ isUserAtCustomTop() {
11790
+ return this.#isAtCustomTop;
11791
+ }
11792
+
11793
+ /**
11794
+ * Returns true if the user is currently scrolled to the bottom of the element.
11795
+ *
11796
+ * @returns {boolean}
11797
+ */
11798
+ isUserAtBottom() {
11799
+ return this.#isAtBottom;
11800
+ }
11801
+
11802
+ /**
11803
+ * Returns true if the user is currently scrolled to the top of the element.
11804
+ *
11805
+ * @returns {boolean}
11806
+ */
11807
+ isUserAtTop() {
11808
+ return this.#isAtTop;
11809
+ }
11810
+
11811
+ /**
11812
+ * Returns true if automatic scrolling is currently paused.
11813
+ *
11814
+ * @returns {boolean}
11815
+ */
11816
+ isScrollPaused() {
11817
+ return this.#scrollPaused;
11818
+ }
11819
+
11820
+ /**
11821
+ * Returns whether the target is the window object.
11822
+ *
11823
+ * @returns {boolean} True if the scroll target is window, false otherwise.
11824
+ */
11825
+ isWindow() {
11826
+ return this.#useWindow;
11827
+ }
11828
+
11829
+ /**
11830
+ * Returns whether the instance has been destroyed.
11831
+ *
11832
+ * @returns {boolean} True if the instance is destroyed, false otherwise.
11833
+ */
11834
+ isDestroyed() {
11835
+ return this.#destroyed;
11836
+ }
11837
+
11838
+ /**
11839
+ * Returns whether auto-scroll-to-bottom is enabled.
11840
+ *
11841
+ * @returns {boolean} True if auto-scroll is active, false otherwise.
11842
+ */
11843
+ getAutoScrollBottom() {
11844
+ return this.#autoScrollBottom;
11845
+ }
11846
+
11847
+ /**
11848
+ * Returns whether MutationObserver is enabled.
11849
+ *
11850
+ * @returns {boolean} True if mutation observation is active, false otherwise.
11851
+ */
11852
+ getObserveMutations() {
11853
+ return this.#observeMutations;
11854
+ }
11855
+
11856
+ /**
11857
+ * Returns whether layout shift protection is enabled.
11858
+ *
11859
+ * @returns {boolean} True if scroll preservation is active, false otherwise.
11860
+ */
11861
+ getPreserveScrollOnLayoutShift() {
11862
+ return this.#preserveScrollOnLayoutShift;
11863
+ }
11864
+
11865
+ /**
11866
+ * Returns the debounce delay in milliseconds used for scroll events.
11867
+ *
11868
+ * @returns {number} Debounce delay time.
11869
+ */
11870
+ getDebounceTime() {
11871
+ return this.#debounceTime;
11872
+ }
11873
+
11874
+ /**
11875
+ * Returns the current number of matching elements observed inside the scroll target.
11876
+ *
11877
+ * @returns {number} Current count of matching elements.
11878
+ */
11879
+ getElemAmount() {
11880
+ return this.#elemAmount;
11881
+ }
11882
+
11883
+ /**
11884
+ * Returns the previous known count of matching elements from the last update.
11885
+ *
11886
+ * @returns {number} Previous count of matching elements.
11887
+ */
11888
+ getPrevElemAmount() {
11889
+ return this.#elemOldAmount;
11890
+ }
11891
+
11892
+ /**
11893
+ * Returns the query selector string used to filter observed elements.
11894
+ *
11895
+ * @returns {string} The CSS selector string, or an empty string if none was provided.
11896
+ */
11897
+ getQuerySelector() {
11898
+ return this.#querySelector;
11899
+ }
11900
+
11901
+ /**
11902
+ * Disconnects all listeners, observers, and clears memory structures.
11903
+ * Once destroyed, this instance should no longer be used.
11904
+ */
11905
+ destroy() {
11906
+ if (this.#destroyed) return;
11907
+ this.#destroyed = true;
11908
+
11909
+ // Disconnects MutationObserver
11910
+ if (this.#mutationObserver) {
11911
+ this.#mutationObserver.disconnect();
11912
+ this.#mutationObserver = null;
11913
+ }
11914
+
11915
+ // Disconnect ResizeObserver
11916
+ if (this.#resizeObserver) {
11917
+ this.#resizeObserver.disconnect();
11918
+ this.#resizeObserver = null;
11919
+ }
11920
+
11921
+ // Removes scroll listener
11922
+ const target = this.#useWindow ? window : this.#target;
11923
+ if (this.#handler) target.removeEventListener('scroll', this.#handler);
11924
+
11925
+ // Clean the WeakMaps
11926
+ this.#oldSizes = new WeakMap();
11927
+ this.#newSizes = new WeakMap();
11928
+ this.#newVisibles = new WeakMap();
11929
+ this.#oldVisibles = new WeakMap();
11930
+ this.#newVisiblesByTime = new WeakMap();
11931
+ this.#oldVisiblesByTime = new WeakMap();
11932
+
11933
+ // Cleans listeners and filters
11934
+ this.#scrollListeners = {};
11935
+ this.#sizeFilter.clear();
11936
+ this.#loadTags.clear();
11937
+ }
11938
+ }
11939
+
11940
+ /* harmony default export */ const libs_TinySmartScroller = ((/* unused pure expression or super */ null && (TinySmartScroller)));
11941
+
10330
11942
  ;// ./src/v1/index.mjs
10331
11943
 
10332
11944
 
@@ -10356,6 +11968,8 @@ class TinyDragger {
10356
11968
 
10357
11969
 
10358
11970
 
11971
+
11972
+
10359
11973
 
10360
11974
 
10361
11975