tiny-essentials 1.17.1 → 1.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/legacy/get/countObj.cjs +2 -2
  2. package/dist/legacy/get/countObj.d.mts +1 -1
  3. package/dist/legacy/get/countObj.mjs +1 -1
  4. package/dist/legacy/index.cjs +2 -1
  5. package/dist/v1/TinyBasicsEs.js +559 -411
  6. package/dist/v1/TinyBasicsEs.min.js +1 -1
  7. package/dist/v1/TinyClipboard.js +459 -0
  8. package/dist/v1/TinyClipboard.min.js +1 -0
  9. package/dist/v1/TinyDragger.js +170 -2454
  10. package/dist/v1/TinyDragger.min.js +1 -2
  11. package/dist/v1/TinyEssentials.js +1093 -63
  12. package/dist/v1/TinyEssentials.min.js +1 -1
  13. package/dist/v1/TinyHtml.js +164 -21
  14. package/dist/v1/TinyHtml.min.js +1 -1
  15. package/dist/v1/TinySmartScroller.js +164 -21
  16. package/dist/v1/TinySmartScroller.min.js +1 -1
  17. package/dist/v1/TinyTextRangeEditor.js +497 -0
  18. package/dist/v1/TinyTextRangeEditor.min.js +1 -0
  19. package/dist/v1/TinyUploadClicker.js +219 -467
  20. package/dist/v1/TinyUploadClicker.min.js +1 -1
  21. package/dist/v1/basics/html.cjs +3 -3
  22. package/dist/v1/basics/html.mjs +1 -1
  23. package/dist/v1/basics/index.cjs +3 -2
  24. package/dist/v1/basics/index.d.mts +2 -2
  25. package/dist/v1/basics/index.mjs +2 -1
  26. package/dist/v1/basics/objChecker.cjs +46 -0
  27. package/dist/v1/basics/objChecker.d.mts +29 -0
  28. package/dist/v1/basics/objChecker.mjs +45 -0
  29. package/dist/v1/basics/objFilter.cjs +4 -45
  30. package/dist/v1/basics/objFilter.d.mts +3 -28
  31. package/dist/v1/basics/objFilter.mjs +2 -45
  32. package/dist/v1/build/TinyClipboard.cjs +7 -0
  33. package/dist/v1/build/TinyClipboard.d.mts +3 -0
  34. package/dist/v1/build/TinyClipboard.mjs +2 -0
  35. package/dist/v1/build/TinyTextRangeEditor.cjs +7 -0
  36. package/dist/v1/build/TinyTextRangeEditor.d.mts +3 -0
  37. package/dist/v1/build/TinyTextRangeEditor.mjs +2 -0
  38. package/dist/v1/index.cjs +7 -2
  39. package/dist/v1/index.d.mts +5 -3
  40. package/dist/v1/index.mjs +5 -2
  41. package/dist/v1/libs/TinyClipboard.cjs +420 -0
  42. package/dist/v1/libs/TinyClipboard.d.mts +155 -0
  43. package/dist/v1/libs/TinyClipboard.mjs +398 -0
  44. package/dist/v1/libs/TinyDragger.cjs +3 -3
  45. package/dist/v1/libs/TinyDragger.mjs +1 -1
  46. package/dist/v1/libs/TinyHtml.cjs +164 -21
  47. package/dist/v1/libs/TinyHtml.d.mts +150 -27
  48. package/dist/v1/libs/TinyHtml.mjs +158 -20
  49. package/dist/v1/libs/TinyTextRangeEditor.cjs +458 -0
  50. package/dist/v1/libs/TinyTextRangeEditor.d.mts +200 -0
  51. package/dist/v1/libs/TinyTextRangeEditor.mjs +424 -0
  52. package/dist/v1/libs/TinyUploadClicker.cjs +5 -4
  53. package/docs/v1/README.md +3 -0
  54. package/docs/v1/basics/objChecker.md +47 -0
  55. package/docs/v1/basics/objFilter.md +0 -40
  56. package/docs/v1/libs/TinyClipboard.md +213 -0
  57. package/docs/v1/libs/TinyHtml.md +112 -15
  58. package/docs/v1/libs/TinyTextRangeEditor.md +208 -0
  59. package/package.json +1 -1
  60. package/dist/v1/TinyDragger.min.js.LICENSE.txt +0 -8
