tiny-essentials 1.16.2 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +559 -71
  94. package/dist/v1/TinyBasicsEs.min.js +1 -1
  95. package/dist/v1/TinyDragger.js +486 -47
  96. package/dist/v1/TinyDragger.min.js +1 -1
  97. package/dist/v1/TinyEssentials.js +2496 -72
  98. package/dist/v1/TinyEssentials.min.js +1 -1
  99. package/dist/v1/TinyHtml.js +486 -47
  100. package/dist/v1/TinyHtml.min.js +1 -1
  101. package/dist/v1/TinySmartScroller.js +6230 -0
  102. package/dist/v1/TinySmartScroller.min.js +1 -0
  103. package/dist/v1/TinyUploadClicker.js +1538 -72
  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 +486 -47
  120. package/dist/v1/libs/TinyHtml.d.mts +352 -139
  121. package/dist/v1/libs/TinyHtml.mjs +431 -47
  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 +197 -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
  /**
@@ -4281,6 +4330,21 @@ const {
4281
4330
  * @typedef {Element|Window} ElementAndWindow
4282
4331
  */
4283
4332
 
4333
+ /**
4334
+ * Represents a raw DOM element/window/document or an instance of TinyHtml.
4335
+ * This type is used to abstract interactions with both plain elements
4336
+ * and wrapped elements via the TinyHtml class.
4337
+ *
4338
+ * @typedef {ElementAndWinAndDoc|TinyHtml} TinyElementAndWinAndDoc
4339
+ */
4340
+
4341
+ /**
4342
+ * Represents a value that can be either a DOM Element, or the global Window object, or the document object.
4343
+ * Useful for functions that operate generically on scrollable or measurable targets.
4344
+ *
4345
+ * @typedef {Element|Window|Document} ElementAndWinAndDoc
4346
+ */
4347
+
4284
4348
  /**
4285
4349
  * A parameter type used for filtering or matching elements.
4286
4350
  * It can be:
@@ -4297,7 +4361,7 @@ const {
4297
4361
  * Elements accepted as constructor values for TinyHtml.
4298
4362
  * These include common DOM elements and root containers.
4299
4363
  *
4300
- * @typedef {Window|Element|Document} ConstructorElValues
4364
+ * @typedef {Window|Element|Document|Text} ConstructorElValues
4301
4365
  */
4302
4366
 
4303
4367
  /**
@@ -4418,40 +4482,114 @@ class TinyHtml_TinyHtml {
4418
4482
 
4419
4483
  static Utils = { ...collision_namespaceObject };
4420
4484
 
4485
+ /**
4486
+ * Creates a new DOM element with the specified tag name and options, then wraps it in a TinyHtml instance.
4487
+ *
4488
+ * @param {string} tagName - The tag name of the element to create (e.g., 'div', 'span').
4489
+ * @param {ElementCreationOptions} ops - Optional settings for creating the element.
4490
+ * @returns {TinyHtml} A TinyHtml instance wrapping the newly created DOM element.
4491
+ * @throws {TypeError} If tagName is not a string or ops is not an object.
4492
+ */
4493
+ static createElement(tagName, ops) {
4494
+ if (typeof tagName !== 'string')
4495
+ throw new TypeError(`[TinyHtml] createElement(): The tagName must be a string.`);
4496
+ if (typeof ops !== 'undefined' && typeof ops !== 'object')
4497
+ throw new TypeError(`[TinyHtml] createElement(): The ops must be a object.`);
4498
+ return new TinyHtml_TinyHtml(document.createElement(tagName, ops));
4499
+ }
4500
+
4501
+ /**
4502
+ * Creates a new TinyHtml instance that wraps a DOM TextNode.
4503
+ *
4504
+ * This method is useful when you want to insert raw text content into the DOM
4505
+ * without it being interpreted as HTML. The returned instance behaves like any
4506
+ * other TinyHtml element and can be appended or manipulated as needed.
4507
+ *
4508
+ * @param {string} value - The plain text content to be wrapped in a TextNode.
4509
+ * @returns {TinyHtml} A TinyHtml instance wrapping the newly created DOM TextNode.
4510
+ * @throws {TypeError} If the provided value is not a string.
4511
+ */
4512
+ static createTextNode(value) {
4513
+ if (typeof value !== 'string')
4514
+ throw new TypeError(`[TinyHtml] createTextNode(): The value must be a string.`);
4515
+ return new TinyHtml_TinyHtml(document.createTextNode(value));
4516
+ }
4517
+
4518
+ /**
4519
+ * Creates an HTMLElement or TextNode from an HTML string.
4520
+ * Supports both elements and plain text.
4521
+ *
4522
+ * @param {string} htmlString - The HTML string to convert.
4523
+ * @returns {TinyHtml} - A single HTMLElement or TextNode.
4524
+ */
4525
+ static createElementFromHTML(htmlString) {
4526
+ const template = document.createElement('template');
4527
+ htmlString = htmlString.trim();
4528
+
4529
+ // If it's only text (e.g., no "<" tag), return a TextNode
4530
+ if (!htmlString.startsWith('<')) {
4531
+ return TinyHtml_TinyHtml.createTextNode(htmlString);
4532
+ }
4533
+
4534
+ template.innerHTML = htmlString;
4535
+ if (!(template.content.firstChild instanceof Element)) throw new Error('');
4536
+ return new TinyHtml_TinyHtml(template.content.firstChild);
4537
+ }
4538
+
4421
4539
  /**
4422
4540
  * Queries the document for the first element matching the CSS selector and wraps it in a TinyHtml instance.
4423
4541
  *
4424
4542
  * @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.
4543
+ * @param {Document|Element} elem - Target element.
4544
+ * @returns {TinyHtml|null} A TinyHtml instance wrapping the matched element.
4427
4545
  */
