tiny-essentials 1.28.0 → 1.28.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.
- package/changelog/1/28/1.md +23 -0
- package/dist/v1/TinyAdvancedRaffle.min.js +1 -1
- package/dist/v1/TinyAfterScrollWatcher.min.js +1 -1
- package/dist/v1/TinyAnalogClock.min.js +1 -1
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyDayNightCycle.min.js +1 -1
- package/dist/v1/TinyDragDropDetector.min.js +1 -1
- package/dist/v1/TinyDragger.min.js +1 -1
- package/dist/v1/TinyElementObserver.min.js +1 -1
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyGamepad.min.js +1 -1
- package/dist/v1/TinyHtml.min.js +1 -1
- package/dist/v1/TinyI18.min.js +1 -1
- package/dist/v1/TinyIframeEvents.min.js +1 -1
- package/dist/v1/TinyLocalStorage.min.js +1 -1
- package/dist/v1/TinyMediaPlayer.min.js +1 -1
- package/dist/v1/TinyNewWinEvents.min.js +1 -1
- package/dist/v1/TinyNotifyCenter.min.js +1 -1
- package/dist/v1/TinyRadioFm.min.js +1 -1
- package/dist/v1/TinyRateLimiter.min.js +1 -1
- package/dist/v1/TinySmartScroller.min.js +1 -1
- package/dist/v1/TinyTextDiffer.min.js +1 -1
- package/dist/v1/TinyTextarea.min.js +1 -1
- package/dist/v1/TinyTimeout.min.js +1 -1
- package/dist/v1/TinyToastNotify.min.js +1 -1
- package/dist/v1/TinyUploadClicker.min.js +1 -1
- package/dist/v1/UltraRandomMsgGen.min.js +1 -1
- package/dist/v1/basics/array.cjs +57 -9
- package/dist/v1/basics/array.d.mts +27 -11
- package/dist/v1/basics/array.mjs +51 -9
- package/dist/v1/basics/clock.cjs +94 -42
- package/dist/v1/basics/clock.d.mts +10 -10
- package/dist/v1/basics/clock.mjs +91 -54
- package/dist/v1/basics/deprecated/html.cjs +246 -0
- package/dist/v1/basics/deprecated/html.d.mts +63 -0
- package/dist/v1/basics/deprecated/html.mjs +190 -0
- package/dist/v1/basics/html.cjs +149 -130
- package/dist/v1/basics/html.d.mts +26 -61
- package/dist/v1/basics/html.mjs +113 -105
- package/dist/v1/basics/index.cjs +2 -0
- package/dist/v1/basics/index.d.mts +3 -1
- package/dist/v1/basics/index.mjs +3 -3
- package/dist/v1/basics/mediaContent.cjs +51 -9
- package/dist/v1/basics/mediaContent.d.mts +0 -87
- package/dist/v1/basics/mediaContent.mjs +40 -10
- package/dist/v1/basics/objChecker.cjs +7 -5
- package/dist/v1/basics/objChecker.d.mts +3 -2
- package/dist/v1/basics/objChecker.mjs +6 -6
- package/dist/v1/basics/objFilter.cjs +40 -7
- package/dist/v1/basics/objFilter.d.mts +6 -6
- package/dist/v1/basics/objFilter.mjs +32 -7
- package/dist/v1/basics/simpleMath.cjs +81 -25
- package/dist/v1/basics/simpleMath.d.mts +2 -2
- package/dist/v1/basics/simpleMath.mjs +70 -27
- package/dist/v1/basics/text.cjs +76 -31
- package/dist/v1/basics/text.d.mts +69 -7
- package/dist/v1/basics/text.mjs +62 -31
- package/dist/v1/index.cjs +4 -0
- package/dist/v1/index.d.mts +4 -1
- package/dist/v1/index.mjs +4 -3
- package/dist/v1/libs/TinyAdvancedRaffle.cjs +64 -50
- package/dist/v1/libs/TinyAdvancedRaffle.d.mts +4 -13
- package/dist/v1/libs/TinyAdvancedRaffle.mjs +62 -49
- package/dist/v1/libs/TinyAfterScrollWatcher.cjs +17 -1
- package/dist/v1/libs/TinyAfterScrollWatcher.d.mts +4 -1
- package/dist/v1/libs/TinyAfterScrollWatcher.mjs +15 -1
- package/dist/v1/libs/TinyAnalogClock.cjs +60 -0
- package/dist/v1/libs/TinyAnalogClock.d.mts +6 -0
- package/dist/v1/libs/TinyAnalogClock.mjs +57 -0
- package/dist/v1/libs/TinyClipboard.cjs +2 -0
- package/dist/v1/libs/TinyClipboard.d.mts +4 -2
- package/dist/v1/libs/TinyClipboard.mjs +2 -0
- package/dist/v1/libs/TinyColorConverter.cjs +1 -0
- package/dist/v1/libs/TinyColorConverter.d.mts +2 -1
- package/dist/v1/libs/TinyColorConverter.mjs +1 -0
- package/dist/v1/libs/TinyDayNightCycle.cjs +67 -42
- package/dist/v1/libs/TinyDayNightCycle.d.mts +2 -7
- package/dist/v1/libs/TinyDayNightCycle.mjs +65 -41
- package/dist/v1/libs/TinyDragDropDetector.cjs +25 -0
- package/dist/v1/libs/TinyDragDropDetector.d.mts +6 -0
- package/dist/v1/libs/TinyDragDropDetector.mjs +22 -0
- package/dist/v1/libs/TinyDragger.cjs +46 -19
- package/dist/v1/libs/TinyDragger.mjs +45 -18
- package/dist/v1/libs/TinyElementObserver.cjs +39 -0
- package/dist/v1/libs/TinyElementObserver.d.mts +6 -0
- package/dist/v1/libs/TinyElementObserver.mjs +36 -0
- package/dist/v1/libs/TinyGamepad.cjs +131 -1
- package/dist/v1/libs/TinyGamepad.mjs +129 -1
- package/dist/v1/libs/TinyHtml.cjs +1 -1
- package/dist/v1/libs/TinyHtml.mjs +2 -2
- package/dist/v1/libs/TinyI18.cjs +61 -0
- package/dist/v1/libs/TinyI18.d.mts +14 -0
- package/dist/v1/libs/TinyI18.mjs +55 -0
- package/dist/v1/libs/TinyIframeEvents.cjs +18 -1
- package/dist/v1/libs/TinyIframeEvents.mjs +18 -1
- package/dist/v1/libs/TinyLocalStorage.cjs +48 -0
- package/dist/v1/libs/TinyLocalStorage.d.mts +6 -0
- package/dist/v1/libs/TinyLocalStorage.mjs +45 -0
- package/dist/v1/libs/TinyMediaPlayer.cjs +72 -0
- package/dist/v1/libs/TinyMediaPlayer.d.mts +10 -0
- package/dist/v1/libs/TinyMediaPlayer.mjs +66 -0
- package/dist/v1/libs/TinyNewWinEvents.cjs +29 -0
- package/dist/v1/libs/TinyNewWinEvents.d.mts +6 -0
- package/dist/v1/libs/TinyNewWinEvents.mjs +26 -0
- package/dist/v1/libs/TinyNotifyCenter.cjs +36 -0
- package/dist/v1/libs/TinyNotifyCenter.d.mts +6 -0
- package/dist/v1/libs/TinyNotifyCenter.mjs +32 -0
- package/dist/v1/libs/TinyRadioFm.cjs +38 -0
- package/dist/v1/libs/TinyRadioFm.d.mts +1 -0
- package/dist/v1/libs/TinyRadioFm.mjs +35 -0
- package/dist/v1/libs/TinyRateLimiter.cjs +52 -0
- package/dist/v1/libs/TinyRateLimiter.d.mts +6 -0
- package/dist/v1/libs/TinyRateLimiter.mjs +48 -0
- package/dist/v1/libs/TinySmartScroller.cjs +52 -3
- package/dist/v1/libs/TinySmartScroller.d.mts +17 -15
- package/dist/v1/libs/TinySmartScroller.mjs +51 -5
- package/dist/v1/libs/TinyTextDiffer.cjs +19 -18
- package/dist/v1/libs/TinyTextDiffer.d.mts +1 -0
- package/dist/v1/libs/TinyTextDiffer.mjs +16 -17
- package/dist/v1/libs/TinyTextarea.cjs +19 -0
- package/dist/v1/libs/TinyTextarea.d.mts +6 -0
- package/dist/v1/libs/TinyTextarea.mjs +17 -0
- package/dist/v1/libs/TinyTimeout.cjs +11 -1
- package/dist/v1/libs/TinyTimeout.mjs +9 -1
- package/dist/v1/libs/TinyToastNotify.cjs +35 -0
- package/dist/v1/libs/TinyToastNotify.d.mts +6 -0
- package/dist/v1/libs/TinyToastNotify.mjs +31 -0
- package/dist/v1/libs/TinyUploadClicker.cjs +19 -0
- package/dist/v1/libs/TinyUploadClicker.d.mts +6 -0
- package/dist/v1/libs/TinyUploadClicker.mjs +17 -0
- package/dist/v1/libs/UltraRandomMsgGen.cjs +148 -35
- package/dist/v1/libs/UltraRandomMsgGen.d.mts +17 -3
- package/dist/v1/libs/UltraRandomMsgGen.mjs +133 -34
- package/dist/v1/libs/utils.cjs +21 -0
- package/dist/v1/libs/utils.d.mts +2 -0
- package/dist/v1/libs/utils.mjs +16 -0
- package/docs/v1/README.md +1 -0
- package/docs/v1/basics/array.md +2 -2
- package/docs/v1/basics/deprecated/html.md +17 -0
- package/docs/v1/basics/fullScreen.md +1 -1
- package/docs/v1/basics/html.md +12 -26
- package/docs/v1/basics/mediaContent.md +1 -1
- package/docs/v1/basics/objChecker.md +1 -1
- package/docs/v1/basics/text.md +2 -2
- package/docs/v1/fileManager/main.md +1 -1
- package/docs/v1/libs/ColorSafeStringify.md +1 -1
- package/docs/v1/libs/TinyAfterScrollWatcher.md +1 -1
- package/docs/v1/libs/TinyAnalogClock.md +1 -1
- package/docs/v1/libs/TinyArrayComparator.md +1 -1
- package/docs/v1/libs/TinyArrayPaginator.md +1 -1
- package/docs/v1/libs/TinyClassManager/TinyPluginInliner.md +2 -2
- package/docs/v1/libs/TinyClassManager.md +1 -1
- package/docs/v1/libs/TinyClipboard.md +1 -1
- package/docs/v1/libs/TinyCookieConsent.md +1 -1
- package/docs/v1/libs/TinyDomReadyManager.md +1 -1
- package/docs/v1/libs/TinyDragDropDetector.md +1 -1
- package/docs/v1/libs/TinyDragger.md +1 -1
- package/docs/v1/libs/TinyElementObserver.md +1 -1
- package/docs/v1/libs/TinyHtml.md +1 -1
- package/docs/v1/libs/TinyIframeEvents.md +2 -2
- package/docs/v1/libs/TinyLevelUp.md +1 -1
- package/docs/v1/libs/TinyLoadingScreen.md +1 -1
- package/docs/v1/libs/TinyNewWinEvents.md +2 -2
- package/docs/v1/libs/TinyNotifications.md +1 -1
- package/docs/v1/libs/TinyPromiseQueue.md +2 -2
- package/docs/v1/libs/TinyTextDiffer.md +1 -1
- package/docs/v1/libs/TinyTextarea.md +1 -1
- package/package.json +10 -1
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { trackFetchProgress } from '../html.mjs';
|
|
2
|
+
import { isJsonObject } from '../objChecker.mjs';
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} FetchTemplateOptions
|
|
5
|
+
* @property {string} [method="GET"] - HTTP method to use (GET, POST, etc.).
|
|
6
|
+
* @property {any} [body] - Request body (only for methods like POST, PUT).
|
|
7
|
+
* @property {number} [timeout=0] - Timeout in milliseconds (ignored if signal is provided).
|
|
8
|
+
* @property {number} [retries=0] - Number of retry attempts (ignored if signal is provided).
|
|
9
|
+
* @property {Headers|Record<string, *>} [headers={}] - Additional headers.
|
|
10
|
+
* @property {AbortSignal|null} [signal] - External AbortSignal; disables timeout and retries.
|
|
11
|
+
* @property {import('../html.mjs').FetchOnProgressResult} [onProgress] - Track the load progress.
|
|
12
|
+
* @deprecated
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} url - The full URL to fetch data from.
|
|
16
|
+
* @param {FetchTemplateOptions} [options] - Optional settings.
|
|
17
|
+
* @returns {Promise<Response>} Result data.
|
|
18
|
+
* @throws {Error} Throws if fetch fails, times out.
|
|
19
|
+
*/
|
|
20
|
+
async function fetchTemplate(url, options = {}) {
|
|
21
|
+
if (options !== null && typeof options !== 'object') {
|
|
22
|
+
throw new TypeError('The "options" argument must be an object.');
|
|
23
|
+
}
|
|
24
|
+
const { method = 'GET', body, timeout = 0, retries = 0, headers = {}, signal = null, onProgress, } = options;
|
|
25
|
+
if (typeof url !== 'string' ||
|
|
26
|
+
(!url.startsWith('../') &&
|
|
27
|
+
!url.startsWith('./') &&
|
|
28
|
+
!url.startsWith('/') &&
|
|
29
|
+
!url.startsWith('https://') &&
|
|
30
|
+
!url.startsWith('http://')))
|
|
31
|
+
throw new Error('Invalid URL: must be a valid http or https address.');
|
|
32
|
+
if (typeof method !== 'string' || !method.trim())
|
|
33
|
+
throw new Error('Invalid method: must be a non-empty string.');
|
|
34
|
+
if (headers !== null && typeof headers !== 'object') {
|
|
35
|
+
throw new TypeError('The "headers" option must be an object.');
|
|
36
|
+
}
|
|
37
|
+
if (signal !== null && !(signal instanceof AbortSignal)) {
|
|
38
|
+
throw new TypeError('The "signal" option must be an instance of AbortSignal.');
|
|
39
|
+
}
|
|
40
|
+
if (onProgress !== undefined && typeof onProgress !== 'function') {
|
|
41
|
+
throw new TypeError('The "onProgress" option must be a function.');
|
|
42
|
+
}
|
|
43
|
+
if (!signal) {
|
|
44
|
+
if (typeof timeout !== 'number' ||
|
|
45
|
+
!Number.isFinite(timeout) ||
|
|
46
|
+
Number.isNaN(timeout) ||
|
|
47
|
+
timeout < 0)
|
|
48
|
+
throw new Error('Invalid timeout: must be a positive number.');
|
|
49
|
+
if (typeof retries !== 'number' ||
|
|
50
|
+
!Number.isFinite(retries) ||
|
|
51
|
+
Number.isNaN(retries) ||
|
|
52
|
+
retries < 0)
|
|
53
|
+
throw new Error('Invalid retries: must be a positive number.');
|
|
54
|
+
}
|
|
55
|
+
const attempts = signal ? 1 : retries + 1;
|
|
56
|
+
/** @type {Error|null} */
|
|
57
|
+
let lastError = null;
|
|
58
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
59
|
+
const controller = signal ? null : new AbortController();
|
|
60
|
+
const localSignal = signal || (controller?.signal ?? null);
|
|
61
|
+
const timer = !signal && timeout && controller ? setTimeout(() => controller.abort(), timeout) : null;
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetch(url, {
|
|
64
|
+
method: method.toUpperCase(),
|
|
65
|
+
headers: {
|
|
66
|
+
Accept: 'application/json',
|
|
67
|
+
...headers,
|
|
68
|
+
},
|
|
69
|
+
body: body !== undefined ? (isJsonObject(body) ? JSON.stringify(body) : body) : undefined,
|
|
70
|
+
signal: localSignal,
|
|
71
|
+
});
|
|
72
|
+
if (timer)
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
if (!response.ok)
|
|
75
|
+
throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
|
|
76
|
+
// If onProgress is not provided or body is null, return original response
|
|
77
|
+
if (!onProgress)
|
|
78
|
+
return response;
|
|
79
|
+
return trackFetchProgress(response, onProgress);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
lastError = /** @type {Error} */ (err);
|
|
83
|
+
if (signal)
|
|
84
|
+
break; // if an external signal came, it does not retry
|
|
85
|
+
if (attempt < retries)
|
|
86
|
+
await new Promise((resolve) => setTimeout(resolve, 300 * (attempt + 1)));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
throw new Error(`Failed to fetch JSON from "${url}"${lastError ? `: ${lastError.message}` : '.'}`);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Loads and parses a JSON from a remote URL using Fetch API.
|
|
93
|
+
*
|
|
94
|
+
* @param {string} url - The full URL to fetch JSON from.
|
|
95
|
+
* @param {FetchTemplateOptions} [options] - Optional settings.
|
|
96
|
+
* @returns {Promise<any[] | Record<string | number | symbol, unknown>>} Parsed JSON object.
|
|
97
|
+
* @throws {Error} Throws if fetch fails, times out, or returns invalid JSON.
|
|
98
|
+
* @deprecated
|
|
99
|
+
*/
|
|
100
|
+
export async function fetchJson(url, options) {
|
|
101
|
+
if (typeof url !== 'string') {
|
|
102
|
+
return Promise.reject(new TypeError('The "url" argument must be a string.'));
|
|
103
|
+
}
|
|
104
|
+
if (options !== undefined && (options === null || typeof options !== 'object')) {
|
|
105
|
+
return Promise.reject(new TypeError('The "options" argument must be an object.'));
|
|
106
|
+
}
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
fetchTemplate(url, options)
|
|
109
|
+
.then(async (res) => {
|
|
110
|
+
const contentType = res.headers.get('content-type') || '';
|
|
111
|
+
if (!contentType.includes('application/json'))
|
|
112
|
+
throw new Error(`Unexpected content-type: ${contentType}`);
|
|
113
|
+
const data = await res.json();
|
|
114
|
+
if (!Array.isArray(data) && !isJsonObject(data))
|
|
115
|
+
throw new Error('Received invalid data instead of valid JSON.');
|
|
116
|
+
return resolve(data);
|
|
117
|
+
})
|
|
118
|
+
.catch(reject);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Loads a remote file as a Blob using Fetch API.
|
|
123
|
+
*
|
|
124
|
+
* @param {string} url - The full URL to fetch the file from.
|
|
125
|
+
* @param {string[]} [allowedMimeTypes] - Optional list of accepted MIME types (e.g., ['image/jpeg']).
|
|
126
|
+
* @param {FetchTemplateOptions} [options] - Optional fetch options.
|
|
127
|
+
* @returns {Promise<Blob>} - The fetched file as a Blob.
|
|
128
|
+
* @throws {Error} Throws if fetch fails, response is not ok, or MIME type is not allowed.
|
|
129
|
+
* @deprecated
|
|
130
|
+
*/
|
|
131
|
+
export async function fetchBlob(url, allowedMimeTypes, options) {
|
|
132
|
+
if (typeof url !== 'string') {
|
|
133
|
+
return Promise.reject(new TypeError('The "url" argument must be a string.'));
|
|
134
|
+
}
|
|
135
|
+
if (allowedMimeTypes !== undefined && !Array.isArray(allowedMimeTypes)) {
|
|
136
|
+
return Promise.reject(new TypeError('The "allowedMimeTypes" argument must be an array of strings.'));
|
|
137
|
+
}
|
|
138
|
+
if (options !== undefined && (options === null || typeof options !== 'object')) {
|
|
139
|
+
return Promise.reject(new TypeError('The "options" argument must be an object.'));
|
|
140
|
+
}
|
|
141
|
+
return new Promise((resolve, reject) => {
|
|
142
|
+
fetchTemplate(url, options)
|
|
143
|
+
.then(async (res) => {
|
|
144
|
+
const contentType = res.headers.get('content-type') || '';
|
|
145
|
+
if (Array.isArray(allowedMimeTypes) &&
|
|
146
|
+
allowedMimeTypes.length > 0 &&
|
|
147
|
+
!allowedMimeTypes.some((type) => contentType.includes(type))) {
|
|
148
|
+
throw new Error(`Blocked MIME type: ${contentType}`);
|
|
149
|
+
}
|
|
150
|
+
const data = await res.blob();
|
|
151
|
+
return resolve(data);
|
|
152
|
+
})
|
|
153
|
+
.catch(reject);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Loads a remote file as a text using Fetch API.
|
|
158
|
+
*
|
|
159
|
+
* @param {string} url - The full URL to fetch the file from.
|
|
160
|
+
* @param {string[]} [allowedMimeTypes] - Optional list of accepted MIME types (e.g., ['image/jpeg']).
|
|
161
|
+
* @param {FetchTemplateOptions} [options] - Optional fetch options.
|
|
162
|
+
* @returns {Promise<string>} - The fetched file as a text.
|
|
163
|
+
* @throws {Error} Throws if fetch fails, response is not ok, or MIME type is not allowed.
|
|
164
|
+
* @deprecated
|
|
165
|
+
*/
|
|
166
|
+
export async function fetchText(url, allowedMimeTypes, options) {
|
|
167
|
+
if (typeof url !== 'string') {
|
|
168
|
+
return Promise.reject(new TypeError('The "url" argument must be a string.'));
|
|
169
|
+
}
|
|
170
|
+
if (allowedMimeTypes !== undefined && !Array.isArray(allowedMimeTypes)) {
|
|
171
|
+
return Promise.reject(new TypeError('The "allowedMimeTypes" argument must be an array of strings.'));
|
|
172
|
+
}
|
|
173
|
+
if (options !== undefined && (options === null || typeof options !== 'object')) {
|
|
174
|
+
return Promise.reject(new TypeError('The "options" argument must be an object.'));
|
|
175
|
+
}
|
|
176
|
+
return new Promise((resolve, reject) => {
|
|
177
|
+
fetchTemplate(url, options)
|
|
178
|
+
.then(async (res) => {
|
|
179
|
+
const contentType = res.headers.get('content-type') || '';
|
|
180
|
+
if (Array.isArray(allowedMimeTypes) &&
|
|
181
|
+
allowedMimeTypes.length > 0 &&
|
|
182
|
+
!allowedMimeTypes.some((type) => contentType.includes(type))) {
|
|
183
|
+
throw new Error(`Blocked MIME type: ${contentType}`);
|
|
184
|
+
}
|
|
185
|
+
const data = await res.text();
|
|
186
|
+
return resolve(data);
|
|
187
|
+
})
|
|
188
|
+
.catch(reject);
|
|
189
|
+
});
|
|
190
|
+
}
|
package/dist/v1/basics/html.cjs
CHANGED
|
@@ -7,14 +7,28 @@ var objChecker = require('./objChecker.cjs');
|
|
|
7
7
|
/**
|
|
8
8
|
* Reads the contents of a file using the specified FileReader method.
|
|
9
9
|
*
|
|
10
|
-
* @param {File} file - The file to be read.
|
|
10
|
+
* @param {File|Blob} file - The file to be read.
|
|
11
11
|
* @param {'readAsArrayBuffer'|'readAsDataURL'|'readAsText'|'readAsBinaryString'} method -
|
|
12
|
-
*
|
|
12
|
+
* The FileReader method to use for reading the file.
|
|
13
13
|
* @returns {Promise<any>} - A promise that resolves with the file content, according to the chosen method.
|
|
14
|
+
* @throws {TypeError} - If arguments don't match expected types.
|
|
14
15
|
* @throws {Error} - If an unexpected error occurs while handling the result.
|
|
15
16
|
* @throws {DOMException} - If the FileReader encounters an error while reading the file.
|
|
16
17
|
*/
|
|
17
18
|
function readFileBlob(file, method) {
|
|
19
|
+
if (!(file instanceof Blob)) {
|
|
20
|
+
return Promise.reject(
|
|
21
|
+
new TypeError('The "file" argument must be an instance of Blob or File.'),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const validMethods = ['readAsArrayBuffer', 'readAsDataURL', 'readAsText', 'readAsBinaryString'];
|
|
26
|
+
if (typeof method !== 'string' || !validMethods.includes(method)) {
|
|
27
|
+
return Promise.reject(
|
|
28
|
+
new TypeError(`The "method" argument must be one of: ${validMethods.join(', ')}.`),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
18
32
|
return new Promise((resolve, reject) => {
|
|
19
33
|
const reader = new FileReader();
|
|
20
34
|
reader.onload = () => {
|
|
@@ -36,9 +50,9 @@ function readFileBlob(file, method) {
|
|
|
36
50
|
*
|
|
37
51
|
* Performs strict validation to ensure the result is a valid Base64 string or a proper data URL.
|
|
38
52
|
*
|
|
39
|
-
* @param {File} file - The file to be read.
|
|
53
|
+
* @param {File|Blob} file - The file to be read.
|
|
40
54
|
* @param {boolean|string} [isDataUrl=false] - If true, returns a full data URL; if false, returns only the Base64 string;
|
|
41
|
-
*
|
|
55
|
+
* if a string is passed, it is used as the MIME type in the data URL.
|
|
42
56
|
* @returns {Promise<string>} - A promise that resolves with the Base64 string or data URL.
|
|
43
57
|
*
|
|
44
58
|
* @throws {TypeError} - If the result is not a string or if `isDataUrl` is not a valid type.
|
|
@@ -46,9 +60,18 @@ function readFileBlob(file, method) {
|
|
|
46
60
|
* @throws {DOMException} - If the FileReader fails to read the file.
|
|
47
61
|
*/
|
|
48
62
|
function readBase64Blob(file, isDataUrl = false) {
|
|
63
|
+
// Validação do argumento 'file'
|
|
64
|
+
if (!(file instanceof Blob)) {
|
|
65
|
+
return Promise.reject(
|
|
66
|
+
new TypeError('The "file" argument must be an instance of Blob or File.'),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (typeof isDataUrl !== 'string' && typeof isDataUrl !== 'boolean') {
|
|
71
|
+
return Promise.reject(new TypeError('The isDataUrl parameter must be a boolean or a string.'));
|
|
72
|
+
}
|
|
73
|
+
|
|
49
74
|
return new Promise((resolve, reject) => {
|
|
50
|
-
if (typeof isDataUrl !== 'string' && typeof isDataUrl !== 'boolean')
|
|
51
|
-
reject(new TypeError('The isDataUrl parameter must be a boolean or a string.'));
|
|
52
75
|
readFileBlob(file, 'readAsDataURL')
|
|
53
76
|
.then(
|
|
54
77
|
/**
|
|
@@ -80,7 +103,7 @@ function readBase64Blob(file, isDataUrl = false) {
|
|
|
80
103
|
*
|
|
81
104
|
* Performs several checks to ensure the file contains valid, parsable JSON data.
|
|
82
105
|
*
|
|
83
|
-
* @param {File} file - The file to be read. It must contain valid JSON as plain text.
|
|
106
|
+
* @param {File|Blob} file - The file to be read. It must contain valid JSON as plain text.
|
|
84
107
|
* @returns {Promise<Record<string|number|symbol, any>|any[]>} - A promise that resolves with the parsed JSON object.
|
|
85
108
|
*
|
|
86
109
|
* @throws {SyntaxError} - If the file content is not valid JSON syntax.
|
|
@@ -89,6 +112,12 @@ function readBase64Blob(file, isDataUrl = false) {
|
|
|
89
112
|
* @throws {DOMException} - If the FileReader fails to read the file.
|
|
90
113
|
*/
|
|
91
114
|
function readJsonBlob(file) {
|
|
115
|
+
if (!(file instanceof Blob)) {
|
|
116
|
+
return Promise.reject(
|
|
117
|
+
new TypeError('The "file" argument must be an instance of Blob or File.'),
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
92
121
|
return new Promise((resolve, reject) =>
|
|
93
122
|
readFileBlob(file, 'readAsText')
|
|
94
123
|
.then((data) => {
|
|
@@ -109,8 +138,16 @@ function readJsonBlob(file) {
|
|
|
109
138
|
* @param {string} filename
|
|
110
139
|
* @param {any} data
|
|
111
140
|
* @param {number} [spaces=2]
|
|
141
|
+
* @throws {TypeError} - If arguments do not match expected types.
|
|
112
142
|
*/
|
|
113
143
|
function saveJsonFile(filename, data, spaces = 2) {
|
|
144
|
+
if (typeof filename !== 'string') {
|
|
145
|
+
throw new TypeError('The "filename" argument must be a string.');
|
|
146
|
+
}
|
|
147
|
+
if (typeof spaces !== 'number' || !Number.isInteger(spaces) || spaces < 0) {
|
|
148
|
+
throw new TypeError('The "spaces" argument must be a non-negative integer.');
|
|
149
|
+
}
|
|
150
|
+
|
|
114
151
|
const json = JSON.stringify(data, null, spaces);
|
|
115
152
|
const blob = new Blob([json], { type: 'application/json' });
|
|
116
153
|
const url = URL.createObjectURL(blob);
|
|
@@ -126,17 +163,6 @@ function saveJsonFile(filename, data, spaces = 2) {
|
|
|
126
163
|
URL.revokeObjectURL(url);
|
|
127
164
|
}
|
|
128
165
|
|
|
129
|
-
/**
|
|
130
|
-
* @typedef {Object} FetchTemplateOptions
|
|
131
|
-
* @property {string} [method="GET"] - HTTP method to use (GET, POST, etc.).
|
|
132
|
-
* @property {any} [body] - Request body (only for methods like POST, PUT).
|
|
133
|
-
* @property {number} [timeout=0] - Timeout in milliseconds (ignored if signal is provided).
|
|
134
|
-
* @property {number} [retries=0] - Number of retry attempts (ignored if signal is provided).
|
|
135
|
-
* @property {Headers|Record<string, *>} [headers={}] - Additional headers.
|
|
136
|
-
* @property {AbortSignal|null} [signal] - External AbortSignal; disables timeout and retries.
|
|
137
|
-
* @property {FetchOnProgressResult} [onProgress] - Track the load progress.
|
|
138
|
-
*/
|
|
139
|
-
|
|
140
166
|
/**
|
|
141
167
|
* Callback function used to report the progress of a data download.
|
|
142
168
|
*
|
|
@@ -155,9 +181,21 @@ function saveJsonFile(filename, data, spaces = 2) {
|
|
|
155
181
|
*
|
|
156
182
|
* @param {Response} response - The original response object to be tracked.
|
|
157
183
|
* @param {FetchOnProgressResult} onProgress - The callback function to handle progress events.
|
|
184
|
+
* @param {(res: Response) => ResponseInit} [returnResponse] - The callback function to return the ResponseInit to ReadableStream.
|
|
158
185
|
* @returns {Response} A new Response object with the tracked stream.
|
|
159
186
|
*/
|
|
160
|
-
function trackFetchProgress(
|
|
187
|
+
function trackFetchProgress(
|
|
188
|
+
response,
|
|
189
|
+
onProgress,
|
|
190
|
+
returnResponse = (res) => ({
|
|
191
|
+
headers: res.headers,
|
|
192
|
+
status: res.status,
|
|
193
|
+
statusText: res.statusText,
|
|
194
|
+
}),
|
|
195
|
+
) {
|
|
196
|
+
if (!(response instanceof Response)) {
|
|
197
|
+
throw new TypeError('The "response" argument must be an instance of Response.');
|
|
198
|
+
}
|
|
161
199
|
if (typeof onProgress !== 'function')
|
|
162
200
|
throw new TypeError('The "onProgress" argument must be a function.');
|
|
163
201
|
if (!response.body) return response;
|
|
@@ -181,107 +219,33 @@ function trackFetchProgress(response, onProgress) {
|
|
|
181
219
|
},
|
|
182
220
|
});
|
|
183
221
|
|
|
184
|
-
return new Response(stream,
|
|
185
|
-
headers: response.headers,
|
|
186
|
-
status: response.status,
|
|
187
|
-
statusText: response.statusText,
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* @param {string} url - The full URL to fetch data from.
|
|
193
|
-
* @param {FetchTemplateOptions} [options] - Optional settings.
|
|
194
|
-
* @returns {Promise<Response>} Result data.
|
|
195
|
-
* @throws {Error} Throws if fetch fails, times out.
|
|
196
|
-
*/
|
|
197
|
-
async function fetchTemplate(
|
|
198
|
-
url,
|
|
199
|
-
{ method = 'GET', body, timeout = 0, retries = 0, headers = {}, signal = null, onProgress } = {},
|
|
200
|
-
) {
|
|
201
|
-
if (
|
|
202
|
-
typeof url !== 'string' ||
|
|
203
|
-
(!url.startsWith('../') &&
|
|
204
|
-
!url.startsWith('./') &&
|
|
205
|
-
!url.startsWith('/') &&
|
|
206
|
-
!url.startsWith('https://') &&
|
|
207
|
-
!url.startsWith('http://'))
|
|
208
|
-
)
|
|
209
|
-
throw new Error('Invalid URL: must be a valid http or https address.');
|
|
210
|
-
|
|
211
|
-
if (typeof method !== 'string' || !method.trim())
|
|
212
|
-
throw new Error('Invalid method: must be a non-empty string.');
|
|
213
|
-
|
|
214
|
-
if (!signal) {
|
|
215
|
-
if (
|
|
216
|
-
typeof timeout !== 'number' ||
|
|
217
|
-
!Number.isFinite(timeout) ||
|
|
218
|
-
Number.isNaN(timeout) ||
|
|
219
|
-
timeout < 0
|
|
220
|
-
)
|
|
221
|
-
throw new Error('Invalid timeout: must be a positive number.');
|
|
222
|
-
|
|
223
|
-
if (
|
|
224
|
-
typeof retries !== 'number' ||
|
|
225
|
-
!Number.isFinite(retries) ||
|
|
226
|
-
Number.isNaN(retries) ||
|
|
227
|
-
retries < 0
|
|
228
|
-
)
|
|
229
|
-
throw new Error('Invalid retries: must be a positive number.');
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const attempts = signal ? 1 : retries + 1;
|
|
233
|
-
/** @type {Error|null} */
|
|
234
|
-
let lastError = null;
|
|
235
|
-
|
|
236
|
-
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
237
|
-
const controller = signal ? null : new AbortController();
|
|
238
|
-
const localSignal = signal || (controller?.signal ?? null);
|
|
239
|
-
const timer =
|
|
240
|
-
!signal && timeout && controller ? setTimeout(() => controller.abort(), timeout) : null;
|
|
241
|
-
|
|
242
|
-
try {
|
|
243
|
-
const response = await fetch(url, {
|
|
244
|
-
method: method.toUpperCase(),
|
|
245
|
-
headers: {
|
|
246
|
-
Accept: 'application/json',
|
|
247
|
-
...headers,
|
|
248
|
-
},
|
|
249
|
-
body: body !== undefined ? (objChecker.isJsonObject(body) ? JSON.stringify(body) : body) : undefined,
|
|
250
|
-
signal: localSignal,
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
if (timer) clearTimeout(timer);
|
|
254
|
-
|
|
255
|
-
if (!response.ok) throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
|
|
256
|
-
|
|
257
|
-
// If onProgress is not provided or body is null, return original response
|
|
258
|
-
if (!onProgress) return response;
|
|
259
|
-
return trackFetchProgress(response, onProgress);
|
|
260
|
-
} catch (err) {
|
|
261
|
-
lastError = /** @type {Error} */ (err);
|
|
262
|
-
if (signal) break; // if an external signal came, it does not retry
|
|
263
|
-
if (attempt < retries)
|
|
264
|
-
await new Promise((resolve) => setTimeout(resolve, 300 * (attempt + 1)));
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
throw new Error(
|
|
269
|
-
`Failed to fetch JSON from "${url}"${lastError ? `: ${lastError.message}` : '.'}`,
|
|
270
|
-
);
|
|
222
|
+
return new Response(stream, returnResponse(response));
|
|
271
223
|
}
|
|
272
224
|
|
|
273
225
|
/**
|
|
274
226
|
* Loads and parses a JSON from a remote URL using Fetch API.
|
|
275
227
|
*
|
|
276
|
-
* @param {string} url - The full URL to fetch JSON from.
|
|
277
|
-
* @param {
|
|
228
|
+
* @param {string | URL | Request} url - The full URL to fetch JSON from.
|
|
229
|
+
* @param {RequestInit|null} [options] - Optional settings.
|
|
230
|
+
* @param {FetchOnProgressResult} [onProgress] - Track the load progress.
|
|
278
231
|
* @returns {Promise<any[] | Record<string | number | symbol, unknown>>} Parsed JSON object.
|
|
279
232
|
* @throws {Error} Throws if fetch fails, times out, or returns invalid JSON.
|
|
280
233
|
*/
|
|
281
|
-
async function fetchJson(url, options) {
|
|
234
|
+
async function fetchJson(url, options, onProgress) {
|
|
235
|
+
if (typeof url !== 'string') {
|
|
236
|
+
return Promise.reject(new TypeError('The "url" argument must be a string.'));
|
|
237
|
+
}
|
|
238
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
239
|
+
return Promise.reject(new TypeError('The "options" argument must be an object.'));
|
|
240
|
+
}
|
|
241
|
+
if (onProgress !== undefined && typeof onProgress !== 'function') {
|
|
242
|
+
return Promise.reject(new TypeError('The "onProgress" option must be a function.'));
|
|
243
|
+
}
|
|
244
|
+
|
|
282
245
|
return new Promise((resolve, reject) => {
|
|
283
|
-
|
|
284
|
-
.then(async (
|
|
246
|
+
fetch(url, options ?? undefined)
|
|
247
|
+
.then(async (response) => {
|
|
248
|
+
const res = onProgress ? trackFetchProgress(response, onProgress) : response;
|
|
285
249
|
const contentType = res.headers.get('content-type') || '';
|
|
286
250
|
if (!contentType.includes('application/json'))
|
|
287
251
|
throw new Error(`Unexpected content-type: ${contentType}`);
|
|
@@ -300,16 +264,33 @@ async function fetchJson(url, options) {
|
|
|
300
264
|
/**
|
|
301
265
|
* Loads a remote file as a Blob using Fetch API.
|
|
302
266
|
*
|
|
303
|
-
* @param {string} url - The full URL to fetch the file from.
|
|
304
|
-
* @param {FetchTemplateOptions} [options] - Optional fetch options.
|
|
267
|
+
* @param {string | URL | Request} url - The full URL to fetch the file from.
|
|
305
268
|
* @param {string[]} [allowedMimeTypes] - Optional list of accepted MIME types (e.g., ['image/jpeg']).
|
|
269
|
+
* @param {RequestInit|null} [options] - Optional fetch options.
|
|
270
|
+
* @param {FetchOnProgressResult} [onProgress] - Track the load progress.
|
|
306
271
|
* @returns {Promise<Blob>} - The fetched file as a Blob.
|
|
307
272
|
* @throws {Error} Throws if fetch fails, response is not ok, or MIME type is not allowed.
|
|
308
273
|
*/
|
|
309
|
-
async function fetchBlob(url, allowedMimeTypes, options) {
|
|
274
|
+
async function fetchBlob(url, allowedMimeTypes, options, onProgress) {
|
|
275
|
+
if (typeof url !== 'string') {
|
|
276
|
+
return Promise.reject(new TypeError('The "url" argument must be a string.'));
|
|
277
|
+
}
|
|
278
|
+
if (allowedMimeTypes !== undefined && !Array.isArray(allowedMimeTypes)) {
|
|
279
|
+
return Promise.reject(
|
|
280
|
+
new TypeError('The "allowedMimeTypes" argument must be an array of strings.'),
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
284
|
+
return Promise.reject(new TypeError('The "options" argument must be an object.'));
|
|
285
|
+
}
|
|
286
|
+
if (onProgress !== undefined && typeof onProgress !== 'function') {
|
|
287
|
+
return Promise.reject(new TypeError('The "onProgress" option must be a function.'));
|
|
288
|
+
}
|
|
289
|
+
|
|
310
290
|
return new Promise((resolve, reject) => {
|
|
311
|
-
|
|
312
|
-
.then(async (
|
|
291
|
+
fetch(url, options ?? undefined)
|
|
292
|
+
.then(async (response) => {
|
|
293
|
+
const res = onProgress ? trackFetchProgress(response, onProgress) : response;
|
|
313
294
|
const contentType = res.headers.get('content-type') || '';
|
|
314
295
|
|
|
315
296
|
if (
|
|
@@ -329,17 +310,33 @@ async function fetchBlob(url, allowedMimeTypes, options) {
|
|
|
329
310
|
|
|
330
311
|
/**
|
|
331
312
|
* Loads a remote file as a text using Fetch API.
|
|
332
|
-
*
|
|
333
|
-
* @param {string} url - The full URL to fetch the file from.
|
|
334
|
-
* @param {FetchTemplateOptions} [options] - Optional fetch options.
|
|
313
|
+
* @param {string | URL | Request} url - The full URL to fetch the file from.
|
|
335
314
|
* @param {string[]} [allowedMimeTypes] - Optional list of accepted MIME types (e.g., ['image/jpeg']).
|
|
315
|
+
* @param {RequestInit|null} [options] - Optional fetch options.
|
|
316
|
+
* @param {FetchOnProgressResult} [onProgress] - Track the load progress.
|
|
336
317
|
* @returns {Promise<string>} - The fetched file as a text.
|
|
337
318
|
* @throws {Error} Throws if fetch fails, response is not ok, or MIME type is not allowed.
|
|
338
319
|
*/
|
|
339
|
-
async function fetchText(url, allowedMimeTypes, options) {
|
|
320
|
+
async function fetchText(url, allowedMimeTypes, options, onProgress) {
|
|
321
|
+
if (typeof url !== 'string') {
|
|
322
|
+
return Promise.reject(new TypeError('The "url" argument must be a string.'));
|
|
323
|
+
}
|
|
324
|
+
if (allowedMimeTypes !== undefined && !Array.isArray(allowedMimeTypes)) {
|
|
325
|
+
return Promise.reject(
|
|
326
|
+
new TypeError('The "allowedMimeTypes" argument must be an array of strings.'),
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
330
|
+
return Promise.reject(new TypeError('The "options" argument must be an object.'));
|
|
331
|
+
}
|
|
332
|
+
if (onProgress !== undefined && typeof onProgress !== 'function') {
|
|
333
|
+
return Promise.reject(new TypeError('The "onProgress" option must be a function.'));
|
|
334
|
+
}
|
|
335
|
+
|
|
340
336
|
return new Promise((resolve, reject) => {
|
|
341
|
-
|
|
342
|
-
.then(async (
|
|
337
|
+
fetch(url, options ?? undefined)
|
|
338
|
+
.then(async (response) => {
|
|
339
|
+
const res = onProgress ? trackFetchProgress(response, onProgress) : response;
|
|
343
340
|
const contentType = res.headers.get('content-type') || '';
|
|
344
341
|
|
|
345
342
|
if (
|
|
@@ -389,7 +386,23 @@ async function fetchText(url, allowedMimeTypes, options) {
|
|
|
389
386
|
* @param {(event: Event, startTime: number) => void} [options.onLoading]
|
|
390
387
|
* @returns {Promise<ImageLoadResult>}
|
|
391
388
|
*/
|
|
392
|
-
async function loadImage(
|
|
389
|
+
async function loadImage(options) {
|
|
390
|
+
if (options === null || typeof options !== 'object') {
|
|
391
|
+
return Promise.reject(new TypeError('The "options" argument must be an object.'));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const { url, onLoading, crossOrigin = 'anonymous' } = options;
|
|
395
|
+
|
|
396
|
+
if (typeof url !== 'string') {
|
|
397
|
+
return Promise.reject(new TypeError('The "url" option must be a string.'));
|
|
398
|
+
}
|
|
399
|
+
if (typeof crossOrigin !== 'string') {
|
|
400
|
+
return Promise.reject(new TypeError('The "crossOrigin" option must be a string.'));
|
|
401
|
+
}
|
|
402
|
+
if (onLoading !== undefined && typeof onLoading !== 'function') {
|
|
403
|
+
return Promise.reject(new TypeError('The "onLoading" option must be a function.'));
|
|
404
|
+
}
|
|
405
|
+
|
|
393
406
|
return new Promise((resolve, reject) => {
|
|
394
407
|
const img = new Image();
|
|
395
408
|
let startTime = performance.now();
|
|
@@ -465,13 +478,19 @@ async function loadImage({ url, onLoading, crossOrigin = 'anonymous' }) {
|
|
|
465
478
|
* @returns {() => void} Function that removes all installed event listeners.
|
|
466
479
|
* @throws {TypeError} If any provided setting is invalid.
|
|
467
480
|
*/
|
|
468
|
-
function installWindowHiddenScript({
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
481
|
+
function installWindowHiddenScript(settings = {}) {
|
|
482
|
+
if (settings === null || typeof settings !== 'object') {
|
|
483
|
+
throw new TypeError('The "settings" argument must be an object.');
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const {
|
|
487
|
+
element = document.body,
|
|
488
|
+
hiddenClass = 'windowHidden',
|
|
489
|
+
visibleClass = 'windowVisible',
|
|
490
|
+
onVisible,
|
|
491
|
+
onHidden,
|
|
492
|
+
} = settings;
|
|
493
|
+
|
|
475
494
|
if (!(element instanceof Element))
|
|
476
495
|
throw new TypeError(`"element" must be an instance of Element.`);
|
|
477
496
|
if (typeof hiddenClass !== 'string') throw new TypeError(`"hiddenClass" must be a string.`);
|