@arex95/vue-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -0
  3. package/dist/composables/axios/axiosFetch.d.ts +10 -0
  4. package/dist/composables/axios/createFetch.d.ts +10 -0
  5. package/dist/composables/axios/index.d.ts +3 -0
  6. package/dist/composables/axios/useVueQuery.d.ts +192 -0
  7. package/dist/composables/breakpoints/index.d.ts +1 -0
  8. package/dist/composables/breakpoints/useBreakpoint.d.ts +62 -0
  9. package/dist/composables/filters/index.d.ts +1 -0
  10. package/dist/composables/filters/useFilter.d.ts +16 -0
  11. package/dist/composables/index.d.ts +5 -0
  12. package/dist/composables/paginators/index.d.ts +1 -0
  13. package/dist/composables/paginators/usePaginator.d.ts +14 -0
  14. package/dist/composables/sorters/index.d.ts +1 -0
  15. package/dist/composables/sorters/useSorter.d.ts +14 -0
  16. package/dist/config/axios/axiosConfig.d.ts +47 -0
  17. package/dist/config/axios/index.d.ts +2 -0
  18. package/dist/config/index.d.ts +1 -0
  19. package/dist/constants/breakpointEnum.d.ts +31 -0
  20. package/dist/constants/exceptionEnum.d.ts +30 -0
  21. package/dist/constants/fileTypesEnum.d.ts +25 -0
  22. package/dist/constants/httpEnum.d.ts +21 -0
  23. package/dist/constants/index.d.ts +6 -0
  24. package/dist/constants/keyCodeEnum.d.ts +10 -0
  25. package/dist/constants/storageEnum.d.ts +50 -0
  26. package/dist/index.d.ts +6 -0
  27. package/dist/rest/RestStd.d.ts +70 -0
  28. package/dist/rest/index.d.ts +1 -0
  29. package/dist/types/AxiosOptionsParameter.d.ts +31 -0
  30. package/dist/types/ExtendedQueryOptions.d.ts +6 -0
  31. package/dist/types/index.d.ts +2 -0
  32. package/dist/utils/browser.d.ts +30 -0
  33. package/dist/utils/dates.d.ts +71 -0
  34. package/dist/utils/debounces.d.ts +57 -0
  35. package/dist/utils/exports.d.ts +34 -0
  36. package/dist/utils/files.d.ts +46 -0
  37. package/dist/utils/index.d.ts +9 -0
  38. package/dist/utils/io.d.ts +168 -0
  39. package/dist/utils/objects.d.ts +109 -0
  40. package/dist/utils/strings.d.ts +63 -0
  41. package/dist/utils/validations.d.ts +114 -0
  42. package/dist/vue-core.cjs.js +2118 -0
  43. package/dist/vue-core.esm.js +1993 -0
  44. package/package.json +45 -0