4428
- static query(selector) {
4429
- const newEl = document.querySelector(selector);
4430
- if (!newEl) throw new Error(`[TinyHtml] query(): No element found for selector "${selector}".`);
4546
+ static query(selector, elem = document) {
4547
+ const newEl = elem.querySelector(selector);
4548
+ if (!newEl) return null;
4431
4549
  return new TinyHtml_TinyHtml(newEl);
4432
4550
  }
4433
4551
 
4552
+ /**
4553
+ * Queries the element for the first element matching the CSS selector and wraps it in a TinyHtml instance.
4554
+ *
4555
+ * @param {string} selector - A valid CSS selector string.
4556
+ * @returns {TinyHtml|null} A TinyHtml instance wrapping the matched element.
4557
+ */
4558
+ querySelector(selector) {
4559
+ return TinyHtml_TinyHtml.query(selector, TinyHtml_TinyHtml._preElem(this, 'query'));
4560
+ }
4561
+
4434
4562
  /**
4435
4563
  * Queries the document for all elements matching the CSS selector and wraps them in TinyHtml instances.
4436
4564
  *
4437
4565
  * @param {string} selector - A valid CSS selector string.
4566
+ * @param {Document|Element} elem - Target element.
4438
4567
  * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the matched elements.
4439
4568
  */
4440
- static queryAll(selector) {
4441
- const newEls = document.querySelectorAll(selector);
4569
+ static queryAll(selector, elem = document) {
4570
+ const newEls = elem.querySelectorAll(selector);
4442
4571
  return TinyHtml_TinyHtml.toTinyElm([...newEls]);
4443
4572
  }
4444
4573
 
4574
+ /**
4575
+ * Queries the element for all elements matching the CSS selector and wraps them in TinyHtml instances.
4576
+ *
4577
+ * @param {string} selector - A valid CSS selector string.
4578
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the matched elements.
4579
+ */
4580
+ querySelectorAll(selector) {
4581
+ return TinyHtml_TinyHtml.queryAll(selector, TinyHtml_TinyHtml._preElem(this, 'queryAll'));
4582
+ }
4583
+
4445
4584
  /**
4446
4585
  * Retrieves an element by its ID and wraps it in a TinyHtml instance.
4447
4586
  *
4448
4587
  * @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.
4588
+ * @returns {TinyHtml|null} A TinyHtml instance wrapping the found element.
4451
4589
  */
4452
4590
  static getById(selector) {
4453
4591
  const newEl = document.getElementById(selector);
4454
- if (!newEl) throw new Error(`[TinyHtml] getById(): No element found with ID "${selector}".`);
4592
+ if (!newEl) return null;
4455
4593
  return new TinyHtml_TinyHtml(newEl);
4456
4594
  }
4457
4595
 
@@ -4459,13 +4597,24 @@ class TinyHtml_TinyHtml {
4459
4597
  * Retrieves all elements with the specified class name and wraps them in TinyHtml instances.
4460
4598
  *
4461
4599
  * @param {string} selector - The class name to search for.
4600
+ * @param {Document|Element} elem - Target element.
4462
4601
  * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
4463
4602
  */
4464
- static getByClassName(selector) {
4465
- const newEls = document.getElementsByClassName(selector);
4603
+ static getByClassName(selector, elem = document) {
4604
+ const newEls = elem.getElementsByClassName(selector);
4466
4605
  return TinyHtml_TinyHtml.toTinyElm([...newEls]);
4467
4606
  }
4468
4607
 
4608
+ /**
4609
+ * Retrieves all elements with the specified class name and wraps them in TinyHtml instances.
4610
+ *
4611
+ * @param {string} selector - The class name to search for.
4612
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
4613
+ */
4614
+ getElementsByClassName(selector) {
4615
+ return TinyHtml_TinyHtml.getByClassName(selector, TinyHtml_TinyHtml._preElem(this, 'getByClassName'));
4616
+ }
4617
+
4469
4618
  /**
4470
4619
  * Retrieves all elements with the specified name attribute and wraps them in TinyHtml instances.
4471
4620
  *
@@ -4483,13 +4632,30 @@ class TinyHtml_TinyHtml {
4483
4632
  *
4484
4633
  * @param {string} localName - The local name (tag) of the elements to search for.
4485
4634
  * @param {string|null} [namespaceURI='http://www.w3.org/1999/xhtml'] - The namespace URI to search within.
4635
+ * @param {Document|Element} elem - Target element.
4486
4636
  * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
4487
4637
  */
4488
- static getByTagNameNS(localName, namespaceURI = 'http://www.w3.org/1999/xhtml') {
4489
- const newEls = document.getElementsByTagNameNS(namespaceURI, localName);
4638
+ static getByTagNameNS(localName, namespaceURI = 'http://www.w3.org/1999/xhtml', elem = document) {
4639
+ const newEls = elem.getElementsByTagNameNS(namespaceURI, localName);
4490
4640
  return TinyHtml_TinyHtml.toTinyElm([...newEls]);
4491
4641
  }
4492
4642
 
4643
+ /**
4644
+ * Retrieves all elements with the specified local tag name within the given namespace URI,
4645
+ * and wraps them in TinyHtml instances.
4646
+ *
4647
+ * @param {string} localName - The local name (tag) of the elements to search for.
4648
+ * @param {string|null} [namespaceURI='http://www.w3.org/1999/xhtml'] - The namespace URI to search within.
4649
+ * @returns {TinyHtml[]} An array of TinyHtml instances wrapping the found elements.
4650
+ */
4651
+ getElementsByTagNameNS(localName, namespaceURI = 'http://www.w3.org/1999/xhtml') {
4652
+ return TinyHtml_TinyHtml.getByTagNameNS(
4653
+ localName,
4654
+ namespaceURI,
4655
+ TinyHtml_TinyHtml._preElem(this, 'getByTagNameNS'),
4656
+ );
4657
+ }
4658
+
4493
4659
  //////////////////////////////////////////////////////////////////
4494
4660
 
4495
4661
  /**
@@ -4770,6 +4936,45 @@ class TinyHtml_TinyHtml {
4770
4936
  return TinyHtml_TinyHtml._preElemTemplate(elems, where, [Element, Window], ['Element', 'Window']);
4771
4937
  }
4772
4938
 
4939
+ /**
4940
+ * Ensures the input is returned as an array.
4941
+ * Useful to normalize operations across multiple or single element/window/document elements.
4942
+ *
4943
+ * @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/window element or array of html elements.
4944
+ * @param {string} where - The method or context name where validation is being called.
4945
+ * @returns {ElementAndWindow[]} - Always returns an array of element/window elements.
4946
+ * @readonly
4947
+ */
4948
+ static _preElemsAndWinAndDoc(elems, where) {
4949
+ const result = TinyHtml_TinyHtml._preElemsTemplate(
4950
+ elems,
4951
+ where,
4952
+ [Element, Window, Document],
4953
+ ['Element', 'Window', 'Document'],
4954
+ );
4955
+ return result.map((elem) => (!(elem instanceof Document) ? elem : elem.documentElement));
4956
+ }
4957
+
4958
+ /**
4959
+ * Ensures the input is returned as an single element/window/document element.
4960
+ * Useful to normalize operations across multiple or single element/window/document elements.
4961
+ *
4962
+ * @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/window element or array of html elements.
4963
+ * @param {string} where - The method or context name where validation is being called.
4964
+ * @returns {ElementAndWindow} - Always returns an single element/window element.
4965
+ * @readonly
4966
+ */
4967
+ static _preElemAndWinAndDoc(elems, where) {
4968
+ const result = TinyHtml_TinyHtml._preElemTemplate(
4969
+ elems,
4970
+ where,
4971
+ [Element, Window, Document],
4972
+ ['Element', 'Window', 'Document'],
4973
+ );
4974
+ if (result instanceof Document) return result.documentElement;
4975
+ return result;
4976
+ }
4977
+
4773
4978
  /**
4774
4979
  * Normalizes and converts one or more DOM elements (or TinyHtml instances)
4775
4980
  * into an array of `TinyHtml` instances.
@@ -5116,12 +5321,13 @@ class TinyHtml_TinyHtml {
5116
5321
  * @param {string} key - The key under which the data will be stored.
5117
5322
  * @param {any} value - The value to store.
5118
5323
  * @param {boolean} [isPrivate=false] - Whether to store the data in the private store.
5119
- * @returns {void}
5324
+ * @returns {TinyElement}
5120
5325
  */
5121
5326
  static setData(el, key, value, isPrivate = false) {
5122
5327
  const data = TinyHtml_TinyHtml._dataSelector[!isPrivate ? 'public' : 'private']('setData', el);
5123
5328
  if (typeof key !== 'string') throw new TypeError('The key must be a string.');
5124
5329
  data[key] = value;
5330
+ return el;
5125
5331
  }
5126
5332
 
5127
5333
  /**
@@ -5130,7 +5336,7 @@ class TinyHtml_TinyHtml {
5130
5336
  * @param {string} key - The key under which the data will be stored.
5131
5337
  * @param {any} value - The value to store.
5132
5338
  * @param {boolean} [isPrivate=false] - Whether to store the data in the private store.
5133
- * @returns {void}
5339
+ * @returns {TinyElement}
5134
5340
  */
5135
5341
  setData(key, value, isPrivate = false) {
5136
5342
  return TinyHtml_TinyHtml.setData(this, key, value, isPrivate);
@@ -5477,18 +5683,21 @@ class TinyHtml_TinyHtml {
5477
5683
  /**
5478
5684
  * Appends child elements or strings to the end of the target element(s).
5479
5685
  *
5480
- * @param {TinyElement | TinyElement[]} el - The target element(s) to receive children.
5686
+ * @param {TinyElement} el - The target element(s) to receive children.
5481
5687
  * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to append.
5688
+ * @returns {TinyElement}
5482
5689
  */
5483
5690
  static append(el, ...children) {
5484
5691
  const elem = TinyHtml_TinyHtml._preElem(el, 'append');
5485
5692
  elem.append(...TinyHtml_TinyHtml._appendChecker('append', ...children));
5693
+ return el;
5486
5694
  }
5487
5695
 
5488
5696
  /**
5489
5697
  * Appends child elements or strings to the end of the target element(s).
5490
5698
  *
5491
5699
  * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to append.
5700
+ * @returns {TinyElement}
5492
5701
  */
5493
5702
  append(...children) {
5494
5703
  return TinyHtml_TinyHtml.append(this, ...children);
@@ -5497,18 +5706,21 @@ class TinyHtml_TinyHtml {
5497
5706
  /**
5498
5707
  * Prepends child elements or strings to the beginning of the target element(s).
5499
5708
  *
5500
- * @param {TinyElement | TinyElement[]} el - The target element(s) to receive children.
5709
+ * @param {TinyElement} el - The target element(s) to receive children.
5501
5710
  * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to prepend.
5711
+ * @returns {TinyElement}
5502
5712
  */
5503
5713
  static prepend(el, ...children) {
5504
5714
  const elem = TinyHtml_TinyHtml._preElem(el, 'prepend');
5505
5715
  elem.prepend(...TinyHtml_TinyHtml._appendChecker('prepend', ...children));
5716
+ return el;
5506
5717
  }
5507
5718
 
5508
5719
  /**
5509
5720
  * Prepends child elements or strings to the beginning of the target element(s).
5510
5721
  *
5511
5722
  * @param {...(TinyNode | TinyNode[] | string)} children - The child elements or text to prepend.
5723
+ * @returns {TinyElement}
5512
5724
  */
5513
5725
  prepend(...children) {
5514
5726
  return TinyHtml_TinyHtml.prepend(this, ...children);
@@ -5517,18 +5729,21 @@ class TinyHtml_TinyHtml {
5517
5729
  /**
5518
5730
  * Inserts elements or strings immediately before the target element(s) in the DOM.
5519
5731
  *
5520
- * @param {TinyElement | TinyElement[]} el - The target element(s) before which new content is inserted.
5732
+ * @param {TinyElement} el - The target element(s) before which new content is inserted.
5521
5733
  * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert before the target.
5734
+ * @returns {TinyElement}
5522
5735
  */
5523
5736
  static before(el, ...children) {
5524
5737
  const elem = TinyHtml_TinyHtml._preElem(el, 'before');
5525
5738
  elem.before(...TinyHtml_TinyHtml._appendChecker('before', ...children));
5739
+ return el;
5526
5740
  }
5527
5741
 
5528
5742
  /**
5529
5743
  * Inserts elements or strings immediately before the target element(s) in the DOM.
5530
5744
  *
5531
5745
  * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert before the target.
5746
+ * @returns {TinyElement}
5532
5747
  */
5533
5748
  before(...children) {
5534
5749
  return TinyHtml_TinyHtml.before(this, ...children);
@@ -5537,18 +5752,21 @@ class TinyHtml_TinyHtml {
5537
5752
  /**
5538
5753
  * Inserts elements or strings immediately after the target element(s) in the DOM.
5539
5754
  *
5540
- * @param {TinyElement | TinyElement[]} el - The target element(s) after which new content is inserted.
5755
+ * @param {TinyElement} el - The target element(s) after which new content is inserted.
5541
5756
  * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert after the target.
5757
+ * @returns {TinyElement}
5542
5758
  */
5543
5759
  static after(el, ...children) {
5544
5760
  const elem = TinyHtml_TinyHtml._preElem(el, 'after');
5545
5761
  elem.after(...TinyHtml_TinyHtml._appendChecker('after', ...children));
5762
+ return el;
5546
5763
  }
5547
5764
 
5548
5765
  /**
5549
5766
  * Inserts elements or strings immediately after the target element(s) in the DOM.
5550
5767
  *
5551
5768
  * @param {...(TinyNode | TinyNode[] | string)} children - Elements or text to insert after the target.
5769
+ * @returns {TinyElement}
5552
5770
  */
5553
5771
  after(...children) {
5554
5772
  return TinyHtml_TinyHtml.after(this, ...children);
@@ -5557,18 +5775,21 @@ class TinyHtml_TinyHtml {
5557
5775
  /**
5558
5776
  * Replaces the target element(s) in the DOM with new elements or text.
5559
5777
  *
5560
- * @param {TinyElement | TinyElement[]} el - The element(s) to be replaced.
5778
+ * @param {TinyElement} el - The element(s) to be replaced.
5561
5779
  * @param {...(TinyNode | TinyNode[] | string)} newNodes - New elements or text to replace the target.
5780
+ * @returns {TinyElement}
5562
5781
  */
5563
5782
  static replaceWith(el, ...newNodes) {
5564
5783
  const elem = TinyHtml_TinyHtml._preElem(el, 'replaceWith');
5565
5784
  elem.replaceWith(...TinyHtml_TinyHtml._appendChecker('replaceWith', ...newNodes));
5785
+ return el;
5566
5786
  }
5567
5787
 
5568
5788
  /**
5569
5789
  * Replaces the target element(s) in the DOM with new elements or text.
5570
5790
  *
5571
5791
  * @param {...(TinyNode | TinyNode[] | string)} newNodes - New elements or text to replace the target.
5792
+ * @returns {TinyElement}
5572
5793
  */
5573
5794
  replaceWith(...newNodes) {
5574
5795
  return TinyHtml_TinyHtml.replaceWith(this, ...newNodes);
@@ -5579,6 +5800,7 @@ class TinyHtml_TinyHtml {
5579
5800
  *
5580
5801
  * @param {TinyNode | TinyNode[]} el - The element(s) to append.
5581
5802
  * @param {TinyNode | TinyNode[]} targets - Target element(s) where content will be appended.
5803
+ * @returns {TinyNode|TinyNode[]}
5582
5804
  */
5583
5805
  static appendTo(el, targets) {
5584
5806
  const elems = TinyHtml_TinyHtml._preNodeElems(el, 'appendTo');
@@ -5588,12 +5810,14 @@ class TinyHtml_TinyHtml {
5588
5810
  target.appendChild(i === tars.length - 1 ? elem : elem.cloneNode(true)),
5589
5811
  );
5590
5812
  });
5813
+ return el;
5591
5814
  }
5592
5815
 
5593
5816
  /**
5594
5817
  * Appends the given element(s) to each target element in sequence.
5595
5818
  *
5596
5819
  * @param {TinyNode | TinyNode[]} targets - Target element(s) where content will be appended.
5820
+ * @returns {TinyNode|TinyNode[]}
5597
5821
  */
5598
5822
  appendTo(targets) {
5599
5823
  return TinyHtml_TinyHtml.appendTo(this, targets);
@@ -5604,6 +5828,7 @@ class TinyHtml_TinyHtml {
5604
5828
  *
5605
5829
  * @param {TinyElement | TinyElement[]} el - The element(s) to prepend.
5606
5830
  * @param {TinyElement | TinyElement[]} targets - Target element(s) where content will be prepended.
5831
+ * @returns {TinyElement|TinyElement[]}
5607
5832
  */
5608
5833
  static prependTo(el, targets) {
5609
5834
  const elems = TinyHtml_TinyHtml._preElems(el, 'prependTo');
@@ -5614,12 +5839,14 @@ class TinyHtml_TinyHtml {
5614
5839
  .reverse()
5615
5840
  .forEach((elem) => target.prepend(i === tars.length - 1 ? elem : elem.cloneNode(true)));
5616
5841
  });
5842
+ return el;
5617
5843
  }
5618
5844
 
5619
5845
  /**
5620
5846
  * Prepends the given element(s) to each target element in sequence.
5621
5847
  *
5622
5848
  * @param {TinyElement | TinyElement[]} targets - Target element(s) where content will be prepended.
5849
+ * @returns {TinyElement|TinyElement[]}
5623
5850
  */
5624
5851
  prependTo(targets) {
5625
5852
  return TinyHtml_TinyHtml.prependTo(this, targets);
@@ -5631,6 +5858,7 @@ class TinyHtml_TinyHtml {
5631
5858
  * @param {TinyNode | TinyNode[]} el - The element(s) to insert.
5632
5859
  * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5633
5860
  * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert before, defaults to target.
5861
+ * @returns {TinyNode|TinyNode[]}
5634
5862
  */
5635
5863
  static insertBefore(el, target, child = null) {
5636
5864
  const elem = TinyHtml_TinyHtml._preNodeElem(el, 'insertBefore');
@@ -5638,6 +5866,7 @@ class TinyHtml_TinyHtml {
5638
5866
  const childNode = TinyHtml_TinyHtml._preNodeElemWithNull(child, 'insertBefore');
5639
5867
  if (!targ.parentNode) throw new Error('');
5640
5868
  targ.parentNode.insertBefore(elem, childNode || targ);
5869
+ return el;
5641
5870
  }
5642
5871
 
5643
5872
  /**
@@ -5645,6 +5874,7 @@ class TinyHtml_TinyHtml {
5645
5874
  *
5646
5875
  * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5647
5876
  * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert before, defaults to target.
5877
+ * @returns {TinyNode|TinyNode[]}
5648
5878
  */
5649
5879
  insertBefore(target, child) {
5650
5880
  return TinyHtml_TinyHtml.insertBefore(this, target, child);
@@ -5656,6 +5886,7 @@ class TinyHtml_TinyHtml {
5656
5886
  * @param {TinyNode | TinyNode[]} el - The element(s) to insert.
5657
5887
  * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5658
5888
  * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert after, defaults to target.
5889
+ * @returns {TinyNode|TinyNode[]}
5659
5890
  */
5660
5891
  static insertAfter(el, target, child = null) {
5661
5892
  const elem = TinyHtml_TinyHtml._preNodeElem(el, 'insertAfter');
@@ -5663,6 +5894,7 @@ class TinyHtml_TinyHtml {
5663
5894
  const childNode = TinyHtml_TinyHtml._preNodeElemWithNull(child, 'insertBefore');
5664
5895
  if (!targ.parentNode) throw new Error('');
5665
5896
  targ.parentNode.insertBefore(elem, childNode || targ.nextSibling);
5897
+ return el;
5666
5898
  }
5667
5899
 
5668
5900
  /**
@@ -5670,6 +5902,7 @@ class TinyHtml_TinyHtml {
5670
5902
  *
5671
5903
  * @param {TinyNode | TinyNode[]} target - The reference element where insertion happens.
5672
5904
  * @param {TinyNode | TinyNode[] | null} [child=null] - Optional child to insert after, defaults to target.
5905
+ * @returns {TinyNode|TinyNode[]}
5673
5906
  */
5674
5907
  insertAfter(target, child) {
5675
5908
  return TinyHtml_TinyHtml.insertAfter(this, target, child);
@@ -5681,6 +5914,7 @@ class TinyHtml_TinyHtml {
5681
5914
  *
5682
5915
  * @param {TinyNode | TinyNode[]} el - The new element(s) to insert.
5683
5916
  * @param {TinyNode | TinyNode[]} targets - The elements to be replaced.
5917
+ * @returns {TinyNode|TinyNode[]}
5684
5918
  */
5685
5919
  static replaceAll(el, targets) {
5686
5920
  const elems = TinyHtml_TinyHtml._preNodeElems(el, 'replaceAll');
@@ -5692,6 +5926,7 @@ class TinyHtml_TinyHtml {
5692
5926
  parent.replaceChild(i === tars.length - 1 ? elem : elem.cloneNode(true), target);
5693
5927
  });
5694
5928
  });
5929
+ return el;
5695
5930
  }
5696
5931
 
5697
5932
  /**
@@ -5699,6 +5934,7 @@ class TinyHtml_TinyHtml {
5699
5934
  * If multiple targets exist, the inserted elements are cloned accordingly.
5700
5935
  *
5701
5936
  * @param {TinyNode | TinyNode[]} targets - The elements to be replaced.
5937
+ * @returns {TinyNode|TinyNode[]}
5702
5938
  */
5703
5939
  replaceAll(targets) {
5704
5940
  return TinyHtml_TinyHtml.replaceAll(this, targets);
@@ -5722,7 +5958,12 @@ class TinyHtml_TinyHtml {
5722
5958
  throw new Error(
5723
5959
  `[TinyHtml] You are trying to put a TinyHtml inside another TinyHtml in constructor.`,
5724
5960
  );
5725
- if (!(el instanceof Element) && !(el instanceof Window) && !(el instanceof Document))
5961
+ if (
5962
+ !(el instanceof Element) &&
5963
+ !(el instanceof Window) &&
5964
+ !(el instanceof Document) &&
5965
+ !(el instanceof Text)
5966
+ )
5726
5967
  throw new Error(`[TinyHtml] Invalid Target in constructor.`);
5727
5968
  this.#el = el;
5728
5969
  }
@@ -6172,6 +6413,7 @@ class TinyHtml_TinyHtml {
6172
6413
  * @param {TinyHtmlElement|TinyHtmlElement[]} el - The element to inspect.
6173
6414
  * @param {string|Object} prop - The property name or an object with key-value pairs
6174
6415
  * @param {string|null} [value=null] - The value to set (if `prop` is a string)
6416
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6175
6417
  */
6176
6418
  static setStyle(el, prop, value = null) {
6177
6419
  TinyHtml_TinyHtml._preHtmlElems(el, 'setStyle').forEach((elem) => {
@@ -6184,6 +6426,7 @@ class TinyHtml_TinyHtml {
6184
6426
  }
6185
6427
  } else elem.style.setProperty(TinyHtml_TinyHtml.toStyleKc(prop), value);
6186
6428
  });
6429
+ return el;
6187
6430
  }
6188
6431
 
6189
6432
  /**
@@ -6194,6 +6437,7 @@ class TinyHtml_TinyHtml {
6194
6437
  *
6195
6438
  * @param {string|Object} prop - The property name or an object with key-value pairs
6196
6439
  * @param {string|null} [value=null] - The value to set (if `prop` is a string)
6440
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6197
6441
  */
6198
6442
  setStyle(prop, value) {
6199
6443
  return TinyHtml_TinyHtml.setStyle(this, prop, value);
@@ -6289,6 +6533,7 @@ class TinyHtml_TinyHtml {
6289
6533
  *
6290
6534
  * @param {TinyHtmlElement|TinyHtmlElement[]} el - A single element or an array of elements.
6291
6535
  * @param {string|string[]} prop - A property name or an array of property names to remove.
6536
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6292
6537
  */
6293
6538
  static removeStyle(el, prop) {
6294
6539
  TinyHtml_TinyHtml._preHtmlElems(el, 'removeStyle').forEach((elem) => {
@@ -6298,12 +6543,14 @@ class TinyHtml_TinyHtml {
6298
6543
  }
6299
6544
  } else elem.style.removeProperty(TinyHtml_TinyHtml.toStyleKc(prop));
6300
6545
  });
6546
+ return el;
6301
6547
  }
6302
6548
 
6303
6549
  /**
6304
6550
  * Removes one or more inline CSS properties from the given element(s).
6305
6551
  *
6306
6552
  * @param {string|string[]} prop - A property name or an array of property names to remove.
6553
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6307
6554
  */
6308
6555
  removeStyle(prop) {
6309
6556
  return TinyHtml_TinyHtml.removeStyle(this, prop);
@@ -6318,6 +6565,7 @@ class TinyHtml_TinyHtml {
6318
6565
  * @param {string} prop - The CSS property to toggle.
6319
6566
  * @param {string} val1 - The first value (used as "current" check).
6320
6567
  * @param {string} val2 - The second value (used as the "alternative").
6568
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6321
6569
  */
6322
6570
  static toggleStyle(el, prop, val1, val2) {
6323
6571
  TinyHtml_TinyHtml._preHtmlElems(el, 'toggleStyle').forEach((elem) => {
@@ -6325,6 +6573,7 @@ class TinyHtml_TinyHtml {
6325
6573
  const newVal = current === TinyHtml_TinyHtml.toStyleKc(val1) ? val2 : val1;
6326
6574
  TinyHtml_TinyHtml.setStyle(elem, prop, newVal);
6327
6575
  });
6576
+ return el;
6328
6577
  }
6329
6578
 
6330
6579
  /**
@@ -6335,6 +6584,7 @@ class TinyHtml_TinyHtml {
6335
6584
  * @param {string} prop - The CSS property to toggle.
6336
6585
  * @param {string} val1 - The first value (used as "current" check).
6337
6586
  * @param {string} val2 - The second value (used as the "alternative").
6587
+ * @returns {TinyHtmlElement|TinyHtmlElement[]}
6338
6588
  */
6339
6589
  toggleStyle(prop, val1, val2) {
6340
6590
  return TinyHtml_TinyHtml.toggleStyle(this, prop, val1, val2);
@@ -6344,14 +6594,16 @@ class TinyHtml_TinyHtml {
6344
6594
  * Removes all inline styles (`style` attribute) from the given element(s).
6345
6595
  *
6346
6596
  * @param {TinyElement|TinyElement[]} el - A single element or an array of elements.
6597
+ * @returns {TinyElement|TinyElement[]}
6347
6598
  */
6348
6599
  static clearStyle(el) {
6349
6600
  TinyHtml_TinyHtml._preElems(el, 'clearStyle').forEach((elem) => elem.removeAttribute('style'));
6601
+ return el;
6350
6602
  }
6351
6603
 
6352
6604
  /**
6353
6605
  * Removes all inline styles (`style` attribute) from the given element(s).
6354
- *
6606
+ * @returns {TinyElement|TinyElement[]}
6355
6607
  */
6356
6608
  clearStyle() {
6357
6609
  return TinyHtml_TinyHtml.clearStyle(this);
@@ -6363,14 +6615,17 @@ class TinyHtml_TinyHtml {
6363
6615
  * Focus the element.
6364
6616
  *
6365
6617
  * @param {TinyHtmlElement} el - The element or a selector string.
6618
+ * @returns {TinyHtmlElement}
6366
6619
  */
6367
6620
  static focus(el) {
6368
6621
  const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'focus');
6369
6622
  elem.focus();
6623
+ return el;
6370
6624
  }
6371
6625
 
6372
6626
  /**
6373
6627
  * Focus the element.
6628
+ * @returns {TinyHtmlElement}
6374
6629
  */
6375
6630
  focus() {
6376
6631
  return TinyHtml_TinyHtml.focus(this);
@@ -6380,19 +6635,46 @@ class TinyHtml_TinyHtml {
6380
6635
  * Blur the element.
6381
6636
  *
6382
6637
  * @param {TinyHtmlElement} el - The element or a selector string.
6638
+ * @returns {TinyHtmlElement}
6383
6639
  */
6384
6640
  static blur(el) {
6385
6641
  const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'blur');
6386
6642
  elem.blur();
6643
+ return el;
6387
6644
  }
6388
6645
 
6389
6646
  /**
6390
6647
  * Blur the element.
6648
+ * @returns {TinyHtmlElement}
6391
6649
  */
6392
6650
  blur() {
6393
6651
  return TinyHtml_TinyHtml.blur(this);
6394
6652
  }
6395
6653
 
6654
+ /**
6655
+ * Interprets a value as a boolean `true` if it matches a common truthy representation.
6656
+ *
6657
+ * This method checks if the input is any of the common forms used to represent `true`,
6658
+ * such as the string `'true'`, `'1'`, `'on'`, the boolean `true`, or the number `1`.
6659
+ *
6660
+ * @param {string|boolean|number} [value] - The value to interpret as boolean.
6661
+ * @returns {boolean} `true` if the value represents a truthy state, otherwise `false`.
6662
+ */
6663
+ static boolCheck(value) {
6664
+ if (
6665
+ typeof value !== 'undefined' &&
6666
+ (value === 'true' ||
6667
+ value === '1' ||
6668
+ value === true ||
6669
+ value === 'on' ||
6670
+ (typeof value === 'number' && value > 0))
6671
+ ) {
6672
+ return true;
6673
+ } else {
6674
+ return false;
6675
+ }
6676
+ }
6677
+
6396
6678
  //////////////////////////////////////////////////////////////////////
6397
6679
 
6398
6680
  /**
@@ -6445,16 +6727,40 @@ class TinyHtml_TinyHtml {
6445
6727
  return window.innerWidth || document.documentElement.clientWidth;
6446
6728
  }
6447
6729
 
6730
+ /**
6731
+ * Checks if the page is currently scrolled to the very top.
6732
+ *
6733
+ * This method compares the current vertical scroll position with the total document height.
6734
+ * It's useful for detecting if the user has reached the top of the page.
6735
+ *
6736
+ * @returns {boolean} `true` if the page is scrolled to the top, otherwise `false`.
6737
+ */
6738
+ static isPageTop() {
6739
+ return window.scrollY === 0;
6740
+ }
6741
+
6742
+ /**
6743
+ * Checks if the page is currently scrolled to the very bottom.
6744
+ *
6745
+ * This method uses the `scrollY` and `innerHeight` properties to determine if the
6746
+ * user has reached the end of the document.
6747
+ *
6748
+ * @returns {boolean} `true` if the page is scrolled to the bottom, otherwise `false`.
6749
+ */
6750
+ static isPageBottom() {
6751
+ return window.innerHeight + window.scrollY >= document.body.offsetHeight;
6752
+ }
6753
+
6448
6754
  /**
6449
6755
  * Gets the width or height of an element based on the box model.
6450
- * @param {TinyElementAndWindow} el - The element or window.
6756
+ * @param {TinyElementAndWinAndDoc} el - The element or window.
6451
6757
  * @param {"width"|"height"} type - Dimension type.
6452
6758
  * @param {"content"|"padding"|"border"|"margin"} [extra='content'] - Box model context.
6453
6759
  * @returns {number} - Computed dimension.
6454
6760
  * @throws {TypeError} If `type` or `extra` is not a string.
6455
6761
  */
6456
6762
  static getDimension(el, type, extra = 'content') {
6457
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'getDimension');
6763
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'getDimension');
6458
6764
  if (typeof type !== 'string') throw new TypeError('The type must be a string.');
6459
6765
  if (typeof extra !== 'string') throw new TypeError('The extra must be a string.');
6460
6766
 
@@ -6542,17 +6848,20 @@ class TinyHtml_TinyHtml {
6542
6848
  * @param {TinyHtmlElement} el - Target element.
6543
6849
  * @param {string|number} value - Height value.
6544
6850
  * @throws {TypeError} If `value` is neither a string nor number.
6851
+ * @returns {TinyHtmlElement}
6545
6852
  */
6546
6853
  static setHeight(el, value) {
6547
6854
  const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'setHeight');
6548
6855
  if (typeof value !== 'number' && typeof value !== 'string')
6549
6856
  throw new TypeError('The value must be a string or number.');
6550
6857
  elem.style.height = typeof value === 'number' ? `${value}px` : value;
6858
+ return el;
6551
6859
  }
6552
6860
 
6553
6861
  /**
6554
6862
  * Sets the height of the element.
6555
6863
  * @param {string|number} value - Height value.
6864
+ * @returns {TinyHtmlElement}
6556
6865
  */
6557
6866
  setHeight(value) {
6558
6867
  return TinyHtml_TinyHtml.setHeight(this, value);
@@ -6563,17 +6872,20 @@ class TinyHtml_TinyHtml {
6563
6872
  * @param {TinyHtmlElement} el - Target element.
6564
6873
  * @param {string|number} value - Width value.
6565
6874
  * @throws {TypeError} If `value` is neither a string nor number.
6875
+ * @returns {TinyHtmlElement}
6566
6876
  */
6567
6877
  static setWidth(el, value) {
6568
6878
  const elem = TinyHtml_TinyHtml._preHtmlElem(el, 'setWidth');
6569
6879
  if (typeof value !== 'number' && typeof value !== 'string')
6570
6880
  throw new TypeError('The value must be a string or number.');
6571
6881
  elem.style.width = typeof value === 'number' ? `${value}px` : value;
6882
+ return el;
6572
6883
  }
6573
6884
 
6574
6885
  /**
6575
6886
  * Sets the width of the element.
6576
6887
  * @param {string|number} value - Width value.
6888
+ * @returns {TinyHtmlElement}
6577
6889
  */
6578
6890
  setWidth(value) {
6579
6891
  return TinyHtml_TinyHtml.setWidth(this, value);
@@ -6581,11 +6893,11 @@ class TinyHtml_TinyHtml {
6581
6893
 
6582
6894
  /**
6583
6895
  * Returns content box height.
6584
- * @param {TinyElementAndWindow} el - Target element.
6896
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6585
6897
  * @returns {number}
6586
6898
  */
6587
6899
  static height(el) {
6588
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'height');
6900
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'height');
6589
6901
  return TinyHtml_TinyHtml.getDimension(elem, 'height', 'content');
6590
6902
  }
6591
6903
 
@@ -6599,11 +6911,11 @@ class TinyHtml_TinyHtml {
6599
6911
 
6600
6912
  /**
6601
6913
  * Returns content box width.
6602
- * @param {TinyElementAndWindow} el - Target element.
6914
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6603
6915
  * @returns {number}
6604
6916
  */
6605
6917
  static width(el) {
6606
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'width');
6918
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'width');
6607
6919
  return TinyHtml_TinyHtml.getDimension(elem, 'width', 'content');
6608
6920
  }
6609
6921
 
@@ -6617,11 +6929,11 @@ class TinyHtml_TinyHtml {
6617
6929
 
6618
6930
  /**
6619
6931
  * Returns padding box height.
6620
- * @param {TinyElementAndWindow} el - Target element.
6932
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6621
6933
  * @returns {number}
6622
6934
  */
6623
6935
  static innerHeight(el) {
6624
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'innerHeight');
6936
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'innerHeight');
6625
6937
  return TinyHtml_TinyHtml.getDimension(elem, 'height', 'padding');
6626
6938
  }
6627
6939
 
@@ -6635,11 +6947,11 @@ class TinyHtml_TinyHtml {
6635
6947
 
6636
6948
  /**
6637
6949
  * Returns padding box width.
6638
- * @param {TinyElementAndWindow} el - Target element.
6950
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6639
6951
  * @returns {number}
6640
6952
  */
6641
6953
  static innerWidth(el) {
6642
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'innerWidth');
6954
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'innerWidth');
6643
6955
  return TinyHtml_TinyHtml.getDimension(elem, 'width', 'padding');
6644
6956
  }
6645
6957
 
@@ -6653,14 +6965,14 @@ class TinyHtml_TinyHtml {
6653
6965
 
6654
6966
  /**
6655
6967
  * Returns outer height of the element, optionally including margin.
6656
- * @param {TinyElementAndWindow} el - Target element.
6968
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6657
6969
  * @param {boolean} [includeMargin=false] - Whether to include margin.
6658
6970
  * @returns {number}
6659
6971
  */
6660
6972
  static outerHeight(el, includeMargin = false) {
6661
6973
  if (typeof includeMargin !== 'boolean')
6662
6974
  throw new TypeError('The "includeMargin" must be a boolean.');
6663
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'outerHeight');
6975
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'outerHeight');
6664
6976
  return TinyHtml_TinyHtml.getDimension(elem, 'height', includeMargin ? 'margin' : 'border');
6665
6977
  }
6666
6978
 
@@ -6675,14 +6987,14 @@ class TinyHtml_TinyHtml {
6675
6987
 
6676
6988
  /**
6677
6989
  * Returns outer width of the element, optionally including margin.
6678
- * @param {TinyElementAndWindow} el - Target element.
6990
+ * @param {TinyElementAndWinAndDoc} el - Target element.
6679
6991
  * @param {boolean} [includeMargin=false] - Whether to include margin.
6680
6992
  * @returns {number}
6681
6993
  */
6682
6994
  static outerWidth(el, includeMargin = false) {
6683
6995
  if (typeof includeMargin !== 'boolean')
6684
6996
  throw new TypeError('The "includeMargin" must be a boolean.');
6685
- const elem = TinyHtml_TinyHtml._preElemAndWindow(el, 'outerWidth');
6997
+ const elem = TinyHtml_TinyHtml._preElemAndWinAndDoc(el, 'outerWidth');
6686
6998
  return TinyHtml_TinyHtml.getDimension(elem, 'width', includeMargin ? 'margin' : 'border');
6687
6999
  }
6688
7000
 
@@ -6855,6 +7167,7 @@ class TinyHtml_TinyHtml {
6855
7167
  * Sets the vertical scroll position.
6856
7168
  * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
6857
7169
  * @param {number} value - Scroll top value.
7170
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
6858
7171
  */
6859
7172
  static setScrollTop(el, value) {
6860
7173
  if (typeof value !== 'number') throw new TypeError('ScrollTop value must be a number.');
@@ -6868,11 +7181,13 @@ class TinyHtml_TinyHtml {
6868
7181
  elem.scrollTop = value;
6869
7182
  }
6870
7183
  });
7184
+ return el;
6871
7185
  }
6872
7186
 
6873
7187
  /**
6874
7188
  * Sets the vertical scroll position.
6875
7189
  * @param {number} value - Scroll top value.
7190
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
6876
7191
  */
6877
7192
  setScrollTop(value) {
6878
7193
  return TinyHtml_TinyHtml.setScrollTop(this, value);
@@ -6882,6 +7197,7 @@ class TinyHtml_TinyHtml {
6882
7197
  * Sets the horizontal scroll position.
6883
7198
  * @param {TinyElementAndWindow|TinyElementAndWindow[]} el - Element or window.
6884
7199
  * @param {number} value - Scroll left value.
7200
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
6885
7201
  */
6886
7202
  static setScrollLeft(el, value) {
6887
7203
  if (typeof value !== 'number') throw new TypeError('ScrollLeft value must be a number.');
@@ -6895,11 +7211,13 @@ class TinyHtml_TinyHtml {
6895
7211
  elem.scrollLeft = value;
6896
7212
  }
6897
7213
  });
7214
+ return el;
6898
7215
  }
6899
7216
 
6900
7217
  /**
6901
7218
  * Sets the horizontal scroll position.
6902
7219
  * @param {number} value - Scroll left value.
7220
+ * @returns {TinyElementAndWindow|TinyElementAndWindow[]}
6903
7221
  */
6904
7222
  setScrollLeft(value) {
6905
7223
  return TinyHtml_TinyHtml.setScrollLeft(this, value);
@@ -7030,15 +7348,16 @@ class TinyHtml_TinyHtml {
7030
7348
 
7031
7349
  /**
7032
7350
  * 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.
7351
+ * @type {(el: TinyElement|TinyElement[], ...tokens: string[]) => (TinyElement|TinyElement[])} - One or more class names to add.
7034
7352
  */
7035
7353
  static addClass(el, ...args) {
7036
7354
  TinyHtml_TinyHtml._preElems(el, 'addClass').forEach((elem) => elem.classList.add(...args));
7355
+ return el;
7037
7356
  }
7038
7357
 
7039
7358
  /**
7040
7359
  * Adds one or more CSS class names to the element.
7041
- * @type {(...tokens: string[]) => void} - One or more class names to add.
7360
+ * @type {(...tokens: string[]) => (TinyElement|TinyElement[])} - One or more class names to add.
7042
7361
  */
7043
7362
  addClass(...args) {
7044
7363
  return TinyHtml_TinyHtml.addClass(this, ...args);
@@ -7046,15 +7365,16 @@ class TinyHtml_TinyHtml {
7046
7365
 
7047
7366
  /**
7048
7367
  * 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.
7368
+ * @type {(el: TinyElement|TinyElement[], ...tokens: string[]) => (TinyElement|TinyElement[])} - One or more class names to remove.
7050
7369
  */
7051
7370
  static removeClass(el, ...args) {
7052
7371
  TinyHtml_TinyHtml._preElems(el, 'removeClass').forEach((elem) => elem.classList.remove(...args));
7372
+ return el;
7053
7373
  }
7054
7374
 
7055
7375
  /**
7056
7376
  * Removes one or more CSS class names from the element.
7057
- * @type {(...tokens: string[]) => void} - One or more class names to remove.
7377
+ * @type {(...tokens: string[]) => (TinyElement|TinyElement[])} - One or more class names to remove.
7058
7378
  */
7059
7379
  removeClass(...args) {
7060
7380
  return TinyHtml_TinyHtml.removeClass(this, ...args);
@@ -7264,15 +7584,18 @@ class TinyHtml_TinyHtml {
7264
7584
  * Set text content of elements.
7265
7585
  * @param {TinyElement|TinyElement[]} el
7266
7586
  * @param {string} value
7587
+ * @returns {TinyElement|TinyElement[]}
7267
7588
  */
7268
7589
  static setText(el, value) {
7269
7590
  if (typeof value !== 'string') throw new Error('Value is not a valid string.');
7270
7591
  TinyHtml_TinyHtml._preElems(el, 'setText').forEach((el) => (el.textContent = value));
7592
+ return el;
7271
7593
  }
7272
7594
 
7273
7595
  /**
7274
7596
  * Set text content of the element.
7275
7597
  * @param {string} value
7598
+ * @returns {TinyElement|TinyElement[]}
7276
7599
  */
7277
7600
  setText(value) {
7278
7601
  return TinyHtml_TinyHtml.setText(this, value);
@@ -7281,13 +7604,16 @@ class TinyHtml_TinyHtml {
7281
7604
  /**
7282
7605
  * Remove all child nodes from each element.
7283
7606
  * @param {TinyElement|TinyElement[]} el
7607
+ * @returns {TinyElement|TinyElement[]}
7284
7608
  */
7285
7609
  static empty(el) {
7286
7610
  TinyHtml_TinyHtml._preElems(el, 'empty').forEach((el) => (el.textContent = ''));
7611
+ return el;
7287
7612
  }
7288
7613
 
7289
7614
  /**
7290
7615
  * Remove all child nodes of the element.
7616
+ * @returns {TinyElement|TinyElement[]}
7291
7617
  */
7292
7618
  empty() {
7293
7619
  return TinyHtml_TinyHtml.empty(this);
@@ -7295,35 +7621,40 @@ class TinyHtml_TinyHtml {
7295
7621
 
7296
7622
  /**
7297
7623
  * Get the innerHTML of the element.
7298
- * @param {TinyElement|TinyElement[]} el
7624
+ * @param {TinyElement} el
7625
+ * @param {GetHTMLOptions} [ops]
7299
7626
  * @returns {string}
7300
7627
  */
7301
- static html(el) {
7628
+ static html(el, ops) {
7302
7629
  const elem = TinyHtml_TinyHtml._preElem(el, 'html');
7303
- return elem.innerHTML;
7630
+ return elem.getHTML(ops);
7304
7631
  }
7305
7632
 
7306
7633
  /**
7307
7634
  * Get the innerHTML of the element.
7635
+ * @param {GetHTMLOptions} [ops]
7308
7636
  * @returns {string}
7309
7637
  */
7310
- html() {
7311
- return TinyHtml_TinyHtml.html(this);
7638
+ html(ops) {
7639
+ return TinyHtml_TinyHtml.html(this, ops);
7312
7640
  }
7313
7641
 
7314
7642
  /**
7315
7643
  * Set the innerHTML of each element.
7316
7644
  * @param {TinyElement|TinyElement[]} el
7317
7645
  * @param {string} value
7646
+ * @returns {TinyElement|TinyElement[]}
7318
7647
  */
7319
7648
  static setHtml(el, value) {
7320
7649
  if (typeof value !== 'string') throw new Error('Value is not a valid string.');
7321
7650
  TinyHtml_TinyHtml._preElems(el, 'setHtml').forEach((el) => (el.innerHTML = value));
7651
+ return el;
7322
7652
  }
7323
7653
 
7324
7654
  /**
7325
7655
  * Set the innerHTML of the element.
7326
7656
  * @param {string} value
7657
+ * @returns {TinyElement|TinyElement[]}
7327
7658
  */
7328
7659
  setHtml(value) {
7329
7660
  return TinyHtml_TinyHtml.setHtml(this, value);
@@ -7457,6 +7788,7 @@ class TinyHtml_TinyHtml {
7457
7788
  * @param {TinyInputElement|TinyInputElement[]} el - Target element.
7458
7789
  * @param {SetValueList|((el: InputElement, val: SetValueList) => SetValueList)} value - The value to assign or a function that returns it.
7459
7790
  * @throws {Error} If the computed value is not a valid string or boolean.
7791
+ * @returns {TinyInputElement|TinyInputElement[]}
7460
7792
  */
7461
7793
  static setVal(el, value) {
7462
7794
  TinyHtml_TinyHtml._preInputElems(el, 'setVal').forEach((elem) => {
@@ -7496,6 +7828,7 @@ class TinyHtml_TinyHtml {
7496
7828
  if (typeof valToSet === 'string') elem.value = valToSet;
7497
7829
  }
7498
7830
  });
7831
+ return el;
7499
7832
  }
7500
7833
 
7501
7834
  /**
@@ -7503,6 +7836,7 @@ class TinyHtml_TinyHtml {
7503
7836
  * Accepts strings, numbers, booleans or arrays of these values, or a callback function that computes them.
7504
7837
  *
7505
7838
  * @param {SetValueList|((el: InputElement, val: SetValueList) => SetValueList)} value - The value to assign or a function that returns it.
7839
+ * @returns {TinyInputElement|TinyInputElement[]}
7506
7840
  * @throws {Error} If the computed value is not a valid string or boolean.
7507
7841
  */
7508
7842
  setVal(value) {
@@ -7772,6 +8106,7 @@ class TinyHtml_TinyHtml {
7772
8106
  * @param {string} event - The event type (e.g. 'click', 'keydown').
7773
8107
  * @param {EventRegistryHandle} handler - The callback function to run on event.
7774
8108
  * @param {EventRegistryOptions} [options] - Optional event listener options.
8109
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7775
8110
  */
7776
8111
  static on(el, event, handler, options) {
7777
8112
  if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
@@ -7784,6 +8119,7 @@ class TinyHtml_TinyHtml {
7784
8119
  if (!Array.isArray(events[event])) events[event] = [];
7785
8120
  events[event].push({ handler, options });
7786
8121
  });
8122
+ return el;
7787
8123
  }
7788
8124
 
7789
8125
  /**
@@ -7792,6 +8128,7 @@ class TinyHtml_TinyHtml {
7792
8128
  * @param {string} event - The event type (e.g. 'click', 'keydown').
7793
8129
  * @param {EventRegistryHandle} handler - The callback function to run on event.
7794
8130
  * @param {EventRegistryOptions} [options] - Optional event listener options.
8131
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7795
8132
  */
7796
8133
  on(event, handler, options) {
7797
8134
  return TinyHtml_TinyHtml.on(this, event, handler, options);
@@ -7804,6 +8141,7 @@ class TinyHtml_TinyHtml {
7804
8141
  * @param {string} event - The event type (e.g. 'click', 'keydown').
7805
8142
  * @param {EventRegistryHandle} handler - The callback function to run on event.
7806
8143
  * @param {EventRegistryOptions} [options={}] - Optional event listener options.
8144
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7807
8145
  */
7808
8146
  static once(el, event, handler, options = {}) {
7809
8147
  if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
@@ -7821,6 +8159,7 @@ class TinyHtml_TinyHtml {
7821
8159
  typeof options === 'boolean' ? options : { ...options, once: true },
7822
8160
  );
7823
8161
  });
8162
+ return el;
7824
8163
  }
7825
8164
 
7826
8165
  /**
@@ -7829,6 +8168,7 @@ class TinyHtml_TinyHtml {
7829
8168
  * @param {string} event - The event type (e.g. 'click', 'keydown').
7830
8169
  * @param {EventRegistryHandle} handler - The callback function to run on event.
7831
8170
  * @param {EventRegistryOptions} [options={}] - Optional event listener options.
8171
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7832
8172
  */
7833
8173
  once(event, handler, options = {}) {
7834
8174
  return TinyHtml_TinyHtml.once(this, event, handler, options);
@@ -7841,6 +8181,7 @@ class TinyHtml_TinyHtml {
7841
8181
  * @param {string} event - The event type.
7842
8182
  * @param {EventRegistryHandle} handler - The function originally bound to the event.
7843
8183
  * @param {boolean|EventListenerOptions} [options] - Optional listener options.
8184
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7844
8185
  */
7845
8186
  static off(el, event, handler, options) {
7846
8187
  if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
@@ -7853,6 +8194,7 @@ class TinyHtml_TinyHtml {
7853
8194
  if (events[event].length === 0) delete events[event];
7854
8195
  }
7855
8196
  });
8197
+ return el;
7856
8198
  }
7857
8199
 
7858
8200
  /**
@@ -7861,6 +8203,7 @@ class TinyHtml_TinyHtml {
7861
8203
  * @param {string} event - The event type.
7862
8204
  * @param {EventRegistryHandle} handler - The function originally bound to the event.
7863
8205
  * @param {boolean|EventListenerOptions} [options] - Optional listener options.
8206
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7864
8207
  */
7865
8208
  off(event, handler, options) {
7866
8209
  return TinyHtml_TinyHtml.off(this, event, handler, options);
@@ -7871,6 +8214,7 @@ class TinyHtml_TinyHtml {
7871
8214
  *
7872
8215
  * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
7873
8216
  * @param {string} event - The event type to remove (e.g. 'click').
8217
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7874
8218
  */
7875
8219
  static offAll(el, event) {
7876
8220
  if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
@@ -7883,12 +8227,14 @@ class TinyHtml_TinyHtml {
7883
8227
  delete events[event];
7884
8228
  }
7885
8229
  });
8230
+ return el;
7886
8231
  }
7887
8232
 
7888
8233
  /**
7889
8234
  * Removes all event listeners of a specific type from the element.
7890
8235
  *
7891
8236
  * @param {string} event - The event type to remove (e.g. 'click').
8237
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7892
8238
  */
7893
8239
  offAll(event) {
7894
8240
  return TinyHtml_TinyHtml.offAll(this, event);
@@ -7900,6 +8246,7 @@ class TinyHtml_TinyHtml {
7900
8246
  * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
7901
8247
  * @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
7902
8248
  * Optional filter function to selectively remove specific handlers.
8249
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7903
8250
  */
7904
8251
  static offAllTypes(el, filterFn = null) {
7905
8252
  if (filterFn !== null && typeof filterFn !== 'function')
@@ -7918,6 +8265,7 @@ class TinyHtml_TinyHtml {
7918
8265
 
7919
8266
  __eventRegistry.delete(elem);
7920
8267
  });
8268
+ return el;
7921
8269
  }
7922
8270
 
7923
8271
  /**
@@ -7925,6 +8273,7 @@ class TinyHtml_TinyHtml {
7925
8273
  *
7926
8274
  * @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
7927
8275
  * Optional filter function to selectively remove specific handlers.
8276
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7928
8277
  */
7929
8278
  offAllTypes(filterFn = null) {
7930
8279
  return TinyHtml_TinyHtml.offAllTypes(this, filterFn);
@@ -7936,6 +8285,7 @@ class TinyHtml_TinyHtml {
7936
8285
  * @param {TinyEventTarget|TinyEventTarget[]} el - Target element where the event should be triggered.
7937
8286
  * @param {string} event - Name of the event to trigger.
7938
8287
  * @param {Event|CustomEvent|CustomEventInit} [payload] - Optional event object or data to pass.
8288
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7939
8289
  */
7940
8290
  static trigger(el, event, payload = {}) {
7941
8291
  if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
@@ -7951,6 +8301,7 @@ class TinyHtml_TinyHtml {
7951
8301
 
7952
8302
  elem.dispatchEvent(evt);
7953
8303
  });
8304
+ return el;
7954
8305
  }
7955
8306
 
7956
8307
  /**
@@ -7958,6 +8309,7 @@ class TinyHtml_TinyHtml {
7958
8309
  *
7959
8310
  * @param {string} event - Name of the event to trigger.
7960
8311
  * @param {Event|CustomEvent|CustomEventInit} [payload] - Optional event object or data to pass.
8312
+ * @returns {TinyEventTarget|TinyEventTarget[]}
7961
8313
  */
7962
8314
  trigger(event, payload = {}) {
7963
8315
  return TinyHtml_TinyHtml.trigger(this, event, payload);
@@ -8000,6 +8352,7 @@ class TinyHtml_TinyHtml {
8000
8352
  * @param {TinyElement|TinyElement[]} el
8001
8353
  * @param {string} name
8002
8354
  * @param {string|null} [value=null]
8355
+ * @returns {TinyElement|TinyElement[]}
8003
8356
  */
8004
8357
  static setAttr(el, name, value = null) {
8005
8358
  if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
@@ -8009,12 +8362,14 @@ class TinyHtml_TinyHtml {
8009
8362
  if (value === null) elem.removeAttribute(name);
8010
8363
  else elem.setAttribute(name, value);
8011
8364
  });
8365
+ return el;
8012
8366
  }
8013
8367
 
8014
8368
  /**
8015
8369
  * Set an attribute on an element.
8016
8370
  * @param {string} name
8017
8371
  * @param {string|null} [value=null]
8372
+ * @returns {TinyElement|TinyElement[]}
8018
8373
  */
8019
8374
  setAttr(name, value) {
8020
8375
  return TinyHtml_TinyHtml.setAttr(this, name, value);
@@ -8024,15 +8379,18 @@ class TinyHtml_TinyHtml {
8024
8379
  * Remove attribute(s) from an element.
8025
8380
  * @param {TinyElement|TinyElement[]} el
8026
8381
  * @param {string} name Space-separated list of attributes.
8382
+ * @returns {TinyElement|TinyElement[]}
8027
8383
  */
8028
8384
  static removeAttr(el, name) {
8029
8385
  if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
8030
8386
  TinyHtml_TinyHtml._preElems(el, 'removeAttr').forEach((elem) => elem.removeAttribute(name));
8387
+ return el;
8031
8388
  }
8032
8389
 
8033
8390
  /**
8034
8391
  * Remove attribute(s) from an element.
8035
8392
  * @param {string} name Space-separated list of attributes.
8393
+ * @returns {TinyElement|TinyElement[]}
8036
8394
  */
8037
8395
  removeAttr(name) {
8038
8396
  return TinyHtml_TinyHtml.removeAttr(this, name);
@@ -8087,6 +8445,7 @@ class TinyHtml_TinyHtml {
8087
8445
  * Set a property on an element.
8088
8446
  * @param {TinyElement|TinyElement[]} el
8089
8447
  * @param {string} name
8448
+ * @returns {TinyElement|TinyElement[]}
8090
8449
  */
8091
8450
  static addProp(el, name) {
8092
8451
  if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
@@ -8096,11 +8455,13 @@ class TinyHtml_TinyHtml {
8096
8455
  // @ts-ignore
8097
8456
  elem[name] = true;
8098
8457
  });
8458
+ return el;
8099
8459
  }
8100
8460
 
8101
8461
  /**
8102
8462
  * Set a property on an element.
8103
8463
  * @param {string} name
8464
+ * @returns {TinyElement|TinyElement[]}
8104
8465
  */
8105
8466
  addProp(name) {
8106
8467
  return TinyHtml_TinyHtml.addProp(this, name);
@@ -8110,6 +8471,7 @@ class TinyHtml_TinyHtml {
8110
8471
  * Remove a property from an element.
8111
8472
  * @param {TinyElement|TinyElement[]} el
8112
8473
  * @param {string} name
8474
+ * @returns {TinyElement|TinyElement[]}
8113
8475
  */
8114
8476
  static removeProp(el, name) {
8115
8477
  if (typeof name !== 'string') throw new TypeError('The "name" must be a string.');
@@ -8119,11 +8481,13 @@ class TinyHtml_TinyHtml {
8119
8481
  // @ts-ignore
8120
8482
  elem[name] = false;
8121
8483
  });
8484
+ return el;
8122
8485
  }
8123
8486
 
8124
8487
  /**
8125
8488
  * Remove a property from an element.
8126
8489
  * @param {string} name
8490
+ * @returns {TinyElement|TinyElement[]}
8127
8491
  */
8128
8492
  removeProp(name) {
8129
8493
  return TinyHtml_TinyHtml.removeProp(this, name);
@@ -8164,13 +8528,16 @@ class TinyHtml_TinyHtml {
8164
8528
  /**
8165
8529
  * Removes an element from the DOM.
8166
8530
  * @param {TinyElement|TinyElement[]} el - The DOM element or selector to remove.
8531
+ * @returns {TinyElement|TinyElement[]}
8167
8532
  */
8168
8533
  static remove(el) {
8169
8534
  TinyHtml_TinyHtml._preElems(el, 'remove').forEach((elem) => elem.remove());
8535
+ return el;
8170
8536
  }
8171
8537
 
8172
8538
  /**
8173
8539
  * Removes the element from the DOM.
8540
+ * @returns {TinyElement|TinyElement[]}
8174
8541
  */
8175
8542
  remove() {
8176
8543
  return TinyHtml_TinyHtml.remove(this);
@@ -8514,6 +8881,14 @@ class TinyHtml_TinyHtml {
8514
8881
  */
8515
8882
  static isInViewport(el) {
8516
8883
  const elem = TinyHtml_TinyHtml._preElem(el, 'isInViewport');
8884
+ if (
8885
+ !elem.checkVisibility({
8886
+ contentVisibilityAuto: false,
8887
+ opacityProperty: false,
8888
+ visibilityProperty: false,
8889
+ })
8890
+ )
8891
+ return false;
8517
8892
  const elementTop = TinyHtml_TinyHtml.offset(elem).top;
8518
8893
  const elementBottom = elementTop + TinyHtml_TinyHtml.outerHeight(elem);
8519
8894
 
@@ -8540,6 +8915,14 @@ class TinyHtml_TinyHtml {
8540
8915
  */
8541
8916
  static isScrolledIntoView(el) {
8542
8917
  const elem = TinyHtml_TinyHtml._preElem(el, 'isScrolledIntoView');
8918
+ if (
8919
+ !elem.checkVisibility({
8920
+ contentVisibilityAuto: false,
8921
+ opacityProperty: false,
8922
+ visibilityProperty: false,
8923
+ })
8924
+ )
8925
+ return false;
8543
8926
  const docViewTop = TinyHtml_TinyHtml.scrollTop(window);
8544
8927
  const docViewBottom = docViewTop + TinyHtml_TinyHtml.height(window);
8545
8928
 
@@ -8557,6 +8940,111 @@ class TinyHtml_TinyHtml {
8557
8940
  isScrolledIntoView() {
8558
8941
  return TinyHtml_TinyHtml.isScrolledIntoView(this);
8559
8942
  }
8943
+
8944
+ /**
8945
+ * Checks if the given element is at least partially visible within the boundaries of a container.
8946
+ *
8947
+ * @param {TinyElement} el - The DOM element to check.
8948
+ * @param {TinyElement} cont - The container element acting as the viewport.
8949
+ * @returns {boolean} True if the element is partially visible within the container, false otherwise.
8950
+ */
8951
+ static isInContainer(el, cont) {
8952
+ const elem = TinyHtml_TinyHtml._preElem(el, 'isInContainer');
8953
+ const container = TinyHtml_TinyHtml._preElem(cont, 'isInContainer');
8954
+
8955
+ if (
8956
+ !elem.checkVisibility({
8957
+ contentVisibilityAuto: false,
8958
+ opacityProperty: false,
8959
+ visibilityProperty: false,
8960
+ })
8961
+ )
8962
+ return false;
8963
+ const elemRect = elem.getBoundingClientRect();
8964
+ const containerRect = container.getBoundingClientRect();
8965
+
8966
+ const verticallyVisible =
8967
+ elemRect.bottom > containerRect.top && elemRect.top < containerRect.bottom;
8968
+
8969
+ const horizontallyVisible =
8970
+ elemRect.right > containerRect.left && elemRect.left < containerRect.right;
8971
+
8972
+ return verticallyVisible && horizontallyVisible;
8973
+ }
8974
+
8975
+ /**
8976
+ * Checks if the given element is at least partially visible within the boundaries of a container.
8977
+ *
8978
+ * @param {TinyElement} cont - The container element acting as the viewport.
8979
+ * @returns {boolean} True if the element is partially visible within the container, false otherwise.
8980
+ */
8981
+ isInContainer(cont) {
8982
+ return TinyHtml_TinyHtml.isInContainer(this, cont);
8983
+ }
8984
+
8985
+ /**
8986
+ * Checks if the given element is fully visible within the boundaries of a container (top and bottom).
8987
+ *
8988
+ * @param {TinyElement} el - The DOM element to check.
8989
+ * @param {TinyElement} cont - The container element acting as the viewport.
8990
+ * @returns {boolean} True if the element is fully visible within the container, false otherwise.
8991
+ */
8992
+ static isFullyInContainer(el, cont) {
8993
+ const elem = TinyHtml_TinyHtml._preElem(el, 'isScrolledIntoView');
8994
+ const container = TinyHtml_TinyHtml._preElem(cont, 'isInContainer');
8995
+
8996
+ if (
8997
+ !elem.checkVisibility({
8998
+ contentVisibilityAuto: false,
8999
+ opacityProperty: false,
9000
+ visibilityProperty: false,
9001
+ })
9002
+ )
9003
+ return false;
9004
+ const elemRect = elem.getBoundingClientRect();
9005
+ const containerRect = container.getBoundingClientRect();
9006
+
9007
+ const isFullyVisible =
9008
+ elemRect.top >= containerRect.top &&
9009
+ elemRect.bottom <= containerRect.bottom &&
9010
+ elemRect.left >= containerRect.left &&
9011
+ elemRect.right <= containerRect.right;
9012
+
9013
+ return isFullyVisible;
9014
+ }
9015
+
9016
+ /**
9017
+ * Checks if the given element is fully visible within the boundaries of a container (top and bottom).
9018
+ *
9019
+ * @param {TinyElement} cont - The container element acting as the viewport.
9020
+ * @returns {boolean} True if the element is fully visible within the container, false otherwise.
9021
+ */
9022
+ isFullyInContainer(cont) {
9023
+ return TinyHtml_TinyHtml.isFullyInContainer(this, cont);
9024
+ }
9025
+
9026
+ /**
9027
+ * Checks if an element has scrollable content.
9028
+ *
9029
+ * @param {TinyElement} el - The DOM element to check.
9030
+ * @returns {{ v: boolean, h: boolean }} - True if scroll is needed in that direction.
9031
+ */
9032
+ static hasScroll(el) {
9033
+ const elem = TinyHtml_TinyHtml._preElem(el, 'hasScroll');
9034
+ return {
9035
+ v: elem.scrollHeight > elem.clientHeight,
9036
+ h: elem.scrollWidth > elem.clientWidth,
9037
+ };
9038
+ }
9039
+
9040
+ /**
9041
+ * Checks if an element has scrollable content.
9042
+ *
9043
+ * @returns {{ v: boolean, h: boolean }} - True if scroll is needed in that direction.
9044
+ */
9045
+ hasScroll() {
9046
+ return TinyHtml_TinyHtml.hasScroll(this);
9047
+ }
8560
9048
  }
8561
9049
 
8562
9050
  /* harmony default export */ const libs_TinyHtml = (TinyHtml_TinyHtml);
@@ -10327,7 +10815,985 @@ class TinyDragger {
10327
10815
 
10328
10816
  /* harmony default export */ const libs_TinyDragger = ((/* unused pure expression or super */ null && (TinyDragger)));
10329
10817
 
10330
- ;// ./src/v1/index.mjs
10818
+ ;// ./src/v1/libs/TinySmartScroller.mjs
10819
+
10820
+
10821
+
10822
+ /**
10823
+ * Represents the dimensions of a DOM element.
10824
+ *
10825
+ * @typedef {Object} NodeSizes
10826
+ * @property {number} height - The height of the element in pixels.
10827
+ * @property {number} width - The width of the element in pixels.
10828
+ */
10829
+
10830
+ /**
10831
+ * A callback function that receives node size change data and optionally returns a modified NodeSizes object.
10832
+ *
10833
+ * @callback NodeSizesEvent
10834
+ * @param {Element} elem - The DOM element whose size is being tracked.
10835
+ * @param {{ old: NodeSizes, now: NodeSizes }} sizes - The old and new size measurements of the element.
10836
+ * @param {{ old: number, now: number }} elemAmount - The number of matching elements before and after the update.
10837
+ * @returns {NodeSizes|undefined} A modified NodeSizes object to override the default measurement, or undefined to use the original.
10838
+ */
10839
+
10840
+ /**
10841
+ * A generic scroll-related event listener callback function.
10842
+ *
10843
+ * @callback ScrollListenersFunc
10844
+ * @param {any} payload - The data payload passed when the scroll event is triggered. The type may vary depending on the event.
10845
+ * @returns {void}
10846
+ */
10847
+
10848
+ /**
10849
+ * TinySmartScroller is a utility class designed to enhance and manage scroll behaviors within containers or the window.
10850
+ *
10851
+ * It enables advanced scroll monitoring, auto-scrolling to bottom, preserving scroll position during DOM changes,
10852
+ * and detecting visibility changes of elements. This is particularly useful for dynamic UIs like chat applications,
10853
+ * feed viewers, or live content containers.
10854
+ *
10855
+ * Features:
10856
+ * - Detects when the scroll reaches the top, bottom, or custom boundaries
10857
+ * - Supports automatic scrolling to the bottom unless the user scrolls away
10858
+ * - Observes DOM mutations and resizes, and adapts scroll position accordingly
10859
+ * - Emits scroll-related events such as 'onScrollBoundary', 'onAutoScroll', and 'onScrollPause'
10860
+ * - Includes customizable scroll correction filters for layout shift mitigation
10861
+ * - Handles media element load events (e.g. `<img>`, `<iframe>`, `<video>`) to prevent sudden scroll jumps
10862
+ *
10863
+ * This class is **not framework-dependent** and works with vanilla DOM elements and the window object.
10864
+ */
10865
+ class TinySmartScroller {
10866
+ static Utils = { ...collision_namespaceObject, TinyHtml: libs_TinyHtml };
10867
+
10868
+ /** @type {WeakMap<Element, NodeSizes>} */
10869
+ #oldSizes = new WeakMap();
10870
+ /** @type {WeakMap<Element, NodeSizes>} */
10871
+ #newSizes = new WeakMap();
10872
+
10873
+ /** @type {WeakMap<Element, boolean>} */
10874
+ #newVisibles = new WeakMap();
10875
+ /** @type {WeakMap<Element, boolean>} */
10876
+ #oldVisibles = new WeakMap();
10877
+
10878
+ /** @type {WeakMap<Element, boolean>} */
10879
+ #newVisiblesByTime = new WeakMap();
10880
+ /** @type {WeakMap<Element, boolean>} */
10881
+ #oldVisiblesByTime = new WeakMap();
10882
+
10883
+ /** @type {Record<string, ScrollListenersFunc[]>} */
10884
+ #scrollListeners = {};
10885
+
10886
+ /** @type {ResizeObserver|null} */
10887
+ #resizeObserver = null;
10888
+ /** @type {MutationObserver|null} */
10889
+ #mutationObserver = null;
10890
+
10891
+ /** @type {Set<string>} */
10892
+ #loadTags = new Set(['IMG', 'IFRAME', 'VIDEO']);
10893
+
10894
+ /** @type {null|EventListenerOrEventListenerObject} */
10895
+ #handler = null;
10896
+
10897
+ #isAtBottom = false;
10898
+ #isAtTop = false;
10899
+ #isAtCustomTop = false;
10900
+ #isAtCustomBottom = false;
10901
+
10902
+ #querySelector = '';
10903
+ #useWindow = false;
10904
+ #destroyed = false;
10905
+ #scrollPaused = false;
10906
+ #autoScrollBottom = false;
10907
+ #observeMutations = false;
10908
+ #preserveScrollOnLayoutShift = false;
10909
+ #debounceTime = 0;
10910
+ #elemAmount = 0;
10911
+ #elemOldAmount = 0;
10912
+ #lastKnownScrollBottomOffset = 0;
10913
+ #extraScrollBoundary = 0;
10914
+
10915
+ /** @type {Set<string>} */
10916
+ #attributeFilter;
10917
+
10918
+ /** @type {Element} */
10919
+ #target;
10920
+
10921
+ /** @type {Set<NodeSizesEvent>} */
10922
+ #sizeFilter = new Set();
10923
+
10924
+ /**
10925
+ * Creates a new instance of TinySmartScroller, attaching scroll and resize observers to manage
10926
+ * automatic scroll behaviors, layout shift correction, and visibility tracking.
10927
+ *
10928
+ * @param {Element|Window} target - The scroll container to monitor. Can be an element or `window`.
10929
+ * @param {Object} [options={}] - Optional settings to configure scroll behavior.
10930
+ * @param {number} [options.extraScrollBoundary=0] - Extra margin in pixels to extend scroll boundary detection.
10931
+ * @param {boolean} [options.autoScrollBottom=true] - Whether to auto-scroll to bottom on layout updates.
10932
+ * @param {boolean} [options.observeMutations=true] - Enables MutationObserver to detect DOM changes.
10933
+ * @param {boolean} [options.preserveScrollOnLayoutShift=true] - Prevents scroll jumps when layout changes.
10934
+ * @param {number} [options.debounceTime=100] - Debounce time in milliseconds for scroll events.
10935
+ * @param {string|null} [options.querySelector=null] - Optional CSS selector to filter observed child nodes.
10936
+ * @param {string[]|Set<string>|null} [options.attributeFilter=['class', 'style', 'src', 'data-*', 'height', 'width']]
10937
+ * - Which attributes to observe for changes.
10938
+ */
10939
+ constructor(
10940
+ target,
10941
+ {
10942
+ extraScrollBoundary = 0,
10943
+ autoScrollBottom = true,
10944
+ observeMutations = true,
10945
+ preserveScrollOnLayoutShift = true,
10946
+ debounceTime = 100,
10947
+ querySelector = null,
10948
+ attributeFilter = ['class', 'style', 'src', 'data-*', 'height', 'width'],
10949
+ } = {},
10950
+ ) {
10951
+ // === target ===
10952
+ if (!(target instanceof Element || target === window))
10953
+ throw new TypeError(
10954
+ `TinySmartScroller: 'target' must be a DOM Element or 'window', but got ${typeof target}`,
10955
+ );
10956
+ // === extraScrollBoundary ===
10957
+ if (typeof extraScrollBoundary !== 'number' || Number.isNaN(extraScrollBoundary))
10958
+ throw new TypeError(
10959
+ `TinySmartScroller: 'extraScrollBoundary' must be a valid number, received ${extraScrollBoundary}`,
10960
+ );
10961
+ // === autoScrollBottom ===
10962
+ if (typeof autoScrollBottom !== 'boolean')
10963
+ throw new TypeError(
10964
+ `TinySmartScroller: 'autoScrollBottom' must be a boolean, received ${typeof autoScrollBottom}`,
10965
+ );
10966
+ // === observeMutations ===
10967
+ if (typeof observeMutations !== 'boolean')
10968
+ throw new TypeError(
10969
+ `TinySmartScroller: 'observeMutations' must be a boolean, received ${typeof observeMutations}`,
10970
+ );
10971
+ // === preserveScrollOnLayoutShift ===
10972
+ if (typeof preserveScrollOnLayoutShift !== 'boolean')
10973
+ throw new TypeError(
10974
+ `TinySmartScroller: 'preserveScrollOnLayoutShift' must be a boolean, received ${typeof preserveScrollOnLayoutShift}`,
10975
+ );
10976
+ // === debounceTime ===
10977
+ if (typeof debounceTime !== 'number' || debounceTime < 0 || Number.isNaN(debounceTime))
10978
+ throw new TypeError(
10979
+ `TinySmartScroller: 'debounceTime' must be a non-negative number, received ${debounceTime}`,
10980
+ );
10981
+ // === querySelector ===
10982
+ if (querySelector !== null && typeof querySelector !== 'string')
10983
+ throw new TypeError(
10984
+ `TinySmartScroller: 'querySelector' must be a string or null, received ${typeof querySelector}`,
10985
+ );
10986
+ // === attributeFilter ===
10987
+ const isValidAttrList =
10988
+ attributeFilter === null || Array.isArray(attributeFilter) || attributeFilter instanceof Set;
10989
+ if (!isValidAttrList)
10990
+ throw new TypeError(
10991
+ `TinySmartScroller: 'attributeFilter' must be an array, Set, or null. Got ${typeof attributeFilter}`,
10992
+ );
10993
+
10994
+ // Start values
10995
+ this.#target = target instanceof Window ? document.documentElement : target;
10996
+ this.#useWindow = target instanceof Window;
10997
+ this.#autoScrollBottom = autoScrollBottom;
10998
+ this.#observeMutations = observeMutations;
10999
+ this.#preserveScrollOnLayoutShift = preserveScrollOnLayoutShift;
11000
+ this.#debounceTime = debounceTime;
11001
+ this.#extraScrollBoundary = extraScrollBoundary;
11002
+ this.#querySelector = querySelector || '';
11003
+ this.#attributeFilter = new Set(attributeFilter || undefined);
11004
+
11005
+ // Bind scroll
11006
+ /** @type {NodeJS.Timeout} */
11007
+ let timeout;
11008
+ this.#handler = () => {
11009
+ this._scrollDataUpdater();
11010
+ clearTimeout(timeout);
11011
+ timeout = setTimeout(() => this._onScroll(), this.#debounceTime);
11012
+ };
11013
+ (this.#useWindow ? window : this.#target).addEventListener('scroll', this.#handler, {
11014
+ passive: true,
11015
+ });
11016
+
11017
+ // Mutations
11018
+ if (this.#observeMutations) {
11019
+ this._observeMutations();
11020
+ this._observeResizes(this.#target.children);
11021
+ }
11022
+
11023
+ this._scrollDataUpdater();
11024
+ }
11025
+
11026
+ /**
11027
+ * Returns a size difference callback that only reacts when height changes, filtered by tag name.
11028
+ *
11029
+ * @param {string[]} filter - List of tag names to allow. If empty, all tags are accepted.
11030
+ * @returns {NodeSizesEvent} A function that compares previous and current height, returning height delta.
11031
+ */
11032
+ getSimpleOnHeight(filter = []) {
11033
+ if (!Array.isArray(filter))
11034
+ throw new TypeError('getSimpleOnHeight(filter): filter must be an array of tag names');
11035
+ return (elem, sizes, amounts) => {
11036
+ if ((filter.length > 0 && !filter.includes(elem.tagName)) || amounts.now !== amounts.old)
11037
+ return;
11038
+ const oldSize = sizes.old;
11039
+ const newSize = sizes.now;
11040
+ const height = newSize.height - oldSize.height;
11041
+ return { height, width: 0 };
11042
+ };
11043
+ }
11044
+
11045
+ /**
11046
+ * Adds a height difference callback to the size filter system.
11047
+ *
11048
+ * @param {string[]} filter - List of tag names to allow.
11049
+ * @returns {NodeSizesEvent} The added size difference callback.
11050
+ */
11051
+ addSimpleOnHeight(filter) {
11052
+ if (!Array.isArray(filter))
11053
+ throw new TypeError('addSimpleOnHeight(filter): filter must be an array of tag names');
11054
+ const result = this.getSimpleOnHeight(filter);
11055
+ this.onSize(result);
11056
+ return result;
11057
+ }
11058
+
11059
+ /**
11060
+ * Returns a list of all currently tracked load tags.
11061
+ *
11062
+ * @returns {string[]} Array of tag names.
11063
+ */
11064
+ getLoadTags() {
11065
+ return Array.from(this.#loadTags);
11066
+ }
11067
+
11068
+ /**
11069
+ * Adds a new tag to the set of load tags.
11070
+ *
11071
+ * @param {string} tag - The tag name to add (e.g., 'IMG').
11072
+ */
11073
+ addLoadTag(tag) {
11074
+ if (typeof tag !== 'string') throw new TypeError('addLoadTag(tag): tag must be a string');
11075
+ this.#loadTags.add(tag.toUpperCase());
11076
+ }
11077
+
11078
+ /**
11079
+ * Removes a tag from the set of load tags.
11080
+ *
11081
+ * @param {string} tag - The tag name to remove.
11082
+ */
11083
+ removeLoadTag(tag) {
11084
+ if (typeof tag !== 'string') throw new TypeError('removeLoadTag(tag): tag must be a string');
11085
+ this.#loadTags.delete(tag.toUpperCase());
11086
+ }
11087
+
11088
+ /**
11089
+ * Checks whether a tag is tracked as a load tag.
11090
+ *
11091
+ * @param {string} tag - The tag name to check.
11092
+ * @returns {boolean} True if the tag is being tracked.
11093
+ */
11094
+ hasLoadTag(tag) {
11095
+ if (typeof tag !== 'string') throw new TypeError('hasLoadTag(tag): tag must be a string');
11096
+ return this.#loadTags.has(tag.toUpperCase());
11097
+ }
11098
+
11099
+ /**
11100
+ * Clears the set of load tags. If `addDefault` is true, it will reset to the default tags: 'IMG', 'IFRAME', and 'VIDEO'.
11101
+ *
11102
+ * @param {boolean} [addDefault=false] - Whether to restore the default tags after clearing.
11103
+ * @throws {TypeError} If `addDefault` is not a boolean.
11104
+ */
11105
+ resetLoadTags(addDefault = false) {
11106
+ if (typeof addDefault !== 'boolean')
11107
+ throw new TypeError('resetLoadTags(addDefault): addDefault must be a boolean');
11108
+ this.#loadTags.clear();
11109
+ if (!addDefault) return;
11110
+ this.#loadTags.add('IMG');
11111
+ this.#loadTags.add('IFRAME');
11112
+ this.#loadTags.add('VIDEO');
11113
+ }
11114
+
11115
+ /**
11116
+ * Returns a list of all currently tracked attribute filters.
11117
+ *
11118
+ * @returns {string[]} Array of attribute names.
11119
+ */
11120
+ getAttributeFilters() {
11121
+ return Array.from(this.#attributeFilter);
11122
+ }
11123
+
11124
+ /**
11125
+ * Adds an attribute to the filter list.
11126
+ *
11127
+ * @param {string} attr - The attribute name to add.
11128
+ */
11129
+ addAttributeFilter(attr) {
11130
+ if (typeof attr !== 'string')
11131
+ throw new TypeError('addAttributeFilter(attr): attr must be a string');
11132
+ this.#attributeFilter.add(attr);
11133
+ }
11134
+
11135
+ /**
11136
+ * Removes an attribute from the filter list.
11137
+ *
11138
+ * @param {string} attr - The attribute name to remove.
11139
+ */
11140
+ removeAttributeFilter(attr) {
11141
+ if (typeof attr !== 'string')
11142
+ throw new TypeError('removeAttributeFilter(attr): attr must be a string');
11143
+ this.#attributeFilter.delete(attr);
11144
+ }
11145
+
11146
+ /**
11147
+ * Checks whether a specific attribute is being filtered.
11148
+ *
11149
+ * @param {string} attr - The attribute name to check.
11150
+ * @returns {boolean} True if the attribute is being filtered.
11151
+ */
11152
+ hasAttributeFilter(attr) {
11153
+ if (typeof attr !== 'string')
11154
+ throw new TypeError('hasAttributeFilter(attr): attr must be a string');
11155
+ return this.#attributeFilter.has(attr);
11156
+ }
11157
+
11158
+ /**
11159
+ * Clears the set of observed attribute filters. If `addDefault` is true, it will reset to the default attributes:
11160
+ * 'class', 'style', 'src', 'data-*', 'height', and 'width'.
11161
+ *
11162
+ * @param {boolean} [addDefault=false] - Whether to restore the default attribute filters after clearing.
11163
+ * @throws {TypeError} If `addDefault` is not a boolean.
11164
+ */
11165
+ resetAttributeFilters(addDefault = false) {
11166
+ if (typeof addDefault !== 'boolean')
11167
+ throw new TypeError('resetAttributeFilters(addDefault): addDefault must be a boolean');
11168
+ this.#attributeFilter.clear();
11169
+ if (!addDefault) return;
11170
+ ['class', 'style', 'src', 'data-*', 'height', 'width'].forEach((attr) =>
11171
+ this.#attributeFilter.add(attr),
11172
+ );
11173
+ }
11174
+
11175
+ /**
11176
+ * Registers a custom node size change handler to the internal size filter set.
11177
+ *
11178
+ * @param {NodeSizesEvent} handler - Function that compares old and new sizes.
11179
+ */
11180
+ onSize(handler) {
11181
+ if (this.#destroyed) return;
11182
+ if (typeof handler !== 'function')
11183
+ throw new TypeError('onSize(handler): handler must be a function');
11184
+ this.#sizeFilter.add(handler);
11185
+ }
11186
+
11187
+ /**
11188
+ * Unregisters a previously registered size handler from the internal filter set.
11189
+ *
11190
+ * @param {NodeSizesEvent} handler - The handler function to remove.
11191
+ */
11192
+ offSize(handler) {
11193
+ if (this.#destroyed) return;
11194
+ if (typeof handler !== 'function')
11195
+ throw new TypeError('offSize(handler): handler must be a function');
11196
+ this.#sizeFilter.delete(handler);
11197
+ }
11198
+
11199
+ /**
11200
+ * Adds a scroll-related event listener.
11201
+ *
11202
+ * @param {string} event - Event name, such as 'onScrollBoundary' or 'onAutoScroll'.
11203
+ * @param {ScrollListenersFunc} handler - Callback function to be called when event fires.
11204
+ */
11205
+ on(event, handler) {
11206
+ if (this.#destroyed) return;
11207
+ if (typeof event !== 'string')
11208
+ throw new TypeError('on(event, handler): event name must be a string');
11209
+ if (typeof handler !== 'function')
11210
+ throw new TypeError('on(event, handler): handler must be a function');
11211
+
11212
+ if (!this.#scrollListeners[event]) this.#scrollListeners[event] = [];
11213
+ this.#scrollListeners[event].push(handler);
11214
+ }
11215
+
11216
+ /**
11217
+ * Removes a previously registered scroll-related event listener.
11218
+ *
11219
+ * @param {string} event - The name of the event to remove the handler from.
11220
+ * @param {ScrollListenersFunc} handler - The specific callback function to remove.
11221
+ */
11222
+ off(event, handler) {
11223
+ if (this.#destroyed) return;
11224
+ if (typeof event !== 'string')
11225
+ throw new TypeError('off(event, handler): event name must be a string');
11226
+ if (typeof handler !== 'function')
11227
+ throw new TypeError('off(event, handler): handler must be a function');
11228
+
11229
+ const listeners = this.#scrollListeners[event];
11230
+ if (!Array.isArray(listeners)) return;
11231
+
11232
+ const index = listeners.indexOf(handler);
11233
+ if (index !== -1) listeners.splice(index, 1);
11234
+
11235
+ // Optionally clean up empty arrays (optional)
11236
+ if (listeners.length === 0) delete this.#scrollListeners[event];
11237
+ }
11238
+
11239
+ /**
11240
+ * Checks which elements inside the target are currently visible and updates internal maps.
11241
+ *
11242
+ * @returns {Map<Element, { oldIsVisible: boolean; isVisible: boolean }>} Visibility comparison results.
11243
+ */
11244
+ _scrollDataUpdater() {
11245
+ const results = new Map();
11246
+ this.#target.querySelectorAll(this.#querySelector || '*').forEach((target) => {
11247
+ const oldIsVisible = this.#newVisibles.get(target) ?? false;
11248
+ this.#oldVisibles.set(target, oldIsVisible);
11249
+
11250
+ const isVisible = libs_TinyHtml.isInContainer(this.#target, target);
11251
+ this.#newVisibles.set(target, isVisible);
11252
+
11253
+ results.set(target, { oldIsVisible, isVisible });
11254
+ });
11255
+ return results;
11256
+ }
11257
+
11258
+ /**
11259
+ * Emits a scroll-related event to all registered listeners.
11260
+ *
11261
+ * @param {string} event - Event name.
11262
+ * @param {*} [payload] - Optional event data payload.
11263
+ */
11264
+ _emit(event, payload) {
11265
+ if (this.#destroyed) return;
11266
+ if (typeof event !== 'string')
11267
+ throw new TypeError('_emit(event, payload): event name must be a string');
11268
+ (this.#scrollListeners[event] || []).forEach((fn) => fn(payload));
11269
+ }
11270
+
11271
+ /**
11272
+ * Handles scroll events, calculates position-related statuses, and emits appropriate events.
11273
+ */
11274
+ _onScroll() {
11275
+ if (this.#destroyed) return;
11276
+ // Get values
11277
+ const scrollCache = this._scrollDataUpdater();
11278
+ const el = this.#target;
11279
+ const scrollTop = el.scrollTop;
11280
+ const scrollHeight = el.scrollHeight;
11281
+ const clientHeight = el.clientHeight;
11282
+
11283
+ // Prepare sroll values
11284
+ const scrollResult = { scrollTop, scrollHeight, clientHeight };
11285
+ let atResult = null;
11286
+ let atCustomResult = null;
11287
+
11288
+ const atTop = scrollTop === 0;
11289
+ const atBottom = scrollTop + clientHeight >= scrollHeight - 1;
11290
+
11291
+ const atCustomTop = scrollTop <= 0 + this.#extraScrollBoundary;
11292
+ const atCustomBottom = scrollTop + clientHeight >= scrollHeight - 1 - this.#extraScrollBoundary;
11293
+
11294
+ // Scroll results
11295
+ if (atTop && atBottom) atResult = 'all';
11296
+ else if (atTop) atResult = 'top';
11297
+ else if (atBottom) atResult = 'bottom';
11298
+
11299
+ if (atCustomTop && atCustomBottom) atCustomResult = 'all';
11300
+ else if (atCustomTop) atCustomResult = 'top';
11301
+ else if (atCustomBottom) atCustomResult = 'bottom';
11302
+
11303
+ this.#isAtTop = atTop;
11304
+ this.#isAtBottom = atBottom;
11305
+
11306
+ this.#isAtCustomTop = atCustomTop;
11307
+ this.#isAtCustomBottom = atCustomBottom;
11308
+
11309
+ this.#scrollPaused = !(this.#autoScrollBottom && this.#isAtBottom);
11310
+
11311
+ this.#lastKnownScrollBottomOffset = scrollHeight - scrollTop - clientHeight;
11312
+
11313
+ // Send results
11314
+ this._emit('onScrollBoundary', { status: atResult, ...scrollResult, scrollCache });
11315
+ this._emit('onExtraScrollBoundary', { status: atCustomResult, ...scrollResult, scrollCache });
11316
+
11317
+ if (!this.#scrollPaused) {
11318
+ this._emit('onAutoScroll', { ...scrollResult, scrollCache });
11319
+ } else {
11320
+ this._emit('onScrollPause', { ...scrollResult, scrollCache });
11321
+ }
11322
+ }
11323
+
11324
+ /**
11325
+ * Attempts to correct the scroll position when layout shifts happen, preserving the user position if needed.
11326
+ *
11327
+ * @param {Element[]} [targets=[]] - List of elements involved in the size change.
11328
+ */
11329
+ _fixScroll(targets = []) {
11330
+ if (this.#destroyed) return;
11331
+ // === Validation ===
11332
+ if (!Array.isArray(targets))
11333
+ throw new TypeError('_fixScroll: targets must be an array of Elements');
11334
+
11335
+ // Get Scroll data
11336
+ const prevScrollHeight = this.#target.scrollHeight;
11337
+ const prevScrollTop = this.#target.scrollTop;
11338
+ const prevBottomOffset =
11339
+ this.#target.scrollHeight - this.#target.scrollTop - this.#target.clientHeight;
11340
+
11341
+ // Get new size
11342
+ const newScrollHeight = this.#target.scrollHeight;
11343
+ const heightDelta = newScrollHeight - prevScrollHeight;
11344
+
11345
+ /** @type {() => NodeSizes} */
11346
+ const calculateScrollSize = () => {
11347
+ // Run size getter
11348
+ const scrollSize = { height: 0, width: 0 };
11349
+ for (const target of targets) {
11350
+ const tgOs = this.#oldSizes.get(target) || { height: 0, width: 0 };
11351
+ const tgNs = this.#newSizes.get(target) || { height: 0, width: 0 };
11352
+ this.#sizeFilter.forEach((fn) => {
11353
+ /** @type {NodeSizes| undefined} */
11354
+ const sizes = fn(
11355
+ target,
11356
+ { old: tgOs, now: tgNs },
11357
+ { old: this.#elemOldAmount, now: this.#elemAmount },
11358
+ );
11359
+
11360
+ // Fix size
11361
+ if (this.#newVisibles.get(target) || this.#newVisiblesByTime.get(target)) {
11362
+ if (typeof sizes !== 'undefined' && typeof sizes !== 'object')
11363
+ throw new Error('_fixScroll: size filter must return an object or undefined');
11364
+ if (typeof sizes === 'undefined') return;
11365
+ scrollSize.height = sizes.height;
11366
+ scrollSize.width = sizes.width;
11367
+ }
11368
+ });
11369
+ }
11370
+
11371
+ // Checker
11372
+ if (typeof scrollSize.height !== 'number' && scrollSize.height < 0)
11373
+ throw new Error('_fixScroll: invalid scrollSize.height value');
11374
+ if (typeof scrollSize.width !== 'number' && scrollSize.width < 0)
11375
+ throw new Error('_fixScroll: invalid scrollSize.width value');
11376
+
11377
+ if (scrollSize.height !== 0 || scrollSize.width !== 0) {
11378
+ for (const target of targets) {
11379
+ this.#newVisiblesByTime.set(target, this.#newVisibles.get(target) ?? false);
11380
+ this.#oldVisiblesByTime.set(target, this.#oldVisibles.get(target) ?? false);
11381
+ }
11382
+ }
11383
+
11384
+ return scrollSize;
11385
+ };
11386
+
11387
+ // Fix scroll size
11388
+ if (
11389
+ this.#elemOldAmount > 0 &&
11390
+ libs_TinyHtml.hasScroll(this.#target).v &&
11391
+ this.#autoScrollBottom &&
11392
+ this.#preserveScrollOnLayoutShift &&
11393
+ !this.#isAtBottom &&
11394
+ !this.#isAtTop
11395
+ ) {
11396
+ const scrollSize = calculateScrollSize();
11397
+ // Complete
11398
+ this.#target.scrollTop = prevScrollTop + heightDelta + scrollSize.height;
11399
+ if (scrollSize.width > 0)
11400
+ this.#target.scrollLeft = this.#target.scrollLeft + scrollSize.width;
11401
+ }
11402
+
11403
+ // Normal stuff
11404
+ else if (!this.#scrollPaused && this.#autoScrollBottom) {
11405
+ calculateScrollSize();
11406
+ this.scrollToBottom();
11407
+ } else if (!this.#autoScrollBottom && !this.#isAtBottom) {
11408
+ calculateScrollSize();
11409
+ this.#target.scrollTop =
11410
+ this.#target.scrollHeight - this.#target.clientHeight - prevBottomOffset;
11411
+ }
11412
+ }
11413
+ /**
11414
+ * Sets up a MutationObserver to watch for DOM changes and react accordingly to maintain scroll consistency.
11415
+ */
11416
+ _observeMutations() {
11417
+ this.#mutationObserver = new MutationObserver((mutations) => {
11418
+ if (this.#destroyed) return;
11419
+ this._scrollDataUpdater();
11420
+ this.#elemOldAmount = this.#elemAmount;
11421
+ this.#elemAmount = this.#target.childElementCount;
11422
+
11423
+ mutations.forEach((mutation) => {
11424
+ mutation.addedNodes.forEach((node) => {
11425
+ if (!(node instanceof Element) || node.nodeType !== 1) return;
11426
+
11427
+ this._observeResizes([node]);
11428
+ this._listenLoadEvents(node);
11429
+
11430
+ if (this.#querySelector) {
11431
+ const children = node.querySelectorAll(this.#querySelector);
11432
+ this._observeResizes(children);
11433
+ this._listenLoadEvents(children);
11434
+ }
11435
+ });
11436
+ });
11437
+
11438
+ this._fixScroll();
11439
+ });
11440
+
11441
+ // Install observer
11442
+ this.#mutationObserver.observe(this.#target, {
11443
+ childList: true,
11444
+ subtree: true,
11445
+ attributes: true,
11446
+ attributeFilter:
11447
+ this.#attributeFilter.size > 0 ? Array.from(this.#attributeFilter) : undefined,
11448
+ });
11449
+ }
11450
+
11451
+ /**
11452
+ * Adds a ResizeObserver to monitor elements' size changes and trigger layout adjustments.
11453
+ *
11454
+ * @param {NodeListOf<Element>|Element[]|HTMLCollection} elements - Elements to observe.
11455
+ */
11456
+ _observeResizes(elements) {
11457
+ // Add resize observer
11458
+ if (!this.#resizeObserver) {
11459
+ this.#resizeObserver = new ResizeObserver((entries) => {
11460
+ if (this.#destroyed) return;
11461
+ this._scrollDataUpdater();
11462
+ /** @type {Element[]} */
11463
+ const targets = [];
11464
+ for (const entry of entries) {
11465
+ // Target
11466
+ const target = entry.target;
11467
+
11468
+ // Update old size
11469
+ const oldSize = this.#newSizes.get(target);
11470
+ if (oldSize) this.#oldSizes.set(target, oldSize);
11471
+
11472
+ // Set new size
11473
+ const { width, height } = entry.contentRect;
11474
+ this.#newSizes.set(target, { width, height });
11475
+ targets.push(target);
11476
+ }
11477
+
11478
+ // Animation frame
11479
+ this._fixScroll(targets);
11480
+ });
11481
+ }
11482
+
11483
+ // Execute observer
11484
+ Array.from(elements).forEach((el) => {
11485
+ if (!this.#resizeObserver)
11486
+ throw new Error('_observeResizes: ResizeObserver instance is not initialized');
11487
+ this.#resizeObserver.observe(el);
11488
+ });
11489
+ }
11490
+
11491
+ /**
11492
+ * Listens for media/content load events (e.g., images, iframes, videos) to trigger scroll updates.
11493
+ *
11494
+ * @param {NodeListOf<Element>|Element} elements - Target element(s) to listen on.
11495
+ */
11496
+ _listenLoadEvents(elements) {
11497
+ if (this.#destroyed) return;
11498
+ const list = elements instanceof NodeList ? Array.from(elements) : [elements];
11499
+
11500
+ list.forEach((el) => {
11501
+ if (this.#loadTags.has(el.tagName)) {
11502
+ // @ts-ignore
11503
+ if (!el.complete) {
11504
+ el.addEventListener('load', () => {
11505
+ this._scrollDataUpdater();
11506
+ if (!this.#scrollPaused && this.#autoScrollBottom) {
11507
+ this.scrollToBottom();
11508
+ }
11509
+ });
11510
+ }
11511
+ }
11512
+ });
11513
+ }
11514
+
11515
+ /**
11516
+ * Returns the internal scroll container element being monitored.
11517
+ *
11518
+ * @returns {Element} The DOM element used as the scroll container target.
11519
+ */
11520
+ get target() {
11521
+ return this.#target;
11522
+ }
11523
+
11524
+ /**
11525
+ * Returns the previous size of a given element, or undefined if not tracked.
11526
+ *
11527
+ * @param {Element} el - The DOM element to query.
11528
+ * @returns {NodeSizes|null} The old size, or undefined.
11529
+ */
11530
+ getOldSize(el) {
11531
+ return this.#oldSizes.get(el) ?? null;
11532
+ }
11533
+
11534
+ /**
11535
+ * Returns the current size of a given element, or undefined if not tracked.
11536
+ *
11537
+ * @param {Element} el - The DOM element to query.
11538
+ * @returns {NodeSizes|null} The new size, or undefined.
11539
+ */
11540
+ getNewSize(el) {
11541
+ return this.#newSizes.get(el) ?? null;
11542
+ }
11543
+
11544
+ /**
11545
+ * Returns whether the given element was visible in the last scroll update.
11546
+ *
11547
+ * @param {Element} el - The DOM element to check.
11548
+ * @returns {boolean} True if visible, false if not, or undefined if not tracked.
11549
+ */
11550
+ wasVisible(el) {
11551
+ return this.#oldVisibles.get(el) ?? false;
11552
+ }
11553
+
11554
+ /**
11555
+ * Returns whether the given element is currently visible.
11556
+ *
11557
+ * @param {Element} el - The DOM element to check.
11558
+ * @returns {boolean} True if visible, false if not, or undefined if not tracked.
11559
+ */
11560
+ isVisible(el) {
11561
+ return this.#newVisibles.get(el) ?? false;
11562
+ }
11563
+
11564
+ /**
11565
+ * Returns whether the element was visible in the last time-based visibility check.
11566
+ *
11567
+ * @param {Element} el - The DOM element to check.
11568
+ * @returns {boolean} Visibility state from the previous timed check.
11569
+ */
11570
+ wasTimedVisible(el) {
11571
+ return this.#oldVisiblesByTime.get(el) ?? false;
11572
+ }
11573
+
11574
+ /**
11575
+ * Returns whether the element is currently visible in the time-based check.
11576
+ *
11577
+ * @param {Element} el - The DOM element to check.
11578
+ * @returns {boolean} Visibility state from the current timed check.
11579
+ */
11580
+ isTimedVisible(el) {
11581
+ return this.#newVisiblesByTime.get(el) ?? false;
11582
+ }
11583
+
11584
+ /**
11585
+ * Sets the extra scroll boundary margin used when determining if the user is at a "custom" bottom or top.
11586
+ *
11587
+ * @param {number} value - Pixels of additional margin to use.
11588
+ */
11589
+ setExtraScrollBoundary(value) {
11590
+ if (typeof value !== 'number' || Number.isNaN(value))
11591
+ throw new TypeError('setExtraScrollBoundary(value): value must be a valid number');
11592
+ this.#extraScrollBoundary = value;
11593
+ }
11594
+
11595
+ /**
11596
+ * Returns the current extra scroll boundary setting.
11597
+ *
11598
+ * @returns {number}
11599
+ */
11600
+ getExtraScrollBoundary() {
11601
+ return this.#extraScrollBoundary;
11602
+ }
11603
+
11604
+ /**
11605
+ * Returns the last known distance (in pixels) from the bottom of the scroll container.
11606
+ *
11607
+ * @returns {number}
11608
+ */
11609
+ getLastKnownScrollBottomOffset() {
11610
+ return this.#lastKnownScrollBottomOffset;
11611
+ }
11612
+
11613
+ /**
11614
+ * Forces the scroll position to move to the very bottom of the target.
11615
+ */
11616
+ scrollToBottom() {
11617
+ this.#target.scrollTop = this.#target.scrollHeight;
11618
+ }
11619
+
11620
+ /**
11621
+ * Forces the scroll position to move to the very top of the target.
11622
+ */
11623
+ scrollToTop() {
11624
+ this.#target.scrollTop = 0;
11625
+ }
11626
+
11627
+ /**
11628
+ * Checks if the user is within the defined extra scroll boundary from the bottom.
11629
+ *
11630
+ * @returns {boolean}
11631
+ */
11632
+ isUserAtCustomBottom() {
11633
+ return this.#isAtCustomBottom;
11634
+ }
11635
+
11636
+ /**
11637
+ * Checks if the user is within the defined extra scroll boundary from the top.
11638
+ *
11639
+ * @returns {boolean}
11640
+ */
11641
+ isUserAtCustomTop() {
11642
+ return this.#isAtCustomTop;
11643
+ }
11644
+
11645
+ /**
11646
+ * Returns true if the user is currently scrolled to the bottom of the element.
11647
+ *
11648
+ * @returns {boolean}
11649
+ */
11650
+ isUserAtBottom() {
11651
+ return this.#isAtBottom;
11652
+ }
11653
+
11654
+ /**
11655
+ * Returns true if the user is currently scrolled to the top of the element.
11656
+ *
11657
+ * @returns {boolean}
11658
+ */
11659
+ isUserAtTop() {
11660
+ return this.#isAtTop;
11661
+ }
11662
+
11663
+ /**
11664
+ * Returns true if automatic scrolling is currently paused.
11665
+ *
11666
+ * @returns {boolean}
11667
+ */
11668
+ isScrollPaused() {
11669
+ return this.#scrollPaused;
11670
+ }
11671
+
11672
+ /**
11673
+ * Returns whether the target is the window object.
11674
+ *
11675
+ * @returns {boolean} True if the scroll target is window, false otherwise.
11676
+ */
11677
+ isWindow() {
11678
+ return this.#useWindow;
11679
+ }
11680
+
11681
+ /**
11682
+ * Returns whether the instance has been destroyed.
11683
+ *
11684
+ * @returns {boolean} True if the instance is destroyed, false otherwise.
11685
+ */
11686
+ isDestroyed() {
11687
+ return this.#destroyed;
11688
+ }
11689
+
11690
+ /**
11691
+ * Returns whether auto-scroll-to-bottom is enabled.
11692
+ *
11693
+ * @returns {boolean} True if auto-scroll is active, false otherwise.
11694
+ */
11695
+ getAutoScrollBottom() {
11696
+ return this.#autoScrollBottom;
11697
+ }
11698
+
11699
+ /**
11700
+ * Returns whether MutationObserver is enabled.
11701
+ *
11702
+ * @returns {boolean} True if mutation observation is active, false otherwise.
11703
+ */
11704
+ getObserveMutations() {
11705
+ return this.#observeMutations;
11706
+ }
11707
+
11708
+ /**
11709
+ * Returns whether layout shift protection is enabled.
11710
+ *
11711
+ * @returns {boolean} True if scroll preservation is active, false otherwise.
11712
+ */
11713
+ getPreserveScrollOnLayoutShift() {
11714
+ return this.#preserveScrollOnLayoutShift;
11715
+ }
11716
+
11717
+ /**
11718
+ * Returns the debounce delay in milliseconds used for scroll events.
11719
+ *
11720
+ * @returns {number} Debounce delay time.
11721
+ */
11722
+ getDebounceTime() {
11723
+ return this.#debounceTime;
11724
+ }
11725
+
11726
+ /**
11727
+ * Returns the current number of matching elements observed inside the scroll target.
11728
+ *
11729
+ * @returns {number} Current count of matching elements.
11730
+ */
11731
+ getElemAmount() {
11732
+ return this.#elemAmount;
11733
+ }
11734
+
11735
+ /**
11736
+ * Returns the previous known count of matching elements from the last update.
11737
+ *
11738
+ * @returns {number} Previous count of matching elements.
11739
+ */
11740
+ getPrevElemAmount() {
11741
+ return this.#elemOldAmount;
11742
+ }
11743
+
11744
+ /**
11745
+ * Returns the query selector string used to filter observed elements.
11746
+ *
11747
+ * @returns {string} The CSS selector string, or an empty string if none was provided.
11748
+ */
11749
+ getQuerySelector() {
11750
+ return this.#querySelector;
11751
+ }
11752
+
11753
+ /**
11754
+ * Disconnects all listeners, observers, and clears memory structures.
11755
+ * Once destroyed, this instance should no longer be used.
11756
+ */
11757
+ destroy() {
11758
+ if (this.#destroyed) return;
11759
+ this.#destroyed = true;
11760
+
11761
+ // Disconnects MutationObserver
11762
+ if (this.#mutationObserver) {
11763
+ this.#mutationObserver.disconnect();
11764
+ this.#mutationObserver = null;
11765
+ }
11766
+
11767
+ // Disconnect ResizeObserver
11768
+ if (this.#resizeObserver) {
11769
+ this.#resizeObserver.disconnect();
11770
+ this.#resizeObserver = null;
11771
+ }
11772
+
11773
+ // Removes scroll listener
11774
+ const target = this.#useWindow ? window : this.#target;
11775
+ if (this.#handler) target.removeEventListener('scroll', this.#handler);
11776
+
11777
+ // Clean the WeakMaps
11778
+ this.#oldSizes = new WeakMap();
11779
+ this.#newSizes = new WeakMap();
11780
+ this.#newVisibles = new WeakMap();
11781
+ this.#oldVisibles = new WeakMap();
11782
+ this.#newVisiblesByTime = new WeakMap();
11783
+ this.#oldVisiblesByTime = new WeakMap();
11784
+
11785
+ // Cleans listeners and filters
11786
+ this.#scrollListeners = {};
11787
+ this.#sizeFilter.clear();
11788
+ this.#loadTags.clear();
11789
+ }
11790
+ }
11791
+
11792
+ /* harmony default export */ const libs_TinySmartScroller = ((/* unused pure expression or super */ null && (TinySmartScroller)));
11793
+
11794
+ ;// ./src/v1/index.mjs
11795
+
11796
+
10331
11797
 
10332
11798
 
10333
11799