tiny-essentials 1.18.0 → 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.
@@ -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.
@@ -10223,7 +9829,7 @@ class TinyDragger {
10223
9829
  if (
10224
9830
  options.vibration !== undefined &&
10225
9831
  options.vibration !== false &&
10226
- !objFilter_isJsonObject(options.vibration)
9832
+ !isJsonObject(options.vibration)
10227
9833
  )
10228
9834
  throw new Error('The "vibration" option must be an object or false.');
10229
9835
 
@@ -10267,7 +9873,7 @@ class TinyDragger {
10267
9873
  const vibrationTemplate = { start: false, end: false, collide: false, move: false };
10268
9874
  this.#vibration = Object.assign(
10269
9875
  vibrationTemplate,
10270
- objFilter_isJsonObject(options.vibration) ? options.vibration : {},
9876
+ isJsonObject(options.vibration) ? options.vibration : {},
10271
9877
  );
10272
9878
 
10273
9879
  if (typeof options.classDragging === 'string') this.#classDragging = options.classDragging;
@@ -12116,6 +11722,7 @@ class TinySmartScroller {
12116
11722
 
12117
11723
 
12118
11724
 
11725
+
12119
11726
 
12120
11727
 
12121
11728
  ;// ./src/v1/libs/TinyUploadClicker.mjs
@@ -12193,7 +11800,7 @@ class TinyUploadClicker {
12193
11800
  * @throws {TypeError} If the config is invalid or required options are missing.
12194
11801
  */
12195
11802
  constructor(options) {
12196
- if (!objFilter_isJsonObject(options))
11803
+ if (!isJsonObject(options))
12197
11804
  throw new TypeError('TinyUploadClicker: "options" must be a valid object.');
12198
11805
 
12199
11806
  this.#config = {
@@ -12259,10 +11866,10 @@ class TinyUploadClicker {
12259
11866
  )
12260
11867
  throw new TypeError('TinyUploadClicker: "onFileLoad" must be a function or null.');
12261
11868
 
12262
- if (options.inputAttributes !== undefined && !objFilter_isJsonObject(options.inputAttributes))
11869
+ if (options.inputAttributes !== undefined && !isJsonObject(options.inputAttributes))
12263
11870
  throw new TypeError('TinyUploadClicker: "inputAttributes" must be an object.');
12264
11871
 
12265
- if (options.inputStyles !== undefined && !objFilter_isJsonObject(options.inputStyles))
11872
+ if (options.inputStyles !== undefined && !isJsonObject(options.inputStyles))
12266
11873
  throw new TypeError('TinyUploadClicker: "inputStyles" must be an object.');
12267
11874
 
12268
11875
  this.#boundClick = this.#handleClick.bind(this);