@@ -0,0 +1,1993 @@
1
+ import axios from 'axios';
2
+ import '@tanstack/vue-query';
3
+ import { computed } from 'vue';
4
+ import { useTimeoutFn } from '@vueuse/core';
5
+
6
+ /**
7
+ * AxiosService class encapsulates the Axios configuration and logic.
8
+ * It manages request and response interceptors and provides methods for making HTTP requests.
9
+ */
10
+ class AxiosService {
11
+ instance;
12
+ cancelTokenSource;
13
+ activeRequests = 0; // Track active requests
14
+ /**
15
+ * Initializes the AxiosService instance by creating an Axios instance with default configuration
16
+ * and setting up request and response interceptors.
17
+ */
18
+ constructor(url) {
19
+ this.cancelTokenSource = axios.CancelToken.source();
20
+ this.instance = axios.create({
21
+ baseURL: url ?? '',
22
+ timeout: 300000,
23
+ headers: {
24
+ Accept: 'application/json',
25
+ 'Content-Type': 'application/json',
26
+ },
27
+ withCredentials: false,
28
+ });
29
+ this.initializeInterceptors();
30
+ }
31
+ /**
32
+ * Initializes request and response interceptors for the Axios instance.
33
+ */
34
+ initializeInterceptors() {
35
+ this.instance.interceptors.request.use((config) => {
36
+ const token = typeof window !== 'undefined' ? localStorage.getItem('authToken') : null;
37
+ if (token && config.headers) {
38
+ config.headers.Authorization = `Bearer ${token}`;
39
+ }
40
+ config.cancelToken = this.cancelTokenSource.token;
41
+ this.activeRequests++;
42
+ return config;
43
+ }, (error) => {
44
+ // handleError(error);
45
+ console.error('Request error:', error.message);
46
+ this.activeRequests++;
47
+ return Promise.reject(error);
48
+ });
49
+ this.instance.interceptors.response.use((response) => {
50
+ this.activeRequests--;
51
+ return response;
52
+ }, (error) => {
53
+ if (axios.isCancel(error)) {
54
+ console.warn('Request canceled:', error.message);
55
+ }
56
+ else if (axios.isAxiosError(error)) {
57
+ // handleError(error);
58
+ console.error('Response error:', error.response?.status, error.message);
59
+ if (error.response?.status === 401) {
60
+ this.handleUnauthorized();
61
+ }
62
+ }
63
+ else {
64
+ console.error('Unexpected error:', error);
65
+ }
66
+ this.activeRequests--;
67
+ return Promise.reject(error);
68
+ });
69
+ }
70
+ /**
71
+ * Returns the number of active requests.
72
+ */
73
+ getActiveRequests() {
74
+ return this.activeRequests;
75
+ }
76
+ /**
77
+ * Handles unauthorized access errors (401).
78
+ */
79
+ handleUnauthorized() {
80
+ console.warn('Unauthorized access - redirecting to login.');
81
+ }
82
+ /**
83
+ * Returns the Axios instance with the configured settings and interceptors.
84
+ * @returns {AxiosInstance} The configured Axios instance.
85
+ */
86
+ getAxiosInstance() {
87
+ return this.instance;
88
+ }
89
+ /**
90
+ * Cancels all ongoing requests.
91
+ */
92
+ cancelAllRequests() {
93
+ this.cancelTokenSource.cancel('Operation canceled by the user.');
94
+ this.cancelTokenSource = axios.CancelToken.source();
95
+ }
96
+ /**
97
+ * Sets a new header for the Axios instance.
98
+ * @param {string} key - The header key.
99
+ * @param {string} value - The header value.
100
+ */
101
+ setHeader(key, value) {
102
+ this.instance.defaults.headers.common[key] = value;
103
+ }
104
+ /**
105
+ * Removes a header from the Axios instance.
106
+ * @param {string} key - The header key to remove.
107
+ */
108
+ removeHeader(key) {
109
+ delete this.instance.defaults.headers.common[key];
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Custom composable for sorting products based on a selected criterion.
115
+ * @param {Array} items - The array of products to sort.
116
+ * @param {Array} criteriaList - The list of available sorting criteria.
117
+ * @param {Number} selectedCriteria - The currently selected sorting criterion.
118
+ * @returns {ComputedRef<Array>} - The sorted array of products.
119
+ */
120
+ function useSorter(items, criteriaList, selectedCriteria) {
121
+ return computed(() => {
122
+ const criteria = criteriaList.find(item => item.value === Number(selectedCriteria));
123
+ if (!criteria)
124
+ return items;
125
+ return [...items].sort((a, b) => {
126
+ const aValue = a[criteria.field];
127
+ const bValue = b[criteria.field];
128
+ if (criteria.type === 'number') {
129
+ const numA = typeof aValue === 'number' ? aValue : parseFloat(aValue) || 0;
130
+ const numB = typeof bValue === 'number' ? bValue : parseFloat(bValue) || 0;
131
+ return criteria.order === 'asc' ? numA - numB : numB - numA;
132
+ }
133
+ else if (criteria.type === 'date') {
134
+ const dateA = new Date(aValue).getTime();
135
+ const dateB = new Date(bValue).getTime();
136
+ return criteria.order === 'asc' ? dateA - dateB : dateB - dateA;
137
+ }
138
+ else if (criteria.type === 'boolean') {
139
+ const boolA = !!aValue;
140
+ const boolB = !!bValue;
141
+ return criteria.order === 'asc' ? (boolA === boolB ? 0 : boolA ? 1 : -1) : (boolA === boolB ? 0 : boolA ? -1 : 1);
142
+ }
143
+ else {
144
+ // Fallback for string sorting
145
+ const strA = (aValue || '').toString().toLowerCase();
146
+ const strB = (bValue || '').toString().toLowerCase();
147
+ return criteria.order === 'asc' ? strA.localeCompare(strB) : strB.localeCompare(strA);
148
+ }
149
+ });
150
+ }).value;
151
+ }
152
+
153
+ /**
154
+ * Object defining available screen sizes as constants.
155
+ * @readonly
156
+ */
157
+ const sizeEnum = {
158
+ XS: 'XS',
159
+ SM: 'SM',
160
+ MD: 'MD',
161
+ LG: 'LG',
162
+ XL: 'XL',
163
+ XXL: 'XXL',
164
+ };
165
+ /**
166
+ * Object defining breakpoints for design based on screen width.
167
+ * Values are in pixels.
168
+ * @readonly
169
+ */
170
+ const screenEnum = {
171
+ XS: 480,
172
+ SM: 576,
173
+ MD: 768,
174
+ LG: 992,
175
+ XL: 1200,
176
+ XXL: 1600,
177
+ };
178
+ /**
179
+ * Map that associates each screen size defined in `sizeEnum` with its corresponding pixel value from `screenEnum`.
180
+ * @type {Map<typeof sizeEnum[keyof typeof sizeEnum], number>}
181
+ */
182
+ const screenMap = new Map();
183
+ screenMap.set(sizeEnum.XS, screenEnum.XS);
184
+ screenMap.set(sizeEnum.SM, screenEnum.SM);
185
+ screenMap.set(sizeEnum.MD, screenEnum.MD);
186
+ screenMap.set(sizeEnum.LG, screenEnum.LG);
187
+ screenMap.set(sizeEnum.XL, screenEnum.XL);
188
+ screenMap.set(sizeEnum.XXL, screenEnum.XXL);
189
+
190
+ /**
191
+ * Object defining various exception codes.
192
+ * @readonly
193
+ */
194
+ const ExceptionEnum = {
195
+ /**
196
+ * HTTP status code for page access forbidden.
197
+ */
198
+ PAGE_NOT_ACCESS: 403,
199
+ /**
200
+ * HTTP status code for page not found.
201
+ */
202
+ PAGE_NOT_FOUND: 404,
203
+ /**
204
+ * HTTP status code for general server error.
205
+ */
206
+ ERROR: 500,
207
+ /**
208
+ * HTTP status code for bad request.
209
+ */
210
+ BAD_REQUEST: 406,
211
+ /**
212
+ * Custom error code for network errors.
213
+ */
214
+ NET_WORK_ERROR: 10000,
215
+ /**
216
+ * Custom code indicating no data on the page, not actually an exception page.
217
+ */
218
+ PAGE_NOT_DATA: 10100,
219
+ };
220
+
221
+ /**
222
+ * Object defining various image MIME types.
223
+ * @readonly
224
+ */
225
+ const ImageTypes = {
226
+ apng: 'image/apng',
227
+ bmp: 'image/bmp',
228
+ gif: 'image/gif',
229
+ jpeg: 'image/jpeg',
230
+ pjpeg: 'image/pjpeg',
231
+ png: 'image/png',
232
+ svg: 'image/svg+xml',
233
+ tiff: 'image/tiff',
234
+ webp: 'image/webp',
235
+ xicon: 'image/x-icon',
236
+ };
237
+ /**
238
+ * Object defining various file meta types.
239
+ * @readonly
240
+ */
241
+ const FileMetaTypes = {
242
+ images: 'image/*',
243
+ audios: 'audio/*',
244
+ videos: 'video/*',
245
+ };
246
+
247
+ /**
248
+ * Object representing different content types for HTTP headers.
249
+ * @readonly
250
+ */
251
+ const ContentTypeEnum = {
252
+ /**
253
+ * Content type for JSON.
254
+ * @constant
255
+ */
256
+ JSON: 'application/json;charset=UTF-8',
257
+ /**
258
+ * Content type for URL-encoded form data.
259
+ * @constant
260
+ */
261
+ FORM_URLENCODED: 'application/x-www-form-urlencoded;charset=UTF-8',
262
+ /**
263
+ * Content type for multipart form data with file uploads.
264
+ * @constant
265
+ */
266
+ FORM_DATA: 'multipart/form-data;charset=UTF-8',
267
+ };
268
+
269
+ /**
270
+ * Object defining various key codes for keyboard events.
271
+ * @readonly
272
+ */
273
+ const KeyCodeEnum = {
274
+ UP: 38,
275
+ DOWN: 40,
276
+ ENTER: 13,
277
+ ESC: 27,
278
+ };
279
+
280
+ /**
281
+ * Key used for storing the authentication token.
282
+ * @constant {string}
283
+ */
284
+ const TOKEN_KEY = 'TOKEN__';
285
+ /**
286
+ * Key used for storing the locale information.
287
+ * @constant {string}
288
+ */
289
+ const LOCALE_KEY = 'LOCALE__';
290
+ /**
291
+ * Key used for storing user information.
292
+ * @constant {string}
293
+ */
294
+ const USER_INFO_KEY = 'USER__INFO__';
295
+ /**
296
+ * Key used for storing role information.
297
+ * @constant {string}
298
+ */
299
+ const ROLES_KEY = 'ROLES__KEY__';
300
+ /**
301
+ * Key used for storing project configuration.
302
+ * @constant {string}
303
+ */
304
+ const PROJ_CFG_KEY = 'PROJ__CFG__KEY__';
305
+ /**
306
+ * Key used for storing lock information.
307
+ * @constant {string}
308
+ */
309
+ const LOCK_INFO_KEY = 'LOCK__INFO__KEY__';
310
+ /**
311
+ * Key used for base global local cache.
312
+ * @constant {string}
313
+ */
314
+ const APP_LOCAL_CACHE_KEY = 'COMMON__LOCAL__KEY';
315
+ /**
316
+ * Key used for base global session cache.
317
+ * @constant {string}
318
+ */
319
+ const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY';
320
+ /**
321
+ * Object defining types of cache storage.
322
+ * @readonly
323
+ */
324
+ const CacheTypeEnum = {
325
+ /** Represents session storage */
326
+ SESSION: 0,
327
+ /** Represents local storage */
328
+ LOCAL: 1,
329
+ };
330
+
331
+ /**
332
+ * Creates and downloads a file from Blob data.
333
+ * @param {Blob} blob The Blob containing the file data.
334
+ * @param {string} fileName The name of the file to create.
335
+ */
336
+ function downloadFile(blob, fileName) {
337
+ const link = document.createElement('a');
338
+ const url = URL.createObjectURL(blob);
339
+ link.setAttribute('href', url);
340
+ link.setAttribute('download', fileName);
341
+ // Append link to the body and trigger a click
342
+ document.body.appendChild(link);
343
+ link.click();
344
+ // Clean up
345
+ document.body.removeChild(link);
346
+ URL.revokeObjectURL(url);
347
+ }
348
+ /**
349
+ * Exports data to a CSV file.
350
+ * @param {string[]} headers The headers for the CSV.
351
+ * @param {any[][]} data The data to export, as an array of arrays.
352
+ * @param {string} fileName The name of the file to create.
353
+ */
354
+ function exportToCSV(headers, data, fileName) {
355
+ const csvRows = [];
356
+ // Add the headers
357
+ csvRows.push(headers.map(header => `"${header.replace(/"/g, '""')}"`).join(','));
358
+ // Add the data rows
359
+ for (const row of data) {
360
+ csvRows.push(row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(','));
361
+ }
362
+ // Create a Blob with CSV data
363
+ const csvContent = csvRows.join('\n');
364
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
365
+ // Use the download utility function
366
+ downloadFile(blob, fileName);
367
+ }
368
+ /**
369
+ * Exports data to an Excel file (.xls) using HTML table.
370
+ * @param {string[]} headers The headers for the Excel file.
371
+ * @param {any[][]} data The data to export, as an array of arrays.
372
+ * @param {string} fileName The name of the file to create.
373
+ */
374
+ function exportToExcel(headers, data, fileName) {
375
+ // Create a table element
376
+ const table = document.createElement('table');
377
+ const thead = table.createTHead();
378
+ const tbody = table.createTBody();
379
+ // Create the header row
380
+ const headerRow = thead.insertRow();
381
+ headers.forEach(header => {
382
+ const th = document.createElement('th');
383
+ th.textContent = header;
384
+ headerRow.appendChild(th);
385
+ });
386
+ // Create the data rows
387
+ data.forEach(row => {
388
+ const tr = tbody.insertRow();
389
+ row.forEach(cell => {
390
+ const td = tr.insertCell();
391
+ td.textContent = String(cell);
392
+ });
393
+ });
394
+ // Create a Blob with the table HTML
395
+ const html = table.outerHTML;
396
+ const blob = new Blob([html], { type: 'application/vnd.ms-excel;charset=utf-8;' });
397
+ // Use the download utility function
398
+ downloadFile(blob, fileName);
399
+ }
400
+ /**
401
+ * Exports data to a JSON file.
402
+ * @param {any[]} data The data to export.
403
+ * @param {string} fileName The name of the file to create.
404
+ */
405
+ function exportToJSON(data, fileName) {
406
+ const jsonContent = JSON.stringify(data, null, 2); // Pretty print with 2 spaces
407
+ const blob = new Blob([jsonContent], { type: 'application/json;charset=utf-8;' });
408
+ // Use the download utility function
409
+ downloadFile(blob, fileName);
410
+ }
411
+ /**
412
+ * Exports data to an XML file.
413
+ * @param {string[]} headers The headers for the XML.
414
+ * @param {any[][]} data The data to export, as an array of arrays.
415
+ * @param {string} fileName The name of the file to create.
416
+ */
417
+ function exportToXML(headers, data, fileName) {
418
+ let xmlContent = '<?xml version="1.0" encoding="UTF-8"?>\n<rows>\n';
419
+ // Add headers
420
+ xmlContent += ' <header>\n';
421
+ headers.forEach(header => {
422
+ xmlContent += ` <column>${header}</column>\n`;
423
+ });
424
+ xmlContent += ' </header>\n';
425
+ // Add data rows
426
+ data.forEach(row => {
427
+ xmlContent += ' <row>\n';
428
+ row.forEach(cell => {
429
+ xmlContent += ` <cell>${cell}</cell>\n`;
430
+ });
431
+ xmlContent += ' </row>\n';
432
+ });
433
+ xmlContent += '</rows>';
434
+ const blob = new Blob([xmlContent], { type: 'application/xml;charset=utf-8;' });
435
+ // Use the download utility function
436
+ downloadFile(blob, fileName);
437
+ }
438
+ /**
439
+ * Exports data to a plain text file.
440
+ * @param {string[]} headers The headers for the text file.
441
+ * @param {any[][]} data The data to export, as an array of arrays.
442
+ * @param {string} fileName The name of the file to create.
443
+ */
444
+ function exportToText(headers, data, fileName) {
445
+ const textRows = [];
446
+ // Add headers
447
+ textRows.push(headers.join('\t'));
448
+ // Add data rows
449
+ data.forEach(row => {
450
+ textRows.push(row.join('\t'));
451
+ });
452
+ // Create a Blob with text data
453
+ const textContent = textRows.join('\n');
454
+ const blob = new Blob([textContent], { type: 'text/plain;charset=utf-8;' });
455
+ // Use the download utility function
456
+ downloadFile(blob, fileName);
457
+ }
458
+
459
+ /**
460
+ * Opens a new window with the specified URL and options.
461
+ * @param {string} url The URL to open.
462
+ * @param {Object} [opt] Options for the new window.
463
+ * @param {string} [opt.target='__blank'] The target window name.
464
+ * @param {boolean} [opt.noopener=true] Whether to add 'noopener' attribute.
465
+ * @param {boolean} [opt.noreferrer=true] Whether to add 'noreferrer' attribute.
466
+ */
467
+ function openWindow(url, opt) {
468
+ const { target = '__blank', noopener = true, noreferrer = true } = opt || {};
469
+ const features = [];
470
+ // Append noopener and noreferrer attributes if enabled
471
+ if (noopener)
472
+ features.push('noopener=yes');
473
+ if (noreferrer)
474
+ features.push('noreferrer=yes');
475
+ // Open the window with the specified features
476
+ window.open(url, target, features.join(','));
477
+ }
478
+ /**
479
+ * Copies text to the clipboard.
480
+ * @param {string} text The text to copy.
481
+ * @returns {Promise<void>} A promise that resolves when the text has been copied.
482
+ */
483
+ async function copyToClipboard(text) {
484
+ if (navigator.clipboard) {
485
+ await navigator.clipboard.writeText(text);
486
+ }
487
+ else {
488
+ // Fallback for browsers that do not support the Clipboard API
489
+ const textArea = document.createElement('textarea');
490
+ textArea.value = text;
491
+ document.body.appendChild(textArea);
492
+ textArea.select();
493
+ document.execCommand('copy');
494
+ document.body.removeChild(textArea);
495
+ }
496
+ }
497
+ /**
498
+ * Scrolls the window to the top smoothly.
499
+ * @param {number} [duration=300] Duration of the scroll animation in milliseconds.
500
+ */
501
+ function scrollToTop(duration = 300) {
502
+ const start = window.scrollY;
503
+ const startTime = performance.now();
504
+ function scroll() {
505
+ const elapsed = performance.now() - startTime;
506
+ const progress = Math.min(elapsed / duration, 1);
507
+ window.scrollTo(0, start * (1 - progress));
508
+ if (progress < 1) {
509
+ requestAnimationFrame(scroll);
510
+ }
511
+ }
512
+ requestAnimationFrame(scroll);
513
+ }
514
+ /**
515
+ * Gets the value of a query parameter from the URL.
516
+ * @param {string} paramName The name of the query parameter.
517
+ * @returns {string | null} The value of the query parameter, or null if it does not exist.
518
+ */
519
+ function getQueryParam(paramName) {
520
+ const urlParams = new URLSearchParams(window.location.search);
521
+ return urlParams.get(paramName);
522
+ }
523
+
524
+ /**
525
+ * Disables the right-click context menu on the window.
526
+ */
527
+ function disableRightClick() {
528
+ const handler = (event) => event.preventDefault();
529
+ window.addEventListener('contextmenu', handler);
530
+ // Store handler to allow removal
531
+ disableRightClick.handler = handler;
532
+ }
533
+ disableRightClick.handler = null;
534
+ /**
535
+ * Enables the right-click context menu on the window.
536
+ */
537
+ function enableRightClick() {
538
+ if (disableRightClick.handler) {
539
+ window.removeEventListener('contextmenu', disableRightClick.handler);
540
+ }
541
+ }
542
+ /**
543
+ * Disables specific mouse buttons.
544
+ * @param {Array<number>} buttons Array of mouse button codes to disable (0 for left, 1 for middle, 2 for right).
545
+ */
546
+ function disableMouseButtons(buttons) {
547
+ const handler = (event) => {
548
+ if (buttons.includes(event.button)) {
549
+ event.preventDefault();
550
+ }
551
+ };
552
+ window.addEventListener('mousedown', handler);
553
+ window.addEventListener('contextmenu', handler); // Also handles right-click context menu
554
+ // Store handler to allow removal later
555
+ disableMouseButtons.handlers.push(handler);
556
+ }
557
+ disableMouseButtons.handlers = [];
558
+ /**
559
+ * Enables all previously disabled mouse buttons.
560
+ */
561
+ function enableMouseButtons() {
562
+ for (const handler of disableMouseButtons.handlers) {
563
+ window.removeEventListener('mousedown', handler);
564
+ window.removeEventListener('contextmenu', handler);
565
+ }
566
+ disableMouseButtons.handlers = [];
567
+ }
568
+ /**
569
+ * Adds a double-click event listener to a specific element.
570
+ * @param {HTMLElement} element The target element.
571
+ * @param {(event: MouseEvent) => void} callback The callback function to execute on double click.
572
+ */
573
+ function addDoubleClickListener(element, callback) {
574
+ element.addEventListener('dblclick', callback);
575
+ }
576
+ /**
577
+ * Removes a double-click event listener from a specific element.
578
+ * @param {HTMLElement} element The target element.
579
+ * @param {(event: MouseEvent) => void} callback The callback function to remove.
580
+ */
581
+ function removeDoubleClickListener(element, callback) {
582
+ element.removeEventListener('dblclick', callback);
583
+ }
584
+ // Example usage:
585
+ // addDoubleClickListener(document.body, () => alert('Double clicked!'));
586
+ /**
587
+ * Detects a click outside a specific element and triggers a callback.
588
+ * @param {HTMLElement} element The element to detect clicks outside of.
589
+ * @param {() => void} callback The callback function to execute when a click outside is detected.
590
+ */
591
+ function clickOutside(element, callback) {
592
+ const handler = (event) => {
593
+ if (!element.contains(event.target)) {
594
+ callback();
595
+ }
596
+ };
597
+ document.addEventListener('click', handler);
598
+ // Store handler to allow removal later
599
+ clickOutside.handlers.push({ element, handler });
600
+ }
601
+ clickOutside.handlers = [];
602
+ /**
603
+ * Removes the click outside listener for a specific element.
604
+ * @param {HTMLElement} element The element to stop detecting clicks outside of.
605
+ */
606
+ function removeClickOutside(element) {
607
+ const index = clickOutside.handlers.findIndex(h => h.element === element);
608
+ if (index !== -1) {
609
+ document.removeEventListener('click', clickOutside.handlers[index].handler);
610
+ clickOutside.handlers.splice(index, 1);
611
+ }
612
+ }
613
+ // Example usage: Close a menu when clicking outside of it
614
+ // const menu = document.getElementById('menu');
615
+ // if (menu) {
616
+ // clickOutside(menu, () => menu.style.display = 'none');
617
+ // }
618
+ /**
619
+ * Disables the F12 key and certain key combinations for developer tools.
620
+ */
621
+ function disableF12Key() {
622
+ const handler = function (event) {
623
+ return true;
624
+ };
625
+ document.addEventListener('keydown', handler);
626
+ }
627
+ /**
628
+ * Enables or disables the tab navigation (Tab key) on the page.
629
+ * @param {boolean} enable Whether to enable or disable tab navigation.
630
+ */
631
+ function toggleTabNavigation(enable) {
632
+ if (enable) {
633
+ document.onkeydown = null; // Removes any previously set handler
634
+ }
635
+ else {
636
+ document.onkeydown = (event) => {
637
+ if (event.key === 'Tab') {
638
+ event.preventDefault();
639
+ return false;
640
+ }
641
+ return true;
642
+ };
643
+ }
644
+ }
645
+ /**
646
+ * Disables the copy (Ctrl + C) functionality on the page.
647
+ */
648
+ function disableCopy() {
649
+ document.addEventListener('copy', (event) => {
650
+ event.preventDefault();
651
+ alert('Copying text is disabled on this page.');
652
+ });
653
+ }
654
+ /**
655
+ * Adds a custom keyboard shortcut to execute a given callback function.
656
+ * @param {string} key The key to trigger the callback.
657
+ * @param {Function} callback The function to execute on the key press.
658
+ * @param {boolean} [ctrlKey=false] Whether Ctrl key should be pressed.
659
+ * @param {boolean} [shiftKey=false] Whether Shift key should be pressed.
660
+ */
661
+ function addCustomKeyboardShortcut(key, callback, ctrlKey = false, shiftKey = false) {
662
+ document.addEventListener('keydown', (event) => {
663
+ if (event.key === key &&
664
+ event.ctrlKey === ctrlKey &&
665
+ event.shiftKey === shiftKey) {
666
+ event.preventDefault();
667
+ callback();
668
+ }
669
+ });
670
+ }
671
+ /**
672
+ * Removes a custom keyboard shortcut by key and modifiers.
673
+ * @param {string} key The key to trigger the callback.
674
+ * @param {boolean} [ctrlKey=false] Whether Ctrl key should be pressed.
675
+ * @param {boolean} [shiftKey=false] Whether Shift key should be pressed.
676
+ */
677
+ function removeCustomKeyboardShortcut(key, ctrlKey = false, shiftKey = false) {
678
+ const handler = (event) => {
679
+ if (event.key === key &&
680
+ event.ctrlKey === ctrlKey &&
681
+ event.shiftKey === shiftKey) {
682
+ event.preventDefault();
683
+ }
684
+ };
685
+ document.removeEventListener('keydown', handler);
686
+ }
687
+ /**
688
+ * Disables specific keys or key combinations.
689
+ * @param {Array<string>} keys Array of key names to disable (e.g., ['F1', 'F5', 'Control+S']).
690
+ */
691
+ function disableSpecificKeys(keys) {
692
+ const handler = (event) => {
693
+ const keyCombination = `${event.ctrlKey ? 'Control+' : ''}${event.shiftKey ? 'Shift+' : ''}${event.altKey ? 'Alt+' : ''}${event.key}`;
694
+ if (keys.includes(event.key) || keys.includes(keyCombination)) {
695
+ event.preventDefault();
696
+ }
697
+ };
698
+ document.addEventListener('keydown', handler);
699
+ // Storing the handler to allow removal later
700
+ disableSpecificKeys.handlers.push(handler);
701
+ }
702
+ disableSpecificKeys.handlers = [];
703
+ /**
704
+ * Enables keys that were previously disabled using disableSpecificKeys.
705
+ */
706
+ function enableSpecificKeys() {
707
+ for (const handler of disableSpecificKeys.handlers) {
708
+ document.removeEventListener('keydown', handler);
709
+ }
710
+ disableSpecificKeys.handlers = [];
711
+ }
712
+ /**
713
+ * Registers multiple keyboard shortcuts with their respective callback functions.
714
+ * @param {Array<{ key: string, ctrlKey?: boolean, shiftKey?: boolean, altKey?: boolean, callback: Function }>} shortcuts Array of shortcut objects.
715
+ */
716
+ function registerKeyboardShortcuts(shortcuts) {
717
+ const handler = (event) => {
718
+ for (const shortcut of shortcuts) {
719
+ if (event.key === shortcut.key &&
720
+ (shortcut.ctrlKey ? event.ctrlKey : true) &&
721
+ (shortcut.shiftKey ? event.shiftKey : true) &&
722
+ (shortcut.altKey ? event.altKey : true)) {
723
+ event.preventDefault();
724
+ shortcut.callback();
725
+ }
726
+ }
727
+ };
728
+ document.addEventListener('keydown', handler);
729
+ // Storing the handler to allow removal later
730
+ registerKeyboardShortcuts.handlers.push(handler);
731
+ }
732
+ registerKeyboardShortcuts.handlers = [];
733
+ /**
734
+ * Unregisters all keyboard shortcuts that were registered with registerKeyboardShortcuts.
735
+ */
736
+ function unregisterKeyboardShortcuts() {
737
+ for (const handler of registerKeyboardShortcuts.handlers) {
738
+ document.removeEventListener('keydown', handler);
739
+ }
740
+ registerKeyboardShortcuts.handlers = [];
741
+ }
742
+ /**
743
+ * Adds a listener for a specific key to trigger a custom event.
744
+ * @param {string} key The key to listen for (e.g., 'Enter', 'Escape').
745
+ * @param {Function} callback The function to execute when the key is pressed.
746
+ */
747
+ function addKeyListener(key, callback) {
748
+ const handler = (event) => {
749
+ if (event.key === key) {
750
+ event.preventDefault();
751
+ callback();
752
+ }
753
+ };
754
+ document.addEventListener('keydown', handler);
755
+ // Storing the handler to allow removal later
756
+ addKeyListener.handlers.push(handler);
757
+ }
758
+ addKeyListener.handlers = [];
759
+ /**
760
+ * Removes all custom key listeners added by addKeyListener.
761
+ */
762
+ function removeKeyListeners() {
763
+ for (const handler of addKeyListener.handlers) {
764
+ document.removeEventListener('keydown', handler);
765
+ }
766
+ addKeyListener.handlers = [];
767
+ }
768
+ /**
769
+ * Detects if a specific key is held down.
770
+ * @param {string} key The key to detect (e.g., 'Shift', 'Control', 'Alt', 'a').
771
+ * @param {Function} onHold Callback function to execute while the key is held down.
772
+ */
773
+ function detectKeyHold(key, onHold) {
774
+ const keyDownHandler = (event) => {
775
+ if (event.key === key) {
776
+ onHold();
777
+ }
778
+ };
779
+ document.addEventListener('keydown', keyDownHandler);
780
+ // Storing the handler to allow removal later
781
+ detectKeyHold.handlers.push(keyDownHandler);
782
+ }
783
+ detectKeyHold.handlers = [];
784
+ /**
785
+ * Stops detecting if a specific key is held down.
786
+ */
787
+ function stopDetectingKeyHold() {
788
+ for (const handler of detectKeyHold.handlers) {
789
+ document.removeEventListener('keydown', handler);
790
+ }
791
+ detectKeyHold.handlers = [];
792
+ }
793
+ /**
794
+ * Tracks currently pressed keys and provides a map of active keys.
795
+ * @returns {Set<string>} A set of currently pressed keys.
796
+ */
797
+ function createKeyMap() {
798
+ const pressedKeys = new Set();
799
+ const keyDownHandler = (event) => {
800
+ pressedKeys.add(event.key);
801
+ };
802
+ const keyUpHandler = (event) => {
803
+ pressedKeys.delete(event.key);
804
+ };
805
+ document.addEventListener('keydown', keyDownHandler);
806
+ document.addEventListener('keyup', keyUpHandler);
807
+ // Function to remove listeners
808
+ createKeyMap.clearListeners = () => {
809
+ document.removeEventListener('keydown', keyDownHandler);
810
+ document.removeEventListener('keyup', keyUpHandler);
811
+ };
812
+ return pressedKeys;
813
+ }
814
+ /**
815
+ * Clears listeners for the key map tracking.
816
+ */
817
+ createKeyMap.clearListeners = () => { };
818
+ /**
819
+ * Sets up custom keyboard shortcuts with flexible order.
820
+ * @param {Array<string>} keys The combination of keys for the shortcut.
821
+ * @param {Function} callback The callback function to execute when the combination is detected.
822
+ */
823
+ function customShortcut(keys, callback) {
824
+ const pressedKeys = new Set();
825
+ const keyDownHandler = (event) => {
826
+ pressedKeys.add(event.key);
827
+ if (keys.every(key => pressedKeys.has(key))) {
828
+ callback();
829
+ }
830
+ };
831
+ const keyUpHandler = (event) => {
832
+ pressedKeys.delete(event.key);
833
+ };
834
+ document.addEventListener('keydown', keyDownHandler);
835
+ document.addEventListener('keyup', keyUpHandler);
836
+ // Store the handlers for later removal
837
+ customShortcut.handlers.push({ keyDownHandler, keyUpHandler });
838
+ }
839
+ customShortcut.handlers = [];
840
+ /**
841
+ * Removes all custom keyboard shortcuts added by customShortcut.
842
+ */
843
+ function removeCustomShortcuts() {
844
+ for (const { keyDownHandler, keyUpHandler } of customShortcut.handlers) {
845
+ document.removeEventListener('keydown', keyDownHandler);
846
+ document.removeEventListener('keyup', keyUpHandler);
847
+ }
848
+ customShortcut.handlers = [];
849
+ }
850
+ /**
851
+ * Simulates a key press event.
852
+ * @param {string} key The key to simulate (e.g., 'Enter', 'a').
853
+ * @param {boolean} ctrlKey If true, include Ctrl key in the event.
854
+ * @param {boolean} shiftKey If true, include Shift key in the event.
855
+ * @param {boolean} altKey If true, include Alt key in the event.
856
+ */
857
+ function simulateKeyPress(key, ctrlKey = false, shiftKey = false, altKey = false) {
858
+ const event = new KeyboardEvent('keydown', {
859
+ key,
860
+ ctrlKey,
861
+ shiftKey,
862
+ altKey,
863
+ bubbles: true,
864
+ cancelable: true
865
+ });
866
+ document.dispatchEvent(event);
867
+ }
868
+
869
+ /**
870
+ * Parses a date string into a Date object.
871
+ * @param {string} dateString The date string in 'YYYY-MM-DD' format.
872
+ * @returns {Date | null} The parsed Date object or null if the format is invalid.
873
+ */
874
+ function parseDate(dateString) {
875
+ const parts = dateString.split('-');
876
+ if (parts.length === 3) {
877
+ const year = parseInt(parts[0], 10);
878
+ const month = parseInt(parts[1], 10) - 1; // Months are zero-based
879
+ const day = parseInt(parts[2], 10);
880
+ const date = new Date(year, month, day);
881
+ if (date.getFullYear() === year && date.getMonth() === month && date.getDate() === day) {
882
+ return date;
883
+ }
884
+ }
885
+ return null;
886
+ }
887
+ /**
888
+ * Formats a Date object into a string.
889
+ * @param {Date} date The date to format.
890
+ * @param {string} format The format string (e.g., 'YYYY-MM-DD').
891
+ * @returns {string} The formatted date string.
892
+ */
893
+ function formatDate(date, format) {
894
+ const map = {
895
+ 'YYYY': date.getFullYear(),
896
+ 'MM': String(date.getMonth() + 1).padStart(2, '0'),
897
+ 'DD': String(date.getDate()).padStart(2, '0'),
898
+ 'HH': String(date.getHours()).padStart(2, '0'),
899
+ 'mm': String(date.getMinutes()).padStart(2, '0'),
900
+ 'ss': String(date.getSeconds()).padStart(2, '0')
901
+ };
902
+ return format.replace(/YYYY|MM|DD|HH|mm|ss/g, (matched) => (map[matched] || matched).toString());
903
+ }
904
+ /**
905
+ * Calculates the number of days between two dates.
906
+ * @param {Date} startDate The start date.
907
+ * @param {Date} endDate The end date.
908
+ * @returns {number} The number of days between the two dates.
909
+ */
910
+ function daysBetween(startDate, endDate) {
911
+ const millisecondsPerDay = 86400000; // Number of milliseconds in one day
912
+ return Math.round((endDate.getTime() - startDate.getTime()) / millisecondsPerDay);
913
+ }
914
+ /**
915
+ * Adds a specified number of days to a date.
916
+ * @param {Date} date The date to modify.
917
+ * @param {number} days The number of days to add.
918
+ * @returns {Date} The new date with days added.
919
+ */
920
+ function addDays(date, days) {
921
+ const result = new Date(date);
922
+ result.setDate(result.getDate() + days);
923
+ return result;
924
+ }
925
+ /**
926
+ * Subtracts a specified number of days from a date.
927
+ * @param {Date} date The date to modify.
928
+ * @param {number} days The number of days to subtract.
929
+ * @returns {Date} The new date with days subtracted.
930
+ */
931
+ function subtractDays(date, days) {
932
+ return addDays(date, -days);
933
+ }
934
+ /**
935
+ * Determines if a year is a leap year.
936
+ * @param {number} year The year to check.
937
+ * @returns {boolean} True if the year is a leap year, false otherwise.
938
+ */
939
+ function isLeapYear(year) {
940
+ return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
941
+ }
942
+ /**
943
+ * Gets the first day of the month for a given date.
944
+ * @param {Date} date The date to use.
945
+ * @returns {Date} The first day of the month.
946
+ */
947
+ function getStartOfMonth(date) {
948
+ return new Date(date.getFullYear(), date.getMonth(), 1);
949
+ }
950
+ /**
951
+ * Gets the last day of the month for a given date.
952
+ * @param {Date} date The date to use.
953
+ * @returns {Date} The last day of the month.
954
+ */
955
+ function getEndOfMonth(date) {
956
+ return new Date(date.getFullYear(), date.getMonth() + 1, 0);
957
+ }
958
+ /**
959
+ * Calculates age from a given birth date.
960
+ * @param {Date} birthDate The birth date.
961
+ * @returns {number} The calculated age.
962
+ */
963
+ function calculateAge(birthDate) {
964
+ const today = new Date();
965
+ let age = today.getFullYear() - birthDate.getFullYear();
966
+ const m = today.getMonth() - birthDate.getMonth();
967
+ if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
968
+ age--;
969
+ }
970
+ return age;
971
+ }
972
+ /**
973
+ * Calculates the number of days until the next birthday.
974
+ * @param {Date} birthDate The birth date.
975
+ * @returns {number} The number of days until the next birthday.
976
+ */
977
+ function daysToNextBirthday(birthDate) {
978
+ const today = new Date();
979
+ const currentYear = today.getFullYear();
980
+ const nextBirthday = new Date(birthDate);
981
+ nextBirthday.setFullYear(currentYear);
982
+ if (nextBirthday < today) {
983
+ nextBirthday.setFullYear(currentYear + 1);
984
+ }
985
+ return daysBetween(today, nextBirthday);
986
+ }
987
+ /**
988
+ * Calculates the age at a specific date.
989
+ * @param {Date} birthDate The birth date.
990
+ * @param {Date} atDate The date to calculate the age at.
991
+ * @returns {number} The calculated age.
992
+ */
993
+ function ageAtDate(birthDate, atDate) {
994
+ let age = atDate.getFullYear() - birthDate.getFullYear();
995
+ const m = atDate.getMonth() - birthDate.getMonth();
996
+ if (m < 0 || (m === 0 && atDate.getDate() < birthDate.getDate())) {
997
+ age--;
998
+ }
999
+ return age;
1000
+ }
1001
+
1002
+ /**
1003
+ * Creates a debounced asynchronous validator function.
1004
+ *
1005
+ * @param validator - The async validator function to debounce.
1006
+ * @param delay - The debounce delay in milliseconds.
1007
+ * @returns A debounced version of the validator function.
1008
+ */
1009
+ function debounceAsyncValidator(validator, delay) {
1010
+ let currentPromiseReject = null;
1011
+ /**
1012
+ * Creates a debounce delay.
1013
+ *
1014
+ * @returns A promise that resolves after the debounce delay.
1015
+ */
1016
+ function debounce() {
1017
+ return new Promise((resolve, reject) => {
1018
+ const { start, stop } = useTimeoutFn(() => {
1019
+ currentPromiseReject = null;
1020
+ resolve();
1021
+ }, delay);
1022
+ if (currentPromiseReject) {
1023
+ currentPromiseReject(new Error('replaced'));
1024
+ }
1025
+ currentPromiseReject = reject;
1026
+ start();
1027
+ });
1028
+ }
1029
+ return function (value) {
1030
+ if (currentPromiseReject) {
1031
+ currentPromiseReject(new Error('replaced'));
1032
+ currentPromiseReject = null;
1033
+ }
1034
+ return validator.call(this, value, debounce);
1035
+ };
1036
+ }
1037
+ /**
1038
+ * Creates a debounced version of an asynchronous function.
1039
+ * @param {Function} func The asynchronous function to debounce.
1040
+ * @param {number} wait The number of milliseconds to wait before invoking the function.
1041
+ * @returns {Function} The debounced function.
1042
+ */
1043
+ function debounceAsync(func, wait) {
1044
+ let timeoutReject = null;
1045
+ return function (...args) {
1046
+ if (timeoutReject) {
1047
+ timeoutReject(new Error('replaced'));
1048
+ timeoutReject = null;
1049
+ }
1050
+ return new Promise((resolve, reject) => {
1051
+ const { start } = useTimeoutFn(() => {
1052
+ func.apply(this, args).then(resolve).catch(reject);
1053
+ }, wait);
1054
+ timeoutReject = reject;
1055
+ start();
1056
+ });
1057
+ };
1058
+ }
1059
+ /**
1060
+ * Creates a debounced asynchronous function that executes immediately on the first call.
1061
+ * @param {Function} func The asynchronous function to debounce.
1062
+ * @param {number} wait The number of milliseconds to wait before invoking the function.
1063
+ * @returns {Function} The debounced function with immediate execution on the first call.
1064
+ */
1065
+ function debounceAsyncWithImmediate(func, wait) {
1066
+ let timeoutReject = null;
1067
+ let callNow = true;
1068
+ return function (...args) {
1069
+ const context = this;
1070
+ if (callNow) {
1071
+ callNow = false;
1072
+ return func.apply(context, args).finally(() => {
1073
+ const { start } = useTimeoutFn(() => {
1074
+ callNow = true;
1075
+ }, wait);
1076
+ start();
1077
+ });
1078
+ }
1079
+ else {
1080
+ if (timeoutReject) {
1081
+ timeoutReject(new Error('replaced'));
1082
+ timeoutReject = null;
1083
+ }
1084
+ return new Promise((resolve, reject) => {
1085
+ const { start } = useTimeoutFn(() => {
1086
+ func.apply(context, args).then(resolve).catch(reject);
1087
+ }, wait);
1088
+ timeoutReject = reject;
1089
+ start();
1090
+ });
1091
+ }
1092
+ };
1093
+ }
1094
+ /**
1095
+ * Creates a debounced version of a function that executes on the leading edge.
1096
+ * @param {Function} func The function to debounce.
1097
+ * @param {number} wait The number of milliseconds to wait before invoking the function.
1098
+ * @returns {Function} The debounced function.
1099
+ */
1100
+ function debounceLeading(func, wait) {
1101
+ let timeoutReject = null;
1102
+ return function (...args) {
1103
+ const context = this;
1104
+ if (!timeoutReject) {
1105
+ func.apply(context, args);
1106
+ }
1107
+ if (timeoutReject) {
1108
+ timeoutReject(new Error('replaced'));
1109
+ timeoutReject = null;
1110
+ }
1111
+ const { start } = useTimeoutFn(() => {
1112
+ timeoutReject = null;
1113
+ }, wait);
1114
+ timeoutReject = () => { };
1115
+ start();
1116
+ };
1117
+ }
1118
+ /**
1119
+ * Creates a debounced version of a function that executes on the trailing edge.
1120
+ * @param {Function} func The function to debounce.
1121
+ * @param {number} wait The number of milliseconds to wait before invoking the function.
1122
+ * @returns {Function} The debounced function.
1123
+ */
1124
+ function debounceTrailing(func, wait) {
1125
+ let timeoutReject = null;
1126
+ return function (...args) {
1127
+ const context = this;
1128
+ if (timeoutReject) {
1129
+ timeoutReject(new Error('replaced'));
1130
+ timeoutReject = null;
1131
+ }
1132
+ const { start } = useTimeoutFn(() => {
1133
+ func.apply(context, args);
1134
+ }, wait);
1135
+ timeoutReject = () => { };
1136
+ start();
1137
+ };
1138
+ }
1139
+ /**
1140
+ * Creates a debounced version of a function that executes on both leading and trailing edges.
1141
+ * @param {Function} func The function to debounce.
1142
+ * @param {number} wait The number of milliseconds to wait before invoking the function.
1143
+ * @returns {Function} The debounced function.
1144
+ */
1145
+ function debounceLeadingTrailing(func, wait) {
1146
+ let timeoutReject = null;
1147
+ let lastCall = 0;
1148
+ return function (...args) {
1149
+ const context = this;
1150
+ const now = Date.now();
1151
+ if (timeoutReject) {
1152
+ timeoutReject(new Error('replaced'));
1153
+ timeoutReject = null;
1154
+ }
1155
+ if (now - lastCall >= wait) {
1156
+ func.apply(context, args);
1157
+ lastCall = now;
1158
+ }
1159
+ else {
1160
+ const { start } = useTimeoutFn(() => {
1161
+ func.apply(context, args);
1162
+ }, wait - (now - lastCall));
1163
+ timeoutReject = () => { };
1164
+ start();
1165
+ }
1166
+ };
1167
+ }
1168
+ /**
1169
+ * Creates a debounced version of a function.
1170
+ * @param {Function} func The function to debounce.
1171
+ * @param {number} wait The number of milliseconds to wait before invoking the function.
1172
+ * @returns {Function} The debounced function.
1173
+ */
1174
+ function debounce(func, wait) {
1175
+ let timeoutReject = null;
1176
+ return function (...args) {
1177
+ if (timeoutReject) {
1178
+ timeoutReject(new Error('replaced'));
1179
+ timeoutReject = null;
1180
+ }
1181
+ const { start } = useTimeoutFn(() => {
1182
+ func.apply(this, args);
1183
+ }, wait);
1184
+ timeoutReject = () => { };
1185
+ start();
1186
+ };
1187
+ }
1188
+ /**
1189
+ * Creates a throttled version of a function.
1190
+ * @param {Function} func The function to throttle.
1191
+ * @param {number} limit The number of milliseconds to wait between function calls.
1192
+ * @returns {Function} The throttled function.
1193
+ */
1194
+ function throttle(func, limit) {
1195
+ let lastCall = 0;
1196
+ return function (...args) {
1197
+ const now = Date.now();
1198
+ if (now - lastCall >= limit) {
1199
+ lastCall = now;
1200
+ func.apply(this, args);
1201
+ }
1202
+ };
1203
+ }
1204
+
1205
+ /**
1206
+ * Converts a FormData object to a plain JavaScript object.
1207
+ * @param {FormData} formData The FormData object to convert.
1208
+ * @returns {Record<string, any>} The plain JavaScript object.
1209
+ */
1210
+ function formDataToObject(formData) {
1211
+ const obj = {};
1212
+ formData.forEach((value, key) => {
1213
+ // Handle multiple values for the same key
1214
+ if (obj[key]) {
1215
+ if (!Array.isArray(obj[key])) {
1216
+ obj[key] = [obj[key]];
1217
+ }
1218
+ obj[key].push(value);
1219
+ }
1220
+ else {
1221
+ obj[key] = value;
1222
+ }
1223
+ });
1224
+ return obj;
1225
+ }
1226
+ /**
1227
+ * Reads a file as text.
1228
+ * @param {File} file The file to read.
1229
+ * @returns {Promise<string>} A promise that resolves with the file content.
1230
+ */
1231
+ function readFileAsText(file) {
1232
+ return new Promise((resolve, reject) => {
1233
+ const reader = new FileReader();
1234
+ reader.onload = () => resolve(reader.result);
1235
+ reader.onerror = reject;
1236
+ reader.readAsText(file);
1237
+ });
1238
+ }
1239
+ /**
1240
+ * Reads a file as a Data URL.
1241
+ * @param {File} file The file to read.
1242
+ * @returns {Promise<string>} A promise that resolves with the Data URL.
1243
+ */
1244
+ function readFileAsDataURL(file) {
1245
+ return new Promise((resolve, reject) => {
1246
+ const reader = new FileReader();
1247
+ reader.onload = () => resolve(reader.result);
1248
+ reader.onerror = reject;
1249
+ reader.readAsDataURL(file);
1250
+ });
1251
+ }
1252
+ /**
1253
+ * Creates a Blob from a string.
1254
+ * @param {string} content The string content for the Blob.
1255
+ * @param {string} [type='text/plain'] The MIME type of the Blob.
1256
+ * @returns {Blob} The Blob object.
1257
+ */
1258
+ function stringToBlob(content, type = 'text/plain') {
1259
+ return new Blob([content], { type });
1260
+ }
1261
+ /**
1262
+ * Creates a Blob from an ArrayBuffer.
1263
+ * @param {ArrayBuffer} buffer The ArrayBuffer to convert.
1264
+ * @param {string} [type='application/octet-stream'] The MIME type of the Blob.
1265
+ * @returns {Blob} The Blob object.
1266
+ */
1267
+ function bufferToBlob(buffer, type = 'application/octet-stream') {
1268
+ return new Blob([buffer], { type });
1269
+ }
1270
+ /**
1271
+ * Creates and downloads a file from Blob data.
1272
+ * @param {Blob} blob The Blob containing the file data.
1273
+ * @param {string} fileName The name of the file to create.
1274
+ */
1275
+ function downloadBlob(blob, fileName) {
1276
+ const link = document.createElement('a');
1277
+ const url = URL.createObjectURL(blob);
1278
+ link.setAttribute('href', url);
1279
+ link.setAttribute('download', fileName);
1280
+ // Append link to the body and trigger a click
1281
+ document.body.appendChild(link);
1282
+ link.click();
1283
+ // Clean up
1284
+ document.body.removeChild(link);
1285
+ URL.revokeObjectURL(url);
1286
+ }
1287
+ /**
1288
+ * Creates a FormData object containing a Blob.
1289
+ * @param {Blob} blob The Blob to include in the FormData.
1290
+ * @param {string} name The name of the form field.
1291
+ * @param {string} [fileName='file'] The file name for the Blob.
1292
+ * @returns {FormData} The FormData object.
1293
+ */
1294
+ function blobToFormData(blob, name, fileName = 'file') {
1295
+ const formData = new FormData();
1296
+ formData.append(name, blob, fileName);
1297
+ return formData;
1298
+ }
1299
+
1300
+ /**
1301
+ * Converts a Proxy object to a plain object.
1302
+ * @param {ProxyConstructor} proxy The Proxy object to convert.
1303
+ * @returns {Object} The plain object.
1304
+ */
1305
+ function proxyToPlainObject(proxy) {
1306
+ if (!proxy)
1307
+ return {};
1308
+ const plainObject = {};
1309
+ for (const property of Object.keys(proxy)) {
1310
+ plainObject[property] = proxy[property];
1311
+ }
1312
+ return plainObject;
1313
+ }
1314
+ /**
1315
+ * Compares two objects to check if they have the same keys.
1316
+ * @param {Object} object1 The first object to compare.
1317
+ * @param {Object} object2 The second object to compare.
1318
+ * @returns {boolean} True if the objects have the same keys, otherwise false.
1319
+ */
1320
+ function compareObject(object1, object2) {
1321
+ return Object.keys(object1).every(function (element) {
1322
+ return Object.keys(object2).includes(element);
1323
+ });
1324
+ }
1325
+ /**
1326
+ * Deeply compares two objects to check if they are equal.
1327
+ * @param {Object} object1 The first object to compare.
1328
+ * @param {Object} object2 The second object to compare.
1329
+ * @returns {boolean} True if the objects are deeply equal, otherwise false.
1330
+ */
1331
+ function deepEqual(object1, object2) {
1332
+ if (object1 === object2)
1333
+ return true;
1334
+ if (typeof object1 !== 'object' || typeof object2 !== 'object' || object1 === null || object2 === null) {
1335
+ return false;
1336
+ }
1337
+ const keys1 = Object.keys(object1);
1338
+ const keys2 = Object.keys(object2);
1339
+ if (keys1.length !== keys2.length) {
1340
+ return false;
1341
+ }
1342
+ for (const key of keys1) {
1343
+ if (!keys2.includes(key) || !deepEqual(object1[key], object2[key])) {
1344
+ return false;
1345
+ }
1346
+ }
1347
+ return true;
1348
+ }
1349
+ /**
1350
+ * Deeply clones an object.
1351
+ * @param {Object} obj The object to clone.
1352
+ * @returns {Object} The cloned object.
1353
+ */
1354
+ function deepClone(obj) {
1355
+ if (obj === null || typeof obj !== 'object') {
1356
+ return obj;
1357
+ }
1358
+ if (obj instanceof Date) {
1359
+ return new Date(obj.getTime());
1360
+ }
1361
+ if (obj instanceof Array) {
1362
+ return obj.map(item => deepClone(item));
1363
+ }
1364
+ if (obj instanceof Object) {
1365
+ const copy = {};
1366
+ for (const key in obj) {
1367
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1368
+ copy[key] = deepClone(obj[key]);
1369
+ }
1370
+ }
1371
+ return copy;
1372
+ }
1373
+ throw new Error('Unable to clone object! Its type is not supported.');
1374
+ }
1375
+ /**
1376
+ * Converts an object to a query string.
1377
+ * @param {Object} obj The object to convert.
1378
+ * @returns {string} The query string.
1379
+ */
1380
+ function objectToQueryString(obj) {
1381
+ return Object.keys(obj)
1382
+ .map(key => encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]))
1383
+ .join('&');
1384
+ }
1385
+ // Example usage:
1386
+ // objectToQueryString({ name: 'John Doe', age: 30 }); // 'name=John%20Doe&age=30'
1387
+ /**
1388
+ * Gets the differences between two objects.
1389
+ * @param {Object} object1 The first object.
1390
+ * @param {Object} object2 The second object.
1391
+ * @returns {Object} An object containing the differences.
1392
+ */
1393
+ function getObjectDifferences(object1, object2) {
1394
+ const differences = {};
1395
+ const keys = new Set([...Object.keys(object1), ...Object.keys(object2)]);
1396
+ for (const key of keys) {
1397
+ if (object1[key] !== object2[key]) {
1398
+ differences[key] = { object1: object1[key], object2: object2[key] };
1399
+ }
1400
+ }
1401
+ return differences;
1402
+ }
1403
+ /**
1404
+ * Filters an object by a list of keys.
1405
+ * @param {Object} obj The object to filter.
1406
+ * @param {Array<string>} keys The keys to keep.
1407
+ * @returns {Object} The filtered object.
1408
+ */
1409
+ function filterObjectByKeys(obj, keys) {
1410
+ const filteredObject = {};
1411
+ for (const key of keys) {
1412
+ if (key in obj) {
1413
+ filteredObject[key] = obj[key];
1414
+ }
1415
+ }
1416
+ return filteredObject;
1417
+ }
1418
+ // Example usage:
1419
+ // filterObjectByKeys({ name: 'John', age: 30, job: 'Developer' }, ['name', 'job']); // { name: 'John', job: 'Developer' }
1420
+ /**
1421
+ * Deeply merges two objects.
1422
+ * @param {Object} target The target object to merge into.
1423
+ * @param {Object} source The source object to merge from.
1424
+ * @returns {Object} The merged object.
1425
+ */
1426
+ function deepMerge(target, source) {
1427
+ if (target === null || typeof target !== 'object' || typeof source !== 'object') {
1428
+ return target;
1429
+ }
1430
+ for (const key in source) {
1431
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
1432
+ if (source[key] && typeof source[key] === 'object') {
1433
+ if (target && !target[key]) {
1434
+ Object.assign(target, { [key]: {} });
1435
+ }
1436
+ deepMerge(target[key], source[key]);
1437
+ }
1438
+ else {
1439
+ Object.assign(target, { [key]: source[key] });
1440
+ }
1441
+ }
1442
+ }
1443
+ return target;
1444
+ }
1445
+ /**
1446
+ * Checks if an object is empty.
1447
+ * @param {Object} obj The object to check.
1448
+ * @returns {boolean} True if the object is empty, otherwise false.
1449
+ */
1450
+ function isEmptyObject(obj) {
1451
+ return Object.keys(obj).length === 0;
1452
+ }
1453
+ /**
1454
+ * Safely accesses nested properties in an object.
1455
+ * @param {Object} obj The object to access.
1456
+ * @param {Array<string>} keys The array of keys representing the path.
1457
+ * @returns {any} The value at the nested path, or undefined if not found.
1458
+ */
1459
+ function safeGet(obj, keys) {
1460
+ return keys.reduce((acc, key) => (acc && acc[key] !== undefined) ? acc[key] : undefined, obj);
1461
+ }
1462
+ // Example usage:
1463
+ // safeGet({ a: { b: { c: 10 } } }, ['a', 'b', 'c']); // 10
1464
+ // safeGet({ a: { b: { c: 10 } } }, ['a', 'x', 'c']); // undefined
1465
+ /**
1466
+ * Removes empty properties (null, undefined, or empty string) from an object.
1467
+ * @param {Object} obj The object to clean.
1468
+ * @returns {Object} A new object without empty properties.
1469
+ */
1470
+ function removeEmptyProperties(obj) {
1471
+ return Object.keys(obj)
1472
+ .filter(key => obj[key] !== null && obj[key] !== undefined && obj[key] !== '')
1473
+ .reduce((acc, key) => {
1474
+ acc[key] = obj[key];
1475
+ return acc;
1476
+ }, {});
1477
+ }
1478
+ // Example usage:
1479
+ // removeEmptyProperties({ a: null, b: 2, c: undefined, d: '', e: 'hello' }); // { b: 2, e: 'hello' }
1480
+ /**
1481
+ * Retrieves all keys of an object as an array.
1482
+ * @param {Object} obj The object to retrieve keys from.
1483
+ * @returns {Array<string>} The array of keys.
1484
+ */
1485
+ function getObjectKeys(obj) {
1486
+ return Object.keys(obj);
1487
+ }
1488
+ // Example usage:
1489
+ // getObjectKeys({ name: 'John', age: 30 }); // ['name', 'age']
1490
+ /**
1491
+ * Checks if an object has nested properties.
1492
+ * @param {Object} obj The object to check.
1493
+ * @returns {boolean} True if there are nested properties, false otherwise.
1494
+ */
1495
+ function hasNestedProperties(obj) {
1496
+ return Object.values(obj).some(value => typeof value === 'object' && value !== null);
1497
+ }
1498
+ // Example usage:
1499
+ // hasNestedProperties({ a: 1, b: { c: 2 } }); // true
1500
+ // hasNestedProperties({ a: 1, b: 2 }); // false
1501
+ /**
1502
+ * Converts an object to FormData, handling nested objects.
1503
+ * @param {Object} obj The object to convert.
1504
+ * @param {FormData} [formData] The FormData object to append to.
1505
+ * @param {string} [parentKey] The parent key for nested objects.
1506
+ * @returns {FormData} The FormData object.
1507
+ */
1508
+ function objectToFormDataEnhanced(obj, formData = new FormData(), parentKey = '') {
1509
+ Object.entries(obj).forEach(([key, value]) => {
1510
+ const finalKey = parentKey ? `${parentKey}[${key}]` : key;
1511
+ if (value && typeof value === 'object' && !(value instanceof File)) {
1512
+ objectToFormDataEnhanced(value, formData, finalKey);
1513
+ }
1514
+ else {
1515
+ formData.append(finalKey, value);
1516
+ }
1517
+ });
1518
+ return formData;
1519
+ }
1520
+ // Example usage:
1521
+ // objectToFormDataEnhanced({ user: { name: 'John', age: 30 } });
1522
+ /**
1523
+ * Converts a JavaScript object into FormData.
1524
+ *
1525
+ * @param obj - The object to be converted.
1526
+ * @param form - An optional FormData instance to use.
1527
+ * @param namespace - An optional namespace to use for nested objects.
1528
+ * @returns The FormData instance with the object's key-value pairs.
1529
+ */
1530
+ const objectToFormData = function (obj, form, namespace) {
1531
+ const fd = form || new FormData();
1532
+ let formKey;
1533
+ for (const property in obj) {
1534
+ if (obj[property] === undefined) {
1535
+ continue;
1536
+ }
1537
+ if (Object.prototype.hasOwnProperty.call(obj, property)) {
1538
+ if (namespace) {
1539
+ formKey = `${namespace}[${property}]`;
1540
+ }
1541
+ else {
1542
+ formKey = property;
1543
+ }
1544
+ if (typeof obj[property] === 'object' && !(obj[property] instanceof File)) {
1545
+ // Recursively handle nested objects
1546
+ objectToFormData(obj[property], fd, formKey);
1547
+ }
1548
+ else {
1549
+ // Convert boolean values to 1/0
1550
+ const value = obj[property] === true || obj[property] === false ? Number(obj[property]) : obj[property];
1551
+ fd.append(formKey, value);
1552
+ }
1553
+ }
1554
+ }
1555
+ return fd;
1556
+ };
1557
+ /**
1558
+ * Flattens a nested object, bringing all properties to the top level.
1559
+ * @param {Object} obj The object to flatten.
1560
+ * @param {string} [parentKey] The parent key for nested properties.
1561
+ * @param {Object} [result] The resulting flattened object.
1562
+ * @returns {Object} The flattened object.
1563
+ */
1564
+ function flattenObject(obj, parentKey = '', result = {}) {
1565
+ for (const key in obj) {
1566
+ if (obj.hasOwnProperty(key)) {
1567
+ const propName = parentKey ? `${parentKey}.${key}` : key;
1568
+ if (typeof obj[key] === 'object' && obj[key] !== null) {
1569
+ flattenObject(obj[key], propName, result);
1570
+ }
1571
+ else {
1572
+ result[propName] = obj[key];
1573
+ }
1574
+ }
1575
+ }
1576
+ return result;
1577
+ }
1578
+ // Example usage:
1579
+ // flattenObject({ a: 1, b: { c: 2, d: { e: 3 } } }); // { 'a': 1, 'b.c': 2, 'b.d.e': 3 }
1580
+
1581
+ /**
1582
+ * Capitalizes the first character of a string.
1583
+ * @param {string} s The string to capitalize.
1584
+ * @returns {string} The capitalized string.
1585
+ */
1586
+ function upperFirst(s) {
1587
+ return s.charAt(0).toUpperCase() + s.slice(1);
1588
+ }
1589
+ // Example usage:
1590
+ // console.log(upperFirst('hello')); // 'Hello'
1591
+ /**
1592
+ * Lowercases the first character of a string.
1593
+ * @param {string} s The string to lowercase.
1594
+ * @returns {string} The lowercased string.
1595
+ */
1596
+ function lowerFirst(s) {
1597
+ return s.charAt(0).toLowerCase() + s.slice(1);
1598
+ }
1599
+ // Example usage:
1600
+ // console.log(lowerFirst('Hello')); // 'hello'
1601
+ /**
1602
+ * Removes accents and special characters from a string and converts it to a URL-friendly format.
1603
+ * @param {string} input The string to process.
1604
+ * @returns {string} The processed string.
1605
+ */
1606
+ function removeAccent(input) {
1607
+ return input
1608
+ .toLowerCase()
1609
+ .trim()
1610
+ .replace(/[\s_-]+/g, "-")
1611
+ .replace(/^-+|-+$/g, "")
1612
+ .normalize("NFD")
1613
+ .replace(/[\u0300-\u036f]/g, "");
1614
+ }
1615
+ // Example usage:
1616
+ // console.log(removeAccent('Café du Nord')); // 'cafe-du-nord'
1617
+ /**
1618
+ * Reverses a string.
1619
+ * @param {string} str The string to reverse.
1620
+ * @returns {string} The reversed string.
1621
+ */
1622
+ function reverseString(str) {
1623
+ return str.split('').reverse().join('');
1624
+ }
1625
+ // Example usage:
1626
+ // console.log(reverseString('hello')); // 'olleh'
1627
+ /**
1628
+ * Counts the number of words in a string.
1629
+ * @param {string} str The string to analyze.
1630
+ * @returns {number} The word count.
1631
+ */
1632
+ function countWords(str) {
1633
+ return str.trim().split(/\s+/).length;
1634
+ }
1635
+ // Example usage:
1636
+ // console.log(countWords('Hello world!')); // 2
1637
+ /**
1638
+ * Truncates a string to the specified length and adds ellipsis if necessary.
1639
+ * @param {string} str The string to truncate.
1640
+ * @param {number} maxLength The maximum length of the string.
1641
+ * @returns {string} The truncated string.
1642
+ */
1643
+ function truncateString(str, maxLength) {
1644
+ return str.length > maxLength ? str.slice(0, maxLength) + '...' : str;
1645
+ }
1646
+ // Example usage:
1647
+ // console.log(truncateString('This is a long string', 10)); // 'This is a...'
1648
+ /**
1649
+ * Converts a string to camel case.
1650
+ * @param {string} str The string to convert.
1651
+ * @returns {string} The camel cased string.
1652
+ */
1653
+ function toCamelCase(str) {
1654
+ return str
1655
+ .toLowerCase()
1656
+ .replace(/[^a-zA-Z0-9]+(.)/g, (match, chr) => chr.toUpperCase());
1657
+ }
1658
+ // Example usage:
1659
+ // console.log(toCamelCase('hello world example')); // 'helloWorldExample'
1660
+ /**
1661
+ * Converts a string to kebab case.
1662
+ * @param {string} str The string to convert.
1663
+ * @returns {string} The kebab cased string.
1664
+ */
1665
+ function toKebabCase(str) {
1666
+ return str
1667
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
1668
+ .replace(/[\s_]+/g, '-')
1669
+ .toLowerCase();
1670
+ }
1671
+ // Example usage:
1672
+ // console.log(toKebabCase('Hello World Example')); // 'hello-world-example'
1673
+ /**
1674
+ * Replaces all instances of a substring within a string.
1675
+ * @param {string} str The original string.
1676
+ * @param {string} find The substring to find.
1677
+ * @param {string} replace The substring to replace with.
1678
+ * @returns {string} The modified string.
1679
+ */
1680
+ function replaceAll(str, find, replace) {
1681
+ return str.split(find).join(replace);
1682
+ }
1683
+ // Example usage:
1684
+ // console.log(replaceAll('hello world', 'o', 'a')); // 'hella warld'
1685
+ /**
1686
+ * Generates a random string of a specific length.
1687
+ * @param {number} length The length of the string to generate.
1688
+ * @returns {string} The random string.
1689
+ */
1690
+ function generateRandomString(length) {
1691
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
1692
+ return Array.from({ length }, () => characters.charAt(Math.floor(Math.random() * characters.length))).join('');
1693
+ }
1694
+ // Example usage:
1695
+ // console.log(generateRandomString(10)); // 'A1b2C3d4E5'
1696
+
1697
+ /**
1698
+ * Validates if the key pressed is a valid letter or special character.
1699
+ * @param {KeyboardEvent} e The keyboard event.
1700
+ * @returns {boolean} True if the key is valid, otherwise false.
1701
+ */
1702
+ function validateLetters(e) {
1703
+ const key = e.keyCode;
1704
+ const validKeys = [
1705
+ ...Array.from({ length: 26 }, (_, i) => i + 65), // Letters A-Z
1706
+ ...Array.from({ length: 26 }, (_, i) => i + 97), // Letters a-z
1707
+ 45, // Hyphen
1708
+ 32, // Space
1709
+ 241, // ñ
1710
+ 209, // Ñ
1711
+ 225, // á
1712
+ 233, // é
1713
+ 237, // í
1714
+ 243, // ó
1715
+ 250, // ú
1716
+ 193, // Á
1717
+ 201, // É
1718
+ 205, // Í
1719
+ 211, // Ó
1720
+ 218 // Ú
1721
+ ];
1722
+ if (validKeys.includes(key)) {
1723
+ return true;
1724
+ }
1725
+ e.preventDefault();
1726
+ return false;
1727
+ }
1728
+ // Example usage:
1729
+ // document.addEventListener('keydown', validateLetters);
1730
+ /**
1731
+ * Validates if the key pressed is a valid alphanumeric character.
1732
+ * @param {KeyboardEvent} e The keyboard event.
1733
+ * @returns {boolean} True if the key is valid, otherwise false.
1734
+ */
1735
+ function validateAlphanumeric(e) {
1736
+ const key = e.keyCode;
1737
+ const validKeys = [
1738
+ ...Array.from({ length: 10 }, (_, i) => i + 48), // Numbers 0-9
1739
+ ...Array.from({ length: 26 }, (_, i) => i + 65), // Letters A-Z
1740
+ ...Array.from({ length: 26 }, (_, i) => i + 97), // Letters a-z
1741
+ 45, // Hyphen
1742
+ 95, // Underscore
1743
+ 32 // Space
1744
+ ];
1745
+ if (validKeys.includes(key)) {
1746
+ return true;
1747
+ }
1748
+ e.preventDefault();
1749
+ return false;
1750
+ }
1751
+ // Example usage:
1752
+ // document.addEventListener('keydown', validateAlphanumeric);
1753
+ /**
1754
+ * Validates if the key pressed is a number.
1755
+ * @param {KeyboardEvent} e The keyboard event.
1756
+ * @returns {boolean} True if the key is a number, otherwise false.
1757
+ */
1758
+ function validateNumbers(e) {
1759
+ const key = e.keyCode;
1760
+ if ((key >= 48 && key <= 57) || key === 46 || key === 8 || key === 37 || key === 39) {
1761
+ return true;
1762
+ }
1763
+ e.preventDefault();
1764
+ return false;
1765
+ }
1766
+ // Example usage:
1767
+ // document.addEventListener('keydown', validateNumbers);
1768
+ /**
1769
+ * Validates if a phone number is valid.
1770
+ * @param {string} phoneNumber The phone number to validate.
1771
+ * @returns {boolean} True if the phone number is valid, otherwise false.
1772
+ */
1773
+ function isValidPhoneNumber(phoneNumber) {
1774
+ const phonePattern = /^[0-9]{10}$/; // Example pattern for 10-digit phone numbers
1775
+ return phonePattern.test(phoneNumber);
1776
+ }
1777
+ // Example usage:
1778
+ // console.log(isValidPhoneNumber('1234567890')); // true
1779
+ // console.log(isValidPhoneNumber('123-456-7890')); // false
1780
+ /**
1781
+ * Validates if a string is a valid email address.
1782
+ * @param {string} email The email address to validate.
1783
+ * @returns {boolean} True if the email address is valid, otherwise false.
1784
+ */
1785
+ function isValidEmail(email) {
1786
+ const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1787
+ return emailPattern.test(email);
1788
+ }
1789
+ // Example usage:
1790
+ // console.log(isValidEmail('example@domain.com')); // true
1791
+ // console.log(isValidEmail('invalid-email')); // false
1792
+ /**
1793
+ * Validates if a string is a valid URL.
1794
+ * @param {string} url The URL to validate.
1795
+ * @returns {boolean} True if the URL is valid, otherwise false.
1796
+ */
1797
+ function isValidURL(url) {
1798
+ try {
1799
+ new URL(url);
1800
+ return true;
1801
+ }
1802
+ catch {
1803
+ return false;
1804
+ }
1805
+ }
1806
+ // Example usage:
1807
+ // console.log(isValidURL('https://www.example.com')); // true
1808
+ // console.log(isValidURL('invalid-url')); // false
1809
+ /**
1810
+ * Validates if a string is a valid date in YYYY-MM-DD format.
1811
+ * @param {string} date The date string to validate.
1812
+ * @returns {boolean} True if the date is valid, otherwise false.
1813
+ */
1814
+ function isValidDate(date) {
1815
+ const datePattern = /^\d{4}-\d{2}-\d{2}$/;
1816
+ if (!datePattern.test(date))
1817
+ return false;
1818
+ const [year, month, day] = date.split('-').map(Number);
1819
+ const dateObj = new Date(year, month - 1, day);
1820
+ return dateObj.getFullYear() === year && dateObj.getMonth() === month - 1 && dateObj.getDate() === day;
1821
+ }
1822
+ // Example usage:
1823
+ // console.log(isValidDate('2024-08-31')); // true
1824
+ // console.log(isValidDate('2024-02-30')); // false
1825
+ /**
1826
+ * Validates if a password meets certain strength criteria.
1827
+ * @param {string} password The password to validate.
1828
+ * @returns {boolean} True if the password is strong, otherwise false.
1829
+ */
1830
+ function isStrongPassword(password) {
1831
+ const minLength = 8;
1832
+ const hasUpperCase = /[A-Z]/.test(password);
1833
+ const hasLowerCase = /[a-z]/.test(password);
1834
+ const hasNumbers = /[0-9]/.test(password);
1835
+ const hasSpecialChars = /[!@#$%^&*(),.?":{}|<>]/.test(password);
1836
+ return password.length >= minLength && hasUpperCase && hasLowerCase && hasNumbers && hasSpecialChars;
1837
+ }
1838
+ // Example usage:
1839
+ // console.log(isStrongPassword('Strong1@password')); // true
1840
+ // console.log(isStrongPassword('weakpass')); // false
1841
+ /**
1842
+ * Validates a credit card number using the Luhn algorithm.
1843
+ * @param {string} cardNumber The credit card number to validate.
1844
+ * @returns {boolean} True if the credit card number is valid, otherwise false.
1845
+ */
1846
+ function isValidCreditCard(cardNumber) {
1847
+ const sanitized = cardNumber.replace(/\D/g, '');
1848
+ let sum = 0;
1849
+ let shouldDouble = false;
1850
+ for (let i = sanitized.length - 1; i >= 0; i--) {
1851
+ let digit = parseInt(sanitized.charAt(i), 10);
1852
+ if (shouldDouble) {
1853
+ digit *= 2;
1854
+ if (digit > 9)
1855
+ digit -= 9;
1856
+ }
1857
+ sum += digit;
1858
+ shouldDouble = !shouldDouble;
1859
+ }
1860
+ return sum % 10 === 0;
1861
+ }
1862
+ // Example usage:
1863
+ // console.log(isValidCreditCard('4111111111111111')); // true
1864
+ // console.log(isValidCreditCard('1234567812345670')); // false
1865
+ /**
1866
+ * Validates if a string is a valid hex color code.
1867
+ * @param {string} color The color code to validate.
1868
+ * @returns {boolean} True if the color code is valid, otherwise false.
1869
+ */
1870
+ function isValidHexColor(color) {
1871
+ const hexPattern = /^#([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})$/;
1872
+ return hexPattern.test(color);
1873
+ }
1874
+ // Example usage:
1875
+ // console.log(isValidHexColor('#FFFFFF')); // true
1876
+ // console.log(isValidHexColor('#FFF')); // true
1877
+ // console.log(isValidHexColor('#12345G')); // false
1878
+ /**
1879
+ * Validates if a string is a valid time in HH:MM format.
1880
+ * @param {string} time The time string to validate.
1881
+ * @returns {boolean} True if the time is valid, otherwise false.
1882
+ */
1883
+ function isValidTime(time) {
1884
+ const timePattern = /^([01]\d|2[0-3]):([0-5]\d)$/;
1885
+ return timePattern.test(time);
1886
+ }
1887
+ // Example usage:
1888
+ // console.log(isValidTime('14:30')); // true
1889
+ // console.log(isValidTime('25:00')); // false
1890
+ /**
1891
+ * Validates if a string is a valid IPv4 address.
1892
+ * @param {string} ip The IP address to validate.
1893
+ * @returns {boolean} True if the IP address is valid, otherwise false.
1894
+ */
1895
+ function isValidIP(ip) {
1896
+ const ipPattern = /^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)$/;
1897
+ return ipPattern.test(ip);
1898
+ }
1899
+ // Example usage:
1900
+ // console.log(isValidIP('192.168.1.1')); // true
1901
+ // console.log(isValidIP('999.999.999.999')); // false
1902
+ /**
1903
+ * Validates if a string is a valid U.S. Social Security Number (SSN).
1904
+ * @param {string} ssn The SSN to validate.
1905
+ * @returns {boolean} True if the SSN is valid, otherwise false.
1906
+ */
1907
+ function isValidSSN(ssn) {
1908
+ const ssnPattern = /^\d{3}-\d{2}-\d{4}$/;
1909
+ return ssnPattern.test(ssn);
1910
+ }
1911
+ // Example usage:
1912
+ // console.log(isValidSSN('123-45-6789')); // true
1913
+ // console.log(isValidSSN('123-45-678')); // false
1914
+ /**
1915
+ * Validates if a string is a valid U.S. ZIP code.
1916
+ * @param {string} zip The ZIP code to validate.
1917
+ * @returns {boolean} True if the ZIP code is valid, otherwise false.
1918
+ */
1919
+ function isValidZIP(zip) {
1920
+ const zipPattern = /^\d{5}(-\d{4})?$/;
1921
+ return zipPattern.test(zip);
1922
+ }
1923
+ // Example usage:
1924
+ // console.log(isValidZIP('12345')); // true
1925
+ // console.log(isValidZIP('12345-6789')); // true
1926
+ // console.log(isValidZIP('1234')); // false
1927
+ /**
1928
+ * Validates if a string is a valid credit card expiry date in MM/YY format.
1929
+ * @param {string} expiryDate The expiry date to validate.
1930
+ * @returns {boolean} True if the expiry date is valid, otherwise false.
1931
+ */
1932
+ function isValidExpiryDate(expiryDate) {
1933
+ const expiryPattern = /^(0[1-9]|1[0-2])\/\d{2}$/;
1934
+ if (!expiryPattern.test(expiryDate))
1935
+ return false;
1936
+ const [month, year] = expiryDate.split('/').map(Number);
1937
+ const currentYear = new Date().getFullYear() % 100;
1938
+ const currentMonth = new Date().getMonth() + 1;
1939
+ return (year > currentYear) || (year === currentYear && month >= currentMonth);
1940
+ }
1941
+ // Example usage:
1942
+ // console.log(isValidExpiryDate('08/24')); // true
1943
+ // console.log(isValidExpiryDate('12/22')); // false
1944
+ /**
1945
+ * Validates if a string is a valid 8-character hexadecimal color code (including alpha).
1946
+ * @param {string} color The color code to validate.
1947
+ * @returns {boolean} True if the color code is valid, otherwise false.
1948
+ */
1949
+ function isValidHexColorAlpha(color) {
1950
+ const hexPattern = /^#([0-9A-Fa-f]{8})$/;
1951
+ return hexPattern.test(color);
1952
+ }
1953
+ // Example usage:
1954
+ // console.log(isValidHexColorAlpha('#RRGGBBAA')); // true
1955
+ // console.log(isValidHexColorAlpha('#FFFFFF')); // false
1956
+ /**
1957
+ * Validates if a username meets specific criteria.
1958
+ * @param {string} username The username to validate.
1959
+ * @returns {boolean} True if the username is valid, otherwise false.
1960
+ */
1961
+ function isValidUsername(username) {
1962
+ const usernamePattern = /^[a-zA-Z0-9_]{3,16}$/; // 3 to 16 characters, letters, numbers, and underscores only
1963
+ return usernamePattern.test(username);
1964
+ }
1965
+ // Example usage:
1966
+ // console.log(isValidUsername('user_name123')); // true
1967
+ // console.log(isValidUsername('us')); // false
1968
+ /**
1969
+ * Validates if a string represents a valid age between 0 and 120.
1970
+ * @param {string} age The age to validate.
1971
+ * @returns {boolean} True if the age is valid, otherwise false.
1972
+ */
1973
+ function isValidAge(age) {
1974
+ const ageNumber = parseInt(age, 10);
1975
+ return !isNaN(ageNumber) && ageNumber >= 0 && ageNumber <= 120;
1976
+ }
1977
+ // Example usage:
1978
+ // console.log(isValidAge('25')); // true
1979
+ // console.log(isValidAge('121')); // false
1980
+ /**
1981
+ * Validates if a string is a valid hexadecimal number.
1982
+ * @param {string} hex The hexadecimal number to validate.
1983
+ * @returns {boolean} True if the number is valid, otherwise false.
1984
+ */
1985
+ function isValidHexNumber(hex) {
1986
+ const hexPattern = /^[0-9A-Fa-f]+$/;
1987
+ return hexPattern.test(hex);
1988
+ }
1989
+ // Example usage:
1990
+ // console.log(isValidHexNumber('1A3F')); // true
1991
+ // console.log(isValidHexNumber('GHIJ')); // false
1992
+
1993
+ export { APP_LOCAL_CACHE_KEY, APP_SESSION_CACHE_KEY, AxiosService, CacheTypeEnum, ContentTypeEnum, ExceptionEnum, FileMetaTypes, ImageTypes, KeyCodeEnum, LOCALE_KEY, LOCK_INFO_KEY, PROJ_CFG_KEY, ROLES_KEY, TOKEN_KEY, USER_INFO_KEY, addCustomKeyboardShortcut, addDays, addDoubleClickListener, addKeyListener, ageAtDate, blobToFormData, bufferToBlob, calculateAge, clickOutside, compareObject, copyToClipboard, countWords, createKeyMap, customShortcut, daysBetween, daysToNextBirthday, debounce, debounceAsync, debounceAsyncValidator, debounceAsyncWithImmediate, debounceLeading, debounceLeadingTrailing, debounceTrailing, deepClone, deepEqual, deepMerge, detectKeyHold, disableCopy, disableF12Key, disableMouseButtons, disableRightClick, disableSpecificKeys, downloadBlob, enableMouseButtons, enableRightClick, enableSpecificKeys, exportToCSV, exportToExcel, exportToJSON, exportToText, exportToXML, filterObjectByKeys, flattenObject, formDataToObject, formatDate, generateRandomString, getEndOfMonth, getObjectDifferences, getObjectKeys, getQueryParam, getStartOfMonth, hasNestedProperties, isEmptyObject, isLeapYear, isStrongPassword, isValidAge, isValidCreditCard, isValidDate, isValidEmail, isValidExpiryDate, isValidHexColor, isValidHexColorAlpha, isValidHexNumber, isValidIP, isValidPhoneNumber, isValidSSN, isValidTime, isValidURL, isValidUsername, isValidZIP, lowerFirst, objectToFormData, objectToFormDataEnhanced, objectToQueryString, openWindow, parseDate, proxyToPlainObject, readFileAsDataURL, readFileAsText, registerKeyboardShortcuts, removeAccent, removeClickOutside, removeCustomKeyboardShortcut, removeCustomShortcuts, removeDoubleClickListener, removeEmptyProperties, removeKeyListeners, replaceAll, reverseString, safeGet, screenEnum, screenMap, scrollToTop, simulateKeyPress, sizeEnum, stopDetectingKeyHold, stringToBlob, subtractDays, throttle, toCamelCase, toKebabCase, toggleTabNavigation, truncateString, unregisterKeyboardShortcuts, upperFirst, useSorter, validateAlphanumeric, validateLetters, validateNumbers };