@@ -2912,9 +2912,55 @@ __webpack_require__.d(collision_namespaceObject, {
2912
2912
 
2913
2913
  // EXTERNAL MODULE: ./node_modules/buffer/index.js
2914
2914
  var buffer = __webpack_require__(287);
2915
+ ;// ./src/v1/basics/objChecker.mjs
2916
+ /**
2917
+ * Counts the number of elements in an array or the number of properties in an object.
2918
+ *
2919
+ * @param {Array<*>|Record<string | number | symbol, any>} obj - The array or object to count.
2920
+ * @returns {number} - The count of items (array elements or object keys), or `0` if the input is neither an array nor an object.
2921
+ *
2922
+ * @example
2923
+ * countObj([1, 2, 3]); // 3
2924
+ * countObj({ a: 1, b: 2 }); // 2
2925
+ * countObj('not an object'); // 0
2926
+ */
2927
+ function countObj(obj) {
2928
+ // Is Array
2929
+ if (Array.isArray(obj)) return obj.length;
2930
+ // Object
2931
+ if (isJsonObject(obj)) return Object.keys(obj).length;
2932
+ // Nothing
2933
+ return 0;
2934
+ }
2935
+
2936
+ /**
2937
+ * Determines whether a given value is a pure JSON object (plain object).
2938
+ *
2939
+ * A pure object satisfies the following:
2940
+ * - It is not null.
2941
+ * - Its type is "object".
2942
+ * - Its internal [[Class]] is "[object Object]".
2943
+ * - It is not an instance of built-in types like Array, Date, Map, Set, etc.
2944
+ *
2945
+ * This function is useful for strict data validation when you want to ensure
2946
+ * a value is a clean JSON-compatible object, free of class instances or special types.
2947
+ *
2948
+ * @param {unknown} value - The value to test.
2949
+ * @returns {value is Record<string | number | symbol, unknown>} Returns true if the value is a pure object.
2950
+ */
2951
+ function isJsonObject(value) {
2952
+ if (value === null || typeof value !== 'object') return false;
2953
+ if (Array.isArray(value)) return false;
2954
+ if (Object.prototype.toString.call(value) !== '[object Object]') return false;
2955
+ return true;
2956
+ }
2957
+
2915
2958
  ;// ./src/v1/basics/objFilter.mjs
2916
2959
 
2917
2960
 
2961
+
2962
+
2963
+
2918
2964
  const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
2919
2965
 
2920
2966
  /**
@@ -3107,48 +3153,6 @@ function getCheckObj() {
3107
3153
  return Object.fromEntries(Object.entries(typeValidator.items).map(([key, fn]) => [key, fn]));
3108
3154
  }
3109
3155
 
3110
- /**
3111
- * Counts the number of elements in an array or the number of properties in an object.
3112
- *
3113
- * @param {Array<*>|Record<string|number, any>} obj - The array or object to count.
3114
- * @returns {number} - The count of items (array elements or object keys), or `0` if the input is neither an array nor an object.
3115
- *
3116
- * @example
3117
- * countObj([1, 2, 3]); // 3
3118
- * countObj({ a: 1, b: 2 }); // 2
3119
- * countObj('not an object'); // 0
3120
- */
3121
- function countObj(obj) {
3122
- // Is Array
3123
- if (Array.isArray(obj)) return obj.length;
3124
- // Object
3125
- if (objType(obj, 'object')) return Object.keys(obj).length;
3126
- // Nothing
3127
- return 0;
3128
- }
3129
-
3130
- /**
3131
- * Determines whether a given value is a pure JSON object (plain object).
3132
- *
3133
- * A pure object satisfies the following:
3134
- * - It is not null.
3135
- * - Its type is "object".
3136
- * - Its internal [[Class]] is "[object Object]".
3137
- * - It is not an instance of built-in types like Array, Date, Map, Set, etc.
3138
- *
3139
- * This function is useful for strict data validation when you want to ensure
3140
- * a value is a clean JSON-compatible object, free of class instances or special types.
3141
- *
3142
- * @param {unknown} value - The value to test.
3143
- * @returns {value is Record<string | number | symbol, unknown>} Returns true if the value is a pure object.
3144
- */
3145
- function objFilter_isJsonObject(value) {
3146
- if (value === null || typeof value !== 'object') return false;
3147
- if (Array.isArray(value)) return false;
3148
- if (Object.prototype.toString.call(value) !== '[object Object]') return false;
3149
- return true;
3150
- }
3151
-
3152
3156
  // Insert obj types
3153
3157
 
3154
3158
  extendObjType([
@@ -3271,7 +3275,7 @@ extendObjType([
3271
3275
  [
3272
3276
  'object',
3273
3277
  /** @param {*} val @returns {val is Record<string | number | symbol, unknown>} */
3274
- (val) => objFilter_isJsonObject(val),
3278
+ (val) => isJsonObject(val),
3275
3279
  ],
3276
3280
  ]);
3277
3281
 
@@ -3471,404 +3475,6 @@ class ColorSafeStringify {
3471
3475
 
3472
3476
  /* harmony default export */ const libs_ColorSafeStringify = ((/* unused pure expression or super */ null && (ColorSafeStringify)));
3473
3477
 
3474
- ;// ./src/v1/basics/html.mjs
3475
-
3476
-
3477
- /////////////////////////////////////////////////////////////////
3478
-
3479
- /**
3480
- * Reads the contents of a file using the specified FileReader method.
3481
- *
3482
- * @param {File} file - The file to be read.
3483
- * @param {'readAsArrayBuffer'|'readAsDataURL'|'readAsText'|'readAsBinaryString'} method -
3484
- * The FileReader method to use for reading the file.
3485
- * @returns {Promise<any>} - A promise that resolves with the file content, according to the chosen method.
3486
- * @throws {Error} - If an unexpected error occurs while handling the result.
3487
- * @throws {DOMException} - If the FileReader encounters an error while reading the file.
3488
- */
3489
- function readFileBlob(file, method) {
3490
- return new Promise((resolve, reject) => {
3491
- const reader = new FileReader();
3492
- reader.onload = () => {
3493
- try {
3494
- resolve(reader.result);
3495
- } catch (error) {
3496
- reject(error);
3497
- }
3498
- };
3499
- reader.onerror = () => {
3500
- reject(reader.error);
3501
- };
3502
- reader[method](file);
3503
- });
3504
- }
3505
-
3506
- /**
3507
- * Reads a file as a Base64 string using FileReader, and optionally formats it as a full data URL.
3508
- *
3509
- * Performs strict validation to ensure the result is a valid Base64 string or a proper data URL.
3510
- *
3511
- * @param {File} file - The file to be read.
3512
- * @param {boolean|string} [isDataUrl=false] - If true, returns a full data URL; if false, returns only the Base64 string;
3513
- * if a string is passed, it is used as the MIME type in the data URL.
3514
- * @returns {Promise<string>} - A promise that resolves with the Base64 string or data URL.
3515
- *
3516
- * @throws {TypeError} - If the result is not a string or if `isDataUrl` is not a valid type.
3517
- * @throws {Error} - If the result does not match the expected data URL format or Base64 structure.
3518
- * @throws {DOMException} - If the FileReader fails to read the file.
3519
- */
3520
- function readBase64Blob(file, isDataUrl = false) {
3521
- return new Promise((resolve, reject) => {
3522
- if (typeof isDataUrl !== 'string' && typeof isDataUrl !== 'boolean')
3523
- reject(new TypeError('The isDataUrl parameter must be a boolean or a string.'));
3524
- readFileBlob(file, 'readAsDataURL')
3525
- .then(
3526
- /**
3527
- * Ensure that the URL format is correct in the required pattern
3528
- * @param {string} base64Data
3529
- */ (base64Data) => {
3530
- if (typeof base64Data !== 'string')
3531
- throw new TypeError('Expected file content to be a string.');
3532
-
3533
- const match = base64Data.match(/^data:(.+);base64,(.*)$/);
3534
- if (!match || !match[2])
3535
- throw new Error('Invalid data URL format or missing Base64 content.');
3536
- const [, mimeType, base64] = match;
3537
- if (!/^[\w/+]+=*$/.test(base64)) throw new Error('Base64 content is malformed.');
3538
-
3539
- if (typeof isDataUrl === 'boolean') return resolve(isDataUrl ? base64Data : base64);
3540
- if (!/^[\w-]+\/[\w.+-]+$/.test(isDataUrl))
3541
- throw new Error(`Invalid MIME type string: ${isDataUrl}`);
3542
-
3543
- return resolve(`data:${isDataUrl};base64,${base64}`);
3544
- },
3545
- )
3546
- .catch(reject);
3547
- });
3548
- }
3549
-
3550
- /**
3551
- * Reads a file and strictly validates its content as proper JSON using FileReader.
3552
- *
3553
- * Performs several checks to ensure the file contains valid, parsable JSON data.
3554
- *
3555
- * @param {File} file - The file to be read. It must contain valid JSON as plain text.
3556
- * @returns {Promise<Record<string|number|symbol, any>|any[]>} - A promise that resolves with the parsed JSON object.
3557
- *
3558
- * @throws {SyntaxError} - If the file content is not valid JSON syntax.
3559
- * @throws {TypeError} - If the result is not a string or does not represent a JSON value.
3560
- * @throws {Error} - If the result is empty or structurally invalid as JSON.
3561
- * @throws {DOMException} - If the FileReader fails to read the file.
3562
- */
3563
- function readJsonBlob(file) {
3564
- return new Promise((resolve, reject) =>
3565
- readFileBlob(file, 'readAsText')
3566
- .then((data) => {
3567
- if (typeof data !== 'string') throw new TypeError('Expected file content to be a string.');
3568
- const trimmed = data.trim();
3569
- if (trimmed.length === 0) throw new Error('File is empty or contains only whitespace.');
3570
- const parsed = JSON.parse(trimmed);
3571
- if (typeof parsed !== 'object' || parsed === null)
3572
- throw new Error('Parsed content is not a valid JSON object or array.');
3573
- resolve(parsed);
3574
- })
3575
- .catch(reject),
3576
- );
3577
- }
3578
-
3579
- /**
3580
- * Saves a JSON object as a downloadable file.
3581
- * @param {string} filename
3582
- * @param {any} data
3583
- * @param {number} [spaces=2]
3584
- */
3585
- function saveJsonFile(filename, data, spaces = 2) {
3586
- const json = JSON.stringify(data, null, spaces);
3587
- const blob = new Blob([json], { type: 'application/json' });
3588
- const url = URL.createObjectURL(blob);
3589
-
3590
- const link = document.createElement('a');
3591
- link.href = url;
3592
- link.download = filename;
3593
-
3594
- document.body.appendChild(link);
3595
- link.click();
3596
- document.body.removeChild(link);
3597
-
3598
- URL.revokeObjectURL(url);
3599
- }
3600
-
3601
- /**
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.
3616
- */
3617
- async function fetchTemplate(
3618
- url,
3619
- { method = 'GET', body, timeout = 0, retries = 0, headers = {}, signal = null } = {},
3620
- ) {
3621
- if (
3622
- typeof url !== 'string' ||
3623
- (!url.startsWith('../') &&
3624
- !url.startsWith('./') &&
3625
- !url.startsWith('/') &&
3626
- !url.startsWith('https://') &&
3627
- !url.startsWith('http://'))
3628
- )
3629
- throw new Error('Invalid URL: must be a valid http or https address.');
3630
-
3631
- if (typeof method !== 'string' || !method.trim())
3632
- throw new Error('Invalid method: must be a non-empty string.');
3633
-
3634
- if (!signal) {
3635
- if (
3636
- typeof timeout !== 'number' ||
3637
- !Number.isFinite(timeout) ||
3638
- Number.isNaN(timeout) ||
3639
- timeout < 0
3640
- )
3641
- throw new Error('Invalid timeout: must be a positive number.');
3642
-
3643
- if (
3644
- typeof retries !== 'number' ||
3645
- !Number.isFinite(retries) ||
3646
- Number.isNaN(retries) ||
3647
- retries < 0
3648
- )
3649
- throw new Error('Invalid retries: must be a positive number.');
3650
- }
3651
-
3652
- const attempts = signal ? 1 : retries + 1;
3653
- /** @type {Error|null} */
3654
- let lastError = null;
3655
-
3656
- for (let attempt = 0; attempt < attempts; attempt++) {
3657
- const controller = signal ? null : new AbortController();
3658
- const localSignal = signal || (controller?.signal ?? null);
3659
- const timer =
3660
- !signal && timeout && controller ? setTimeout(() => controller.abort(), timeout) : null;
3661
-
3662
- try {
3663
- const response = await fetch(url, {
3664
- method: method.toUpperCase(),
3665
- headers: {
3666
- Accept: 'application/json',
3667
- ...headers,
3668
- },
3669
- body: body !== undefined ? (isJsonObject(body) ? JSON.stringify(body) : body) : undefined,
3670
- signal: localSignal,
3671
- });
3672
-
3673
- if (timer) clearTimeout(timer);
3674
-
3675
- if (!response.ok) throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
3676
- return response;
3677
- } catch (err) {
3678
- lastError = /** @type {Error} */ (err);
3679
- if (signal) break; // if an external signal came, it does not retry
3680
- if (attempt < retries)
3681
- await new Promise((resolve) => setTimeout(resolve, 300 * (attempt + 1)));
3682
- }
3683
- }
3684
-
3685
- throw new Error(
3686
- `Failed to fetch JSON from "${url}"${lastError ? `: ${lastError.message}` : '.'}`,
3687
- );
3688
- }
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
-
3747
- ///////////////////////////////////////////////////////////////////////////////
3748
-
3749
- /**
3750
- * Installs a script that toggles CSS classes on a given element
3751
- * based on the page's visibility or focus state, and optionally
3752
- * triggers callbacks on visibility changes.
3753
- *
3754
- * @param {Object} [settings={}]
3755
- * @param {Element} [settings.element=document.body] - The element to receive visibility classes.
3756
- * @param {string} [settings.hiddenClass='windowHidden'] - CSS class applied when the page is hidden.
3757
- * @param {string} [settings.visibleClass='windowVisible'] - CSS class applied when the page is visible.
3758
- * @param {() => void} [settings.onVisible] - Callback called when page becomes visible.
3759
- * @param {() => void} [settings.onHidden] - Callback called when page becomes hidden.
3760
- * @returns {() => void} Function that removes all installed event listeners.
3761
- * @throws {TypeError} If any provided setting is invalid.
3762
- */
3763
- function installWindowHiddenScript({
3764
- element = document.body,
3765
- hiddenClass = 'windowHidden',
3766
- visibleClass = 'windowVisible',
3767
- onVisible,
3768
- onHidden,
3769
- } = {}) {
3770
- if (!(element instanceof Element))
3771
- throw new TypeError(`"element" must be an instance of Element.`);
3772
- if (typeof hiddenClass !== 'string') throw new TypeError(`"hiddenClass" must be a string.`);
3773
- if (typeof visibleClass !== 'string') throw new TypeError(`"visibleClass" must be a string.`);
3774
- if (onVisible !== undefined && typeof onVisible !== 'function')
3775
- throw new TypeError(`"onVisible" must be a function if provided.`);
3776
- if (onHidden !== undefined && typeof onHidden !== 'function')
3777
- throw new TypeError(`"onHidden" must be a function if provided.`);
3778
-
3779
- const removeClass = () => {
3780
- element.classList.remove(hiddenClass);
3781
- element.classList.remove(visibleClass);
3782
- };
3783
-
3784
- /** @type {string|null} */
3785
- let hiddenProp = null;
3786
-
3787
- const visibilityEvents = [
3788
- 'visibilitychange',
3789
- 'mozvisibilitychange',
3790
- 'webkitvisibilitychange',
3791
- 'msvisibilitychange',
3792
- ];
3793
-
3794
- const visibilityProps = ['hidden', 'mozHidden', 'webkitHidden', 'msHidden'];
3795
-
3796
- for (let i = 0; i < visibilityProps.length; i++) {
3797
- if (visibilityProps[i] in document) {
3798
- hiddenProp = visibilityProps[i];
3799
- break;
3800
- }
3801
- }
3802
-
3803
- /** @type {(this: any, evt: Event) => void} */
3804
- const handler = function (evt) {
3805
- removeClass();
3806
-
3807
- const type = evt?.type;
3808
- // @ts-ignore
3809
- const isHidden = hiddenProp && document[hiddenProp];
3810
-
3811
- const visibleEvents = ['focus', 'focusin', 'pageshow'];
3812
- const hiddenEvents = ['blur', 'focusout', 'pagehide'];
3813
-
3814
- if (visibleEvents.includes(type)) {
3815
- element.classList.add(visibleClass);
3816
- onVisible?.();
3817
- } else if (hiddenEvents.includes(type)) {
3818
- element.classList.add(hiddenClass);
3819
- onHidden?.();
3820
- } else {
3821
- if (isHidden) {
3822
- element.classList.add(hiddenClass);
3823
- onHidden?.();
3824
- } else {
3825
- element.classList.add(visibleClass);
3826
- onVisible?.();
3827
- }
3828
- }
3829
- };
3830
-
3831
- /** @type {() => void} */
3832
- let uninstall = () => {};
3833
-
3834
- if (hiddenProp) {
3835
- const eventType = visibilityEvents[visibilityProps.indexOf(hiddenProp)];
3836
- document.addEventListener(eventType, handler);
3837
- window.addEventListener('focus', handler);
3838
- window.addEventListener('blur', handler);
3839
-
3840
- uninstall = () => {
3841
- document.removeEventListener(eventType, handler);
3842
- window.removeEventListener('focus', handler);
3843
- window.removeEventListener('blur', handler);
3844
- removeClass();
3845
- };
3846
- } else if ('onfocusin' in document) {
3847
- // Fallback for IE9 and older
3848
- // @ts-ignore
3849
- document.onfocusin = document.onfocusout = handler;
3850
- uninstall = () => {
3851
- // @ts-ignore
3852
- document.onfocusin = document.onfocusout = null;
3853
- removeClass();
3854
- };
3855
- } else {
3856
- // Last resort fallback
3857
- window.onpageshow = window.onpagehide = window.onfocus = window.onblur = handler;
3858
- uninstall = () => {
3859
- window.onpageshow = window.onpagehide = window.onfocus = window.onblur = null;
3860
- removeClass();
3861
- };
3862
- }
3863
-
3864
- // Trigger initial state
3865
- // @ts-ignore
3866
- const simulatedEvent = new Event(hiddenProp && document[hiddenProp] ? 'blur' : 'focus');
3867
- handler(simulatedEvent);
3868
-
3869
- return uninstall;
3870
- }
3871
-
3872
3478
  ;// ./src/v1/basics/collision.mjs
3873
3479
  /**
3874
3480
  * A direction relative to a rectangle.
@@ -4358,6 +3964,21 @@ const {
4358
3964
  * @typedef {Element|Window|Document} ElementAndWinAndDoc
4359
3965
  */
4360
3966
 
3967
+ /**
3968
+ * Represents a raw DOM element with document or an instance of TinyHtml.
3969
+ * This type is used to abstract interactions with both plain elements
3970
+ * and wrapped elements via the TinyHtml class.
3971
+ *
3972
+ * @typedef {ElementWithDoc|TinyHtml} TinyElementWithDoc
3973
+ */
3974
+
3975
+ /**
3976
+ * Represents a value that can be either a DOM Element, or the document object.
3977
+ * Useful for functions that operate generically on measurable targets.
3978
+ *
3979
+ * @typedef {Element|Document} ElementWithDoc
3980
+ */
3981
+
4361
3982
  /**
4362
3983
  * A parameter type used for filtering or matching elements.
4363
3984
  * It can be:
@@ -4377,12 +3998,6 @@ const {
4377
3998
  * @typedef {Window|Element|Document|Text} ConstructorElValues
4378
3999
  */
4379
4000
 
4380
- /**
4381
- * The handler function used in event listeners.
4382
- *
4383
- * @typedef {(e: Event) => any} EventRegistryHandle
4384
- */
4385
-
4386
4001
  /**
4387
4002
  * Options passed to `addEventListener` or `removeEventListener`.
4388
4003
  * Can be a boolean or an object of type `AddEventListenerOptions`.
@@ -4394,7 +4009,7 @@ const {
4394
4009
  * Structure describing a registered event callback and its options.
4395
4010
  *
4396
4011
  * @typedef {Object} EventRegistryItem
4397
- * @property {EventRegistryHandle} handler - The function to be executed when the event is triggered.
4012
+ * @property {EventListenerOrEventListenerObject|null} handler - The function to be executed when the event is triggered.
4398
4013
  * @property {EventRegistryOptions} [options] - Optional configuration passed to the listener.
4399
4014
  */
4400
4015
 
@@ -4953,9 +4568,9 @@ class TinyHtml_TinyHtml {
4953
4568
  * Ensures the input is returned as an array.
4954
4569
  * Useful to normalize operations across multiple or single element/window/document elements.
4955
4570
  *
4956
- * @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/window element or array of html elements.
4571
+ * @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/document/window element or array of html elements.
4957
4572
  * @param {string} where - The method or context name where validation is being called.
4958
- * @returns {ElementAndWindow[]} - Always returns an array of element/window elements.
4573
+ * @returns {ElementAndWindow[]} - Always returns an array of element/document/window elements.
4959
4574
  * @readonly
4960
4575
  */
4961
4576
  static _preElemsAndWinAndDoc(elems, where) {
@@ -4972,9 +4587,9 @@ class TinyHtml_TinyHtml {
4972
4587
  * Ensures the input is returned as an single element/window/document element.
4973
4588
  * Useful to normalize operations across multiple or single element/window/document elements.
4974
4589
  *
4975
- * @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/window element or array of html elements.
4590
+ * @param {TinyElementAndWinAndDoc|TinyElementAndWinAndDoc[]} elems - A single element/document/window element or array of html elements.
4976
4591
  * @param {string} where - The method or context name where validation is being called.
4977
- * @returns {ElementAndWindow} - Always returns an single element/window element.
4592
+ * @returns {ElementAndWindow} - Always returns an single element/document/window element.
4978
4593
  * @readonly
4979
4594
  */
4980
4595
  static _preElemAndWinAndDoc(elems, where) {
@@ -4988,6 +4603,32 @@ class TinyHtml_TinyHtml {
4988
4603
  return result;
4989
4604
  }
4990
4605
 
4606
+ /**
4607
+ * Ensures the input is returned as an array.
4608
+ * Useful to normalize operations across multiple or single element with document elements.
4609
+ *
4610
+ * @param {TinyElementWithDoc|TinyElementWithDoc[]} elems - A single element with document element or array of html elements.
4611
+ * @param {string} where - The method or context name where validation is being called.
4612
+ * @returns {ElementWithDoc[]} - Always returns an array of element with document elements.
4613
+ * @readonly
4614
+ */
4615
+ static _preElemsWithDoc(elems, where) {
4616
+ return TinyHtml_TinyHtml._preElemsTemplate(elems, where, [Element, Document], ['Element', 'Document']);
4617
+ }
4618
+
4619
+ /**
4620
+ * Ensures the input is returned as an single element with document element.
4621
+ * Useful to normalize operations across multiple or single element with document elements.
4622
+ *
4623
+ * @param {TinyElementWithDoc|TinyElementWithDoc[]} elems - A single element/window element or array of html elements.
4624
+ * @param {string} where - The method or context name where validation is being called.
4625
+ * @returns {ElementWithDoc} - Always returns an single element/window element.
4626
+ * @readonly
4627
+ */
4628
+ static _preElemWithDoc(elems, where) {
4629
+ return TinyHtml_TinyHtml._preElemTemplate(elems, where, [Element, Document], ['Element', 'Document']);
4630
+ }
4631
+
4991
4632
  /**
4992
4633
  * Normalizes and converts one or more DOM elements (or TinyHtml instances)
4993
4634
  * into an array of `TinyHtml` instances.
@@ -8247,12 +7888,120 @@ class TinyHtml_TinyHtml {
8247
7888
 
8248
7889
  ////////////////////////////////////////////
8249
7890
 
7891
+ /**
7892
+ * Registers a listener for the "paste" event to extract files and text from the clipboard (e.g., when the user presses Ctrl+V).
7893
+ *
7894
+ * This method allows reacting to pasted content by providing separate callbacks for files and plain text.
7895
+ * Useful for building file upload areas, rich-text editors, or input enhancements.
7896
+ *
7897
+ * @param {TinyElementWithDoc|TinyElementWithDoc[]} el - The target element(s) where the "paste" event will be listened.
7898
+ * @param {Object} [settings={}] - Optional callbacks to handle clipboard content.
7899
+ * @param {(data: DataTransferItem, file: File) => void} [settings.onFilePaste] - Called for each file pasted from the clipboard (e.g., images).
7900
+ * @param {(data: DataTransferItem, text: string) => void} [settings.onTextPaste] - Called when plain text is pasted from the clipboard.
7901
+ * @returns {EventListenerOrEventListenerObject} The internal "paste" event handler used.
7902
+ */
7903
+ static listenForPaste(el, { onFilePaste, onTextPaste } = {}) {
7904
+ if (typeof onFilePaste !== 'undefined' && typeof onFilePaste !== 'function')
7905
+ throw new TypeError('onFilePaste must be a function.');
7906
+ if (typeof onTextPaste !== 'undefined' && typeof onTextPaste !== 'function')
7907
+ throw new TypeError('onTextPaste must be a function.');
7908
+
7909
+ /** @type {EventListenerOrEventListenerObject} */
7910
+ const pasteEvent = (event) => {
7911
+ if (!(event instanceof ClipboardEvent)) return;
7912
+ const items = event.clipboardData?.items || [];
7913
+ for (const item of items) {
7914
+ if (item.kind === 'file') {
7915
+ if (typeof onFilePaste === 'function') {
7916
+ const file = item.getAsFile();
7917
+ if (file) onFilePaste(item, file);
7918
+ }
7919
+ } else if (item.kind === 'string') {
7920
+ if (typeof onTextPaste === 'function')
7921
+ item.getAsString((text) => onTextPaste(item, text));
7922
+ }
7923
+ }
7924
+ };
7925
+
7926
+ TinyHtml_TinyHtml._preElemsWithDoc(el, 'listenForPaste').forEach((elem) =>
7927
+ TinyHtml_TinyHtml.on(elem, 'paste', pasteEvent),
7928
+ );
7929
+ return pasteEvent;
7930
+ }
7931
+
7932
+ /**
7933
+ * Registers a listener for the "paste" event to extract files and text from the clipboard (e.g., when the user presses Ctrl+V).
7934
+ *
7935
+ * This method allows reacting to pasted content by providing separate callbacks for files and plain text.
7936
+ * Useful for building file upload areas, rich-text editors, or input enhancements.
7937
+ *
7938
+ * @param {Object} [settings={}] - Optional callbacks to handle clipboard content.
7939
+ * @param {(data: DataTransferItem, file: File) => void} [settings.onFilePaste] - Called for each file pasted from the clipboard (e.g., images).
7940
+ * @param {(data: DataTransferItem, text: string) => void} [settings.onTextPaste] - Called when plain text is pasted from the clipboard.
7941
+ * @returns {EventListenerOrEventListenerObject} The internal "paste" event handler used.
7942
+ */
7943
+ listenForPaste({ onFilePaste, onTextPaste } = {}) {
7944
+ return TinyHtml_TinyHtml.listenForPaste(this, { onFilePaste, onTextPaste });
7945
+ }
7946
+
7947
+ /**
7948
+ * Checks if the element has a listener for a specific event.
7949
+ *
7950
+ * @param {TinyEventTarget} el - The element to check.
7951
+ * @param {string} event - The event name to check.
7952
+ * @returns {boolean}
7953
+ */
7954
+ static hasEventListener(el, event) {
7955
+ const elem = TinyHtml_TinyHtml._preEventTargetElem(el, 'hasEventListener');
7956
+ if (!__eventRegistry.has(elem)) return false;
7957
+ const events = __eventRegistry.get(elem);
7958
+ return !!(events && Array.isArray(events[event]) && events[event].length > 0);
7959
+ }
7960
+
7961
+ /**
7962
+ * Checks if the element has a listener for a specific event.
7963
+ *
7964
+ * @param {string} event - The event name to check.
7965
+ * @returns {boolean}
7966
+ */
7967
+ hasEventListener(event) {
7968
+ return TinyHtml_TinyHtml.hasEventListener(this, event);
7969
+ }
7970
+
7971
+ /**
7972
+ * Checks if the element has the exact handler registered for a specific event.
7973
+ *
7974
+ * @param {TinyEventTarget} el - The element to check.
7975
+ * @param {string} event - The event name to check.
7976
+ * @param {EventListenerOrEventListenerObject} handler - The handler function to check.
7977
+ * @returns {boolean}
7978
+ */
7979
+ static hasExactEventListener(el, event, handler) {
7980
+ const elem = TinyHtml_TinyHtml._preEventTargetElem(el, 'hasExactEventListener');
7981
+ if (typeof handler !== 'function') throw new TypeError('The "handler" must be a function.');
7982
+ if (!__eventRegistry.has(elem)) return false;
7983
+ const events = __eventRegistry.get(elem);
7984
+ if (!events || !Array.isArray(events[event])) return false;
7985
+ return events[event].some((item) => item.handler === handler);
7986
+ }
7987
+
7988
+ /**
7989
+ * Checks if the element has the exact handler registered for a specific event.
7990
+ *
7991
+ * @param {string} event - The event name to check.
7992
+ * @param {EventListenerOrEventListenerObject} handler - The handler function to check.
7993
+ * @returns {boolean}
7994
+ */
7995
+ hasExactEventListener(event, handler) {
7996
+ return TinyHtml_TinyHtml.hasExactEventListener(this, event, handler);
7997
+ }
7998
+
8250
7999
  /**
8251
8000
  * Registers an event listener on the specified element.
8252
8001
  *
8253
8002
  * @param {TinyEventTarget|TinyEventTarget[]} el - The target to listen on.
8254
8003
  * @param {string} event - The event type (e.g. 'click', 'keydown').
8255
- * @param {EventRegistryHandle} handler - The callback function to run on event.
8004
+ * @param {EventListenerOrEventListenerObject|null} handler - The callback function to run on event.
8256
8005
  * @param {EventRegistryOptions} [options] - Optional event listener options.
8257
8006
  * @returns {TinyEventTarget|TinyEventTarget[]}
8258
8007
  */
@@ -8274,7 +8023,7 @@ class TinyHtml_TinyHtml {
8274
8023
  * Registers an event listener on the specified element.
8275
8024
  *
8276
8025
  * @param {string} event - The event type (e.g. 'click', 'keydown').
8277
- * @param {EventRegistryHandle} handler - The callback function to run on event.
8026
+ * @param {EventListenerOrEventListenerObject|null} handler - The callback function to run on event.
8278
8027
  * @param {EventRegistryOptions} [options] - Optional event listener options.
8279
8028
  * @returns {TinyEventTarget|TinyEventTarget[]}
8280
8029
  */
@@ -8287,17 +8036,17 @@ class TinyHtml_TinyHtml {
8287
8036
  *
8288
8037
  * @param {TinyEventTarget|TinyEventTarget[]} el - The target to listen on.
8289
8038
  * @param {string} event - The event type (e.g. 'click', 'keydown').
8290
- * @param {EventRegistryHandle} handler - The callback function to run on event.
8039
+ * @param {EventListenerOrEventListenerObject} handler - The callback function to run on event.
8291
8040
  * @param {EventRegistryOptions} [options={}] - Optional event listener options.
8292
8041
  * @returns {TinyEventTarget|TinyEventTarget[]}
8293
8042
  */
8294
8043
  static once(el, event, handler, options = {}) {
8295
8044
  if (typeof event !== 'string') throw new TypeError('The event name must be a string.');
8296
8045
  TinyHtml_TinyHtml._preEventTargetElems(el, 'once').forEach((elem) => {
8297
- /** @type {EventRegistryHandle} e */
8046
+ /** @type {EventListenerOrEventListenerObject} */
8298
8047
  const wrapped = (e) => {
8299
8048
  TinyHtml_TinyHtml.off(elem, event, wrapped);
8300
- handler(e);
8049
+ if (typeof handler === 'function') handler(e);
8301
8050
  };
8302
8051
 
8303
8052
  TinyHtml_TinyHtml.on(
@@ -8314,7 +8063,7 @@ class TinyHtml_TinyHtml {
8314
8063
  * Registers an event listener that runs only once, then is removed.
8315
8064
  *
8316
8065
  * @param {string} event - The event type (e.g. 'click', 'keydown').
8317
- * @param {EventRegistryHandle} handler - The callback function to run on event.
8066
+ * @param {EventListenerOrEventListenerObject} handler - The callback function to run on event.
8318
8067
  * @param {EventRegistryOptions} [options={}] - Optional event listener options.
8319
8068
  * @returns {TinyEventTarget|TinyEventTarget[]}
8320
8069
  */
@@ -8327,7 +8076,7 @@ class TinyHtml_TinyHtml {
8327
8076
  *
8328
8077
  * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
8329
8078
  * @param {string} event - The event type.
8330
- * @param {EventRegistryHandle} handler - The function originally bound to the event.
8079
+ * @param {EventListenerOrEventListenerObject|null} handler - The function originally bound to the event.
8331
8080
  * @param {boolean|EventListenerOptions} [options] - Optional listener options.
8332
8081
  * @returns {TinyEventTarget|TinyEventTarget[]}
8333
8082
  */
@@ -8349,7 +8098,7 @@ class TinyHtml_TinyHtml {
8349
8098
  * Removes a specific event listener from an element.
8350
8099
  *
8351
8100
  * @param {string} event - The event type.
8352
- * @param {EventRegistryHandle} handler - The function originally bound to the event.
8101
+ * @param {EventListenerOrEventListenerObject|null} handler - The function originally bound to the event.
8353
8102
  * @param {boolean|EventListenerOptions} [options] - Optional listener options.
8354
8103
  * @returns {TinyEventTarget|TinyEventTarget[]}
8355
8104
  */
@@ -8392,7 +8141,7 @@ class TinyHtml_TinyHtml {
8392
8141
  * Removes all event listeners of all types from the element.
8393
8142
  *
8394
8143
  * @param {TinyEventTarget|TinyEventTarget[]} el - The target element.
8395
- * @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
8144
+ * @param {((handler: EventListenerOrEventListenerObject|null, event: string) => boolean)|null} [filterFn=null] -
8396
8145
  * Optional filter function to selectively remove specific handlers.
8397
8146
  * @returns {TinyEventTarget|TinyEventTarget[]}
8398
8147
  */
@@ -8419,7 +8168,7 @@ class TinyHtml_TinyHtml {
8419
8168
  /**
8420
8169
  * Removes all event listeners of all types from the element.
8421
8170
  *
8422
- * @param {((handler: EventListenerOrEventListenerObject, event: string) => boolean)|null} [filterFn=null] -
8171
+ * @param {((handler: EventListenerOrEventListenerObject|null, event: string) => boolean)|null} [filterFn=null] -
8423
8172
  * Optional filter function to selectively remove specific handlers.
8424
8173
  * @returns {TinyEventTarget|TinyEventTarget[]}
8425
8174
  */
@@ -10080,7 +9829,7 @@ class TinyDragger {
10080
9829
  if (
10081
9830
  options.vibration !== undefined &&
10082
9831
  options.vibration !== false &&
10083
- !objFilter_isJsonObject(options.vibration)
9832
+ !isJsonObject(options.vibration)
10084
9833
  )
10085
9834
  throw new Error('The "vibration" option must be an object or false.');
10086
9835
 
@@ -10124,7 +9873,7 @@ class TinyDragger {
10124
9873
  const vibrationTemplate = { start: false, end: false, collide: false, move: false };
10125
9874
  this.#vibration = Object.assign(
10126
9875
  vibrationTemplate,
10127
- objFilter_isJsonObject(options.vibration) ? options.vibration : {},
9876
+ isJsonObject(options.vibration) ? options.vibration : {},
10128
9877
  );
10129
9878
 
10130
9879
  if (typeof options.classDragging === 'string') this.#classDragging = options.classDragging;
@@ -11969,6 +11718,9 @@ class TinySmartScroller {
11969
11718
 
11970
11719
 
11971
11720
 
11721
+
11722
+
11723
+
11972
11724
 
11973
11725
 
11974
11726
 
@@ -12048,7 +11800,7 @@ class TinyUploadClicker {
12048
11800
  * @throws {TypeError} If the config is invalid or required options are missing.
12049
11801
  */
12050
11802
  constructor(options) {
12051
- if (!objFilter_isJsonObject(options))
11803
+ if (!isJsonObject(options))
12052
11804
  throw new TypeError('TinyUploadClicker: "options" must be a valid object.');
12053
11805
 
12054
11806
  this.#config = {
@@ -12114,10 +11866,10 @@ class TinyUploadClicker {
12114
11866
  )
12115
11867
  throw new TypeError('TinyUploadClicker: "onFileLoad" must be a function or null.');
12116
11868
 
12117
- if (options.inputAttributes !== undefined && !objFilter_isJsonObject(options.inputAttributes))
11869
+ if (options.inputAttributes !== undefined && !isJsonObject(options.inputAttributes))
12118
11870
  throw new TypeError('TinyUploadClicker: "inputAttributes" must be an object.');
12119
11871
 
12120
- if (options.inputStyles !== undefined && !objFilter_isJsonObject(options.inputStyles))
11872
+ if (options.inputStyles !== undefined && !isJsonObject(options.inputStyles))
12121
11873
  throw new TypeError('TinyUploadClicker: "inputStyles" must be an object.');
12122
11874
 
12123
11875
  this.#boundClick = this.#handleClick.bind(this);