@kupola/kupola 1.4.2 → 1.4.4
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/README.md +28 -3
- package/css/components-ext.css +1 -1
- package/css/kupola.css +3 -2
- package/css/table.css +74 -0
- package/dist/css/components-ext.css +158 -1
- package/dist/css/components.css +93 -1
- package/dist/css/kupola.css +13 -1
- package/dist/css/table.css +74 -0
- package/dist/css/theme-dark.css +5 -5
- package/dist/css/utilities.css +159 -1
- package/dist/kupola.cjs.js +214 -17409
- package/dist/kupola.cjs.js.map +1 -1
- package/dist/kupola.css +101 -4
- package/dist/kupola.esm.js +8196 -17225
- package/dist/kupola.esm.js.map +1 -1
- package/dist/kupola.min.css +1 -1
- package/dist/kupola.umd.js +214 -17415
- package/dist/kupola.umd.js.map +1 -1
- package/dist/types/kupola.d.ts +41 -25
- package/js/calendar.js +3 -10
- package/js/carousel.js +3 -10
- package/js/collapse.js +3 -10
- package/js/color-picker.js +5 -16
- package/js/component.js +4 -18
- package/js/countdown.js +3 -10
- package/js/data-bind.js +56 -36
- package/js/datepicker.js +16 -24
- package/js/depends.js +27 -16
- package/js/dialog.js +1 -5
- package/js/drawer.js +3 -11
- package/js/dropdown.js +11 -18
- package/js/dynamic-tags.js +3 -10
- package/js/fileupload.js +10 -12
- package/js/form.js +26 -20
- package/js/global-events.js +1 -14
- package/js/heatmap.js +3 -12
- package/js/i18n.js +1 -14
- package/js/icons.js +2 -2
- package/js/image-preview.js +1 -7
- package/js/initializer.js +0 -43
- package/js/kupola-core.js +1 -13
- package/js/kupola-lifecycle.js +1 -9
- package/js/message.js +1 -6
- package/js/modal.js +22 -20
- package/js/notification.js +1 -6
- package/js/numberinput.js +3 -10
- package/js/pagination.js +0 -5
- package/js/registry.js +0 -4
- package/js/select.js +5 -16
- package/js/slide-captcha.js +8 -21
- package/js/slider.js +3 -10
- package/js/statcard.js +3 -11
- package/js/table.js +240 -262
- package/js/tag.js +3 -10
- package/js/theme.js +11 -16
- package/js/timepicker.js +6 -19
- package/js/tooltip.js +3 -10
- package/js/utils.js +281 -1439
- package/js/validation.js +2 -7
- package/js/virtual-list.js +13 -38
- package/js/web-components.js +0 -9
- package/package.json +12 -7
- package/scripts/build-css.cjs +3 -2
- package/types/kupola.d.ts +41 -25
- package/version.json +10 -0
- package/dist/css/kupola.min.css +0 -1
- package/dist/icons.svg +0 -284
- package/dist/kupola.min.js +0 -2
- package/dist/kupola.min.js.map +0 -1
- /package/css/{colors_and_type.css → colors-and-type.css} +0 -0
- /package/dist/css/{colors_and_type.css → colors-and-type.css} +0 -0
package/js/utils.js
CHANGED
|
@@ -1,1462 +1,304 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
trimLeft(str) {
|
|
7
|
-
return str ? str.replace(/^\s+/, '') : '';
|
|
8
|
-
},
|
|
9
|
-
trimRight(str) {
|
|
10
|
-
return str ? str.replace(/\s+$/, '') : '';
|
|
11
|
-
},
|
|
12
|
-
toUpperCase(str) {
|
|
13
|
-
return str ? str.toUpperCase() : '';
|
|
14
|
-
},
|
|
15
|
-
toLowerCase(str) {
|
|
16
|
-
return str ? str.toLowerCase() : '';
|
|
17
|
-
},
|
|
18
|
-
capitalize(str) {
|
|
19
|
-
if (!str) return '';
|
|
20
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
21
|
-
},
|
|
22
|
-
camelize(str) {
|
|
23
|
-
if (!str) return '';
|
|
24
|
-
return str.replace(/-(\w)/g, (_, c) => c ? c.toUpperCase() : '');
|
|
25
|
-
},
|
|
26
|
-
hyphenate(str) {
|
|
27
|
-
if (!str) return '';
|
|
28
|
-
return str.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
29
|
-
},
|
|
30
|
-
padStart(str, length, padChar = ' ') {
|
|
31
|
-
return (String(str) || '').padStart(length, padChar);
|
|
32
|
-
},
|
|
33
|
-
padEnd(str, length, padChar = ' ') {
|
|
34
|
-
return (String(str) || '').padEnd(length, padChar);
|
|
35
|
-
},
|
|
36
|
-
truncate(str, maxLength, suffix = '...') {
|
|
37
|
-
if (!str || str.length <= maxLength) return str || '';
|
|
38
|
-
return str.slice(0, maxLength) + suffix;
|
|
39
|
-
},
|
|
40
|
-
replaceAll(str, search, replacement) {
|
|
41
|
-
if (!str) return '';
|
|
42
|
-
return str.split(search).join(replacement);
|
|
43
|
-
},
|
|
44
|
-
format(template, data) {
|
|
45
|
-
if (!template) return '';
|
|
46
|
-
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => data[key] !== undefined ? data[key] : `{{${key}}}`);
|
|
47
|
-
},
|
|
48
|
-
startsWith(str, prefix) {
|
|
49
|
-
return (str || '').startsWith(prefix);
|
|
50
|
-
},
|
|
51
|
-
endsWith(str, suffix) {
|
|
52
|
-
return (str || '').endsWith(suffix);
|
|
53
|
-
},
|
|
54
|
-
includes(str, search) {
|
|
55
|
-
return (str || '').includes(search);
|
|
56
|
-
},
|
|
57
|
-
repeat(str, times) {
|
|
58
|
-
return (str || '').repeat(times);
|
|
59
|
-
},
|
|
60
|
-
reverse(str) {
|
|
61
|
-
return (str || '').split('').reverse().join('');
|
|
62
|
-
},
|
|
63
|
-
countOccurrences(str, search) {
|
|
64
|
-
if (!str || !search) return 0;
|
|
65
|
-
return str.split(search).length - 1;
|
|
66
|
-
},
|
|
67
|
-
escapeHtml(str) {
|
|
68
|
-
if (!str) return '';
|
|
69
|
-
const div = document.createElement('div');
|
|
70
|
-
div.textContent = str;
|
|
71
|
-
return div.innerHTML;
|
|
72
|
-
},
|
|
73
|
-
unescapeHtml(str) {
|
|
74
|
-
if (!str) return '';
|
|
75
|
-
const div = document.createElement('div');
|
|
76
|
-
div.innerHTML = str;
|
|
77
|
-
return div.textContent;
|
|
78
|
-
},
|
|
79
|
-
generateRandom(length = 8) {
|
|
80
|
-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
81
|
-
let result = '';
|
|
82
|
-
for (let i = 0; i < length; i++) {
|
|
83
|
-
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
84
|
-
}
|
|
85
|
-
return result;
|
|
86
|
-
},
|
|
87
|
-
generateUUID() {
|
|
88
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
89
|
-
const r = Math.random() * 16 | 0;
|
|
90
|
-
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
91
|
-
return v.toString(16);
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
},
|
|
1
|
+
// Prototype Pollution guard: reject keys that can access the prototype chain
|
|
2
|
+
const _UNSAFE_KEYS = new Set(['__proto__', 'prototype', 'constructor']);
|
|
3
|
+
function _isUnsafeKey(key) {
|
|
4
|
+
return _UNSAFE_KEYS.has(key);
|
|
5
|
+
}
|
|
95
6
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
},
|
|
121
|
-
join(arr, separator = ',') {
|
|
122
|
-
return arr ? arr.join(separator) : '';
|
|
123
|
-
},
|
|
124
|
-
indexOf(arr, item, fromIndex = 0) {
|
|
125
|
-
return arr ? arr.indexOf(item, fromIndex) : -1;
|
|
126
|
-
},
|
|
127
|
-
lastIndexOf(arr, item, fromIndex) {
|
|
128
|
-
return arr ? arr.lastIndexOf(item, fromIndex) : -1;
|
|
129
|
-
},
|
|
130
|
-
includes(arr, item) {
|
|
131
|
-
return arr ? arr.includes(item) : false;
|
|
132
|
-
},
|
|
133
|
-
push(arr, ...items) {
|
|
134
|
-
if (arr) arr.push(...items);
|
|
135
|
-
return arr;
|
|
136
|
-
},
|
|
137
|
-
pop(arr) {
|
|
138
|
-
return arr ? arr.pop() : undefined;
|
|
139
|
-
},
|
|
140
|
-
shift(arr) {
|
|
141
|
-
return arr ? arr.shift() : undefined;
|
|
142
|
-
},
|
|
143
|
-
unshift(arr, ...items) {
|
|
144
|
-
if (arr) arr.unshift(...items);
|
|
145
|
-
return arr;
|
|
146
|
-
},
|
|
147
|
-
remove(arr, item) {
|
|
148
|
-
if (!arr) return arr;
|
|
149
|
-
const index = arr.indexOf(item);
|
|
150
|
-
if (index > -1) arr.splice(index, 1);
|
|
151
|
-
return arr;
|
|
152
|
-
},
|
|
153
|
-
removeAt(arr, index) {
|
|
154
|
-
if (!arr || index < 0 || index >= arr.length) return arr;
|
|
155
|
-
arr.splice(index, 1);
|
|
156
|
-
return arr;
|
|
157
|
-
},
|
|
158
|
-
insert(arr, index, item) {
|
|
159
|
-
if (!arr) return arr;
|
|
160
|
-
arr.splice(index, 0, item);
|
|
161
|
-
return arr;
|
|
162
|
-
},
|
|
163
|
-
reverse(arr) {
|
|
164
|
-
return arr ? arr.slice().reverse() : [];
|
|
165
|
-
},
|
|
166
|
-
sort(arr, compareFn) {
|
|
167
|
-
return arr ? arr.slice().sort(compareFn) : [];
|
|
168
|
-
},
|
|
169
|
-
sortBy(arr, key, order = 'asc') {
|
|
170
|
-
if (!arr) return [];
|
|
171
|
-
return arr.slice().sort((a, b) => {
|
|
172
|
-
const valA = typeof a === 'object' ? a[key] : a;
|
|
173
|
-
const valB = typeof b === 'object' ? b[key] : b;
|
|
174
|
-
if (valA < valB) return order === 'asc' ? -1 : 1;
|
|
175
|
-
if (valA > valB) return order === 'asc' ? 1 : -1;
|
|
176
|
-
return 0;
|
|
177
|
-
});
|
|
178
|
-
},
|
|
179
|
-
filter(arr, predicate) {
|
|
180
|
-
return arr ? arr.filter(predicate) : [];
|
|
181
|
-
},
|
|
182
|
-
map(arr, fn) {
|
|
183
|
-
return arr ? arr.map(fn) : [];
|
|
184
|
-
},
|
|
185
|
-
reduce(arr, fn, initialValue) {
|
|
186
|
-
return arr ? arr.reduce(fn, initialValue) : initialValue;
|
|
187
|
-
},
|
|
188
|
-
forEach(arr, fn) {
|
|
189
|
-
if (arr) arr.forEach(fn);
|
|
190
|
-
},
|
|
191
|
-
every(arr, predicate) {
|
|
192
|
-
return arr ? arr.every(predicate) : true;
|
|
193
|
-
},
|
|
194
|
-
some(arr, predicate) {
|
|
195
|
-
return arr ? arr.some(predicate) : false;
|
|
196
|
-
},
|
|
197
|
-
find(arr, predicate) {
|
|
198
|
-
return arr ? arr.find(predicate) : undefined;
|
|
199
|
-
},
|
|
200
|
-
findIndex(arr, predicate) {
|
|
201
|
-
return arr ? arr.findIndex(predicate) : -1;
|
|
202
|
-
},
|
|
203
|
-
flat(arr, depth = 1) {
|
|
204
|
-
return arr ? arr.flat(depth) : [];
|
|
205
|
-
},
|
|
206
|
-
flattenDeep(arr) {
|
|
207
|
-
return arr ? arr.reduce((acc, item) =>
|
|
208
|
-
Array.isArray(item) ? acc.concat(this.flattenDeep(item)) : acc.concat(item), []) : [];
|
|
209
|
-
},
|
|
210
|
-
unique(arr) {
|
|
211
|
-
return arr ? [...new Set(arr)] : [];
|
|
212
|
-
},
|
|
213
|
-
uniqueBy(arr, key) {
|
|
214
|
-
if (!arr) return [];
|
|
215
|
-
const seen = new Set();
|
|
216
|
-
return arr.filter(item => {
|
|
217
|
-
const value = typeof item === 'object' ? item[key] : item;
|
|
218
|
-
if (seen.has(value)) return false;
|
|
219
|
-
seen.add(value);
|
|
220
|
-
return true;
|
|
221
|
-
});
|
|
222
|
-
},
|
|
223
|
-
chunk(arr, size) {
|
|
224
|
-
if (!arr || size <= 0) return [];
|
|
225
|
-
const chunks = [];
|
|
226
|
-
for (let i = 0; i < arr.length; i += size) {
|
|
227
|
-
chunks.push(arr.slice(i, i + size));
|
|
228
|
-
}
|
|
229
|
-
return chunks;
|
|
230
|
-
},
|
|
231
|
-
shuffle(arr) {
|
|
232
|
-
if (!arr) return [];
|
|
233
|
-
const result = arr.slice();
|
|
234
|
-
for (let i = result.length - 1; i > 0; i--) {
|
|
235
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
236
|
-
[result[i], result[j]] = [result[j], result[i]];
|
|
237
|
-
}
|
|
238
|
-
return result;
|
|
239
|
-
},
|
|
240
|
-
sum(arr) {
|
|
241
|
-
return arr ? arr.reduce((acc, val) => acc + (Number(val) || 0), 0) : 0;
|
|
242
|
-
},
|
|
243
|
-
average(arr) {
|
|
244
|
-
if (!arr || arr.length === 0) return 0;
|
|
245
|
-
return this.sum(arr) / arr.length;
|
|
246
|
-
},
|
|
247
|
-
max(arr) {
|
|
248
|
-
return arr && arr.length > 0 ? Math.max(...arr) : -Infinity;
|
|
249
|
-
},
|
|
250
|
-
min(arr) {
|
|
251
|
-
return arr && arr.length > 0 ? Math.min(...arr) : Infinity;
|
|
252
|
-
},
|
|
253
|
-
intersection(...args) {
|
|
254
|
-
if (args.length === 0) return [];
|
|
255
|
-
return args.reduce((result, arr) =>
|
|
256
|
-
result.filter(item => arr && arr.includes(item)));
|
|
257
|
-
},
|
|
258
|
-
union(...args) {
|
|
259
|
-
return [...new Set(args.flat().filter(Boolean))];
|
|
260
|
-
},
|
|
261
|
-
difference(arr1, arr2) {
|
|
262
|
-
return arr1 ? arr1.filter(item => !arr2 || !arr2.includes(item)) : [];
|
|
263
|
-
},
|
|
264
|
-
zip(...args) {
|
|
265
|
-
if (args.length === 0) return [];
|
|
266
|
-
const maxLength = Math.max(...args.map(arr => arr ? arr.length : 0));
|
|
267
|
-
return Array.from({ length: maxLength }, (_, i) =>
|
|
268
|
-
args.map(arr => arr && arr[i]));
|
|
269
|
-
}
|
|
270
|
-
},
|
|
7
|
+
// === String Utilities ===
|
|
8
|
+
function trim(str) { return str ? str.trim() : ''; }
|
|
9
|
+
function trimLeft(str) { return str ? str.replace(/^\s+/, '') : ''; }
|
|
10
|
+
function trimRight(str) { return str ? str.replace(/\s+$/, '') : ''; }
|
|
11
|
+
function toUpperCase(str) { return str ? str.toUpperCase() : ''; }
|
|
12
|
+
function toLowerCase(str) { return str ? str.toLowerCase() : ''; }
|
|
13
|
+
function capitalize(str) { if (!str) return ''; return str.charAt(0).toUpperCase() + str.slice(1); }
|
|
14
|
+
function camelize(str) { if (!str) return ''; return str.replace(/-(\w)/g, (_, c) => c ? c.toUpperCase() : ''); }
|
|
15
|
+
function hyphenate(str) { if (!str) return ''; return str.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, ''); }
|
|
16
|
+
function padStart(str, length, padChar = ' ') { return (String(str) || '').padStart(length, padChar); }
|
|
17
|
+
function padEnd(str, length, padChar = ' ') { return (String(str) || '').padEnd(length, padChar); }
|
|
18
|
+
function truncate(str, maxLength, suffix = '...') { if (!str || str.length <= maxLength) return str || ''; return str.slice(0, maxLength) + suffix; }
|
|
19
|
+
function replaceAll(str, search, replacement) { if (!str) return ''; return str.split(search).join(replacement); }
|
|
20
|
+
function format(template, data) { if (!template) return ''; return template.replace(/\{\{(\w+)\}\}/g, (_, key) => data[key] !== undefined ? data[key] : `{{${key}}}`); }
|
|
21
|
+
function startsWith(str, prefix) { return (str || '').startsWith(prefix); }
|
|
22
|
+
function endsWith(str, suffix) { return (str || '').endsWith(suffix); }
|
|
23
|
+
function includesStr(str, search) { return (str || '').includes(search); }
|
|
24
|
+
function repeat(str, times) { return (str || '').repeat(times); }
|
|
25
|
+
function reverse(str) { return (str || '').split('').reverse().join(''); }
|
|
26
|
+
function countOccurrences(str, search) { if (!str || !search) return 0; return str.split(search).length - 1; }
|
|
27
|
+
function escapeHtml(str) { if (!str) return ''; const div = document.createElement('div'); div.textContent = str; return div.innerHTML; }
|
|
28
|
+
function unescapeHtml(str) { if (!str) return ''; const div = document.createElement('div'); div.innerHTML = str; return div.textContent; }
|
|
29
|
+
function generateRandom(length = 8) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; }
|
|
30
|
+
function generateUUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); }
|
|
271
31
|
|
|
272
|
-
|
|
273
|
-
isObject(obj) {
|
|
274
|
-
return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
|
|
275
|
-
},
|
|
276
|
-
isEmpty(obj) {
|
|
277
|
-
if (!obj || typeof obj !== 'object') return true;
|
|
278
|
-
return Object.keys(obj).length === 0;
|
|
279
|
-
},
|
|
280
|
-
keys(obj) {
|
|
281
|
-
return obj ? Object.keys(obj) : [];
|
|
282
|
-
},
|
|
283
|
-
values(obj) {
|
|
284
|
-
return obj ? Object.values(obj) : [];
|
|
285
|
-
},
|
|
286
|
-
entries(obj) {
|
|
287
|
-
return obj ? Object.entries(obj) : [];
|
|
288
|
-
},
|
|
289
|
-
has(obj, key) {
|
|
290
|
-
return obj ? Object.prototype.hasOwnProperty.call(obj, key) : false;
|
|
291
|
-
},
|
|
292
|
-
get(obj, path, defaultValue) {
|
|
293
|
-
if (!obj) return defaultValue;
|
|
294
|
-
const keys = path.split('.');
|
|
295
|
-
return keys.reduce((current, key) => current && current[key], obj) || defaultValue;
|
|
296
|
-
},
|
|
297
|
-
set(obj, path, value) {
|
|
298
|
-
if (!obj || typeof obj !== 'object') return obj;
|
|
299
|
-
const keys = path.split('.');
|
|
300
|
-
const lastKey = keys.pop();
|
|
301
|
-
let current = obj;
|
|
302
|
-
keys.forEach(key => {
|
|
303
|
-
if (!current[key]) current[key] = {};
|
|
304
|
-
current = current[key];
|
|
305
|
-
});
|
|
306
|
-
current[lastKey] = value;
|
|
307
|
-
return obj;
|
|
308
|
-
},
|
|
309
|
-
pick(obj, keys) {
|
|
310
|
-
if (!obj) return {};
|
|
311
|
-
return keys.reduce((result, key) => {
|
|
312
|
-
if (obj[key] !== undefined) result[key] = obj[key];
|
|
313
|
-
return result;
|
|
314
|
-
}, {});
|
|
315
|
-
},
|
|
316
|
-
omit(obj, keys) {
|
|
317
|
-
if (!obj) return {};
|
|
318
|
-
return Object.keys(obj).reduce((result, key) => {
|
|
319
|
-
if (!keys.includes(key)) result[key] = obj[key];
|
|
320
|
-
return result;
|
|
321
|
-
}, {});
|
|
322
|
-
},
|
|
323
|
-
merge(...args) {
|
|
324
|
-
return args.reduce((result, obj) => {
|
|
325
|
-
if (obj && typeof obj === 'object') {
|
|
326
|
-
Object.keys(obj).forEach(key => {
|
|
327
|
-
if (this.isObject(obj[key]) && this.isObject(result[key])) {
|
|
328
|
-
result[key] = this.merge(result[key], obj[key]);
|
|
329
|
-
} else {
|
|
330
|
-
result[key] = obj[key];
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
return result;
|
|
335
|
-
}, {});
|
|
336
|
-
},
|
|
337
|
-
clone(obj) {
|
|
338
|
-
return obj ? JSON.parse(JSON.stringify(obj)) : obj;
|
|
339
|
-
},
|
|
340
|
-
deepClone(obj, hash = new WeakMap()) {
|
|
341
|
-
if (!obj || typeof obj !== 'object') return obj;
|
|
342
|
-
if (hash.has(obj)) return hash.get(obj);
|
|
343
|
-
if (obj instanceof Date) return new Date(obj);
|
|
344
|
-
if (obj instanceof RegExp) return new RegExp(obj);
|
|
345
|
-
if (obj instanceof Map) {
|
|
346
|
-
const map = new Map();
|
|
347
|
-
hash.set(obj, map);
|
|
348
|
-
obj.forEach((val, key) => map.set(key, this.deepClone(val, hash)));
|
|
349
|
-
return map;
|
|
350
|
-
}
|
|
351
|
-
if (obj instanceof Set) {
|
|
352
|
-
const set = new Set();
|
|
353
|
-
hash.set(obj, set);
|
|
354
|
-
obj.forEach(val => set.add(this.deepClone(val, hash)));
|
|
355
|
-
return set;
|
|
356
|
-
}
|
|
357
|
-
if (Array.isArray(obj)) {
|
|
358
|
-
const arr = [];
|
|
359
|
-
hash.set(obj, arr);
|
|
360
|
-
obj.forEach(item => arr.push(this.deepClone(item, hash)));
|
|
361
|
-
return arr;
|
|
362
|
-
}
|
|
363
|
-
const clone = {};
|
|
364
|
-
hash.set(obj, clone);
|
|
365
|
-
Object.keys(obj).forEach(key => {
|
|
366
|
-
clone[key] = this.deepClone(obj[key], hash);
|
|
367
|
-
});
|
|
368
|
-
return clone;
|
|
369
|
-
},
|
|
370
|
-
forEach(obj, fn) {
|
|
371
|
-
if (!obj) return;
|
|
372
|
-
Object.keys(obj).forEach(key => fn(obj[key], key, obj));
|
|
373
|
-
},
|
|
374
|
-
map(obj, fn) {
|
|
375
|
-
if (!obj) return {};
|
|
376
|
-
const result = {};
|
|
377
|
-
Object.keys(obj).forEach(key => {
|
|
378
|
-
result[key] = fn(obj[key], key, obj);
|
|
379
|
-
});
|
|
380
|
-
return result;
|
|
381
|
-
},
|
|
382
|
-
filter(obj, fn) {
|
|
383
|
-
if (!obj) return {};
|
|
384
|
-
const result = {};
|
|
385
|
-
Object.keys(obj).forEach(key => {
|
|
386
|
-
if (fn(obj[key], key, obj)) result[key] = obj[key];
|
|
387
|
-
});
|
|
388
|
-
return result;
|
|
389
|
-
},
|
|
390
|
-
reduce(obj, fn, initialValue) {
|
|
391
|
-
if (!obj) return initialValue;
|
|
392
|
-
return Object.keys(obj).reduce((acc, key) =>
|
|
393
|
-
fn(acc, obj[key], key, obj), initialValue);
|
|
394
|
-
},
|
|
395
|
-
toArray(obj) {
|
|
396
|
-
if (!obj) return [];
|
|
397
|
-
return Object.keys(obj).map(key => ({ key, value: obj[key] }));
|
|
398
|
-
},
|
|
399
|
-
fromArray(arr, keyField, valueField) {
|
|
400
|
-
if (!arr) return {};
|
|
401
|
-
return arr.reduce((result, item) => {
|
|
402
|
-
const key = typeof item === 'object' ? item[keyField] : item;
|
|
403
|
-
const value = valueField ? item[valueField] : item;
|
|
404
|
-
if (key !== undefined) result[key] = value;
|
|
405
|
-
return result;
|
|
406
|
-
}, {});
|
|
407
|
-
},
|
|
408
|
-
size(obj) {
|
|
409
|
-
return obj ? Object.keys(obj).length : 0;
|
|
410
|
-
},
|
|
411
|
-
invert(obj) {
|
|
412
|
-
if (!obj) return {};
|
|
413
|
-
const result = {};
|
|
414
|
-
Object.keys(obj).forEach(key => {
|
|
415
|
-
result[obj[key]] = key;
|
|
416
|
-
});
|
|
417
|
-
return result;
|
|
418
|
-
},
|
|
419
|
-
isEqual(obj1, obj2) {
|
|
420
|
-
if (obj1 === obj2) return true;
|
|
421
|
-
if (!obj1 || !obj2 || typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;
|
|
422
|
-
const keys1 = Object.keys(obj1);
|
|
423
|
-
const keys2 = Object.keys(obj2);
|
|
424
|
-
if (keys1.length !== keys2.length) return false;
|
|
425
|
-
return keys1.every(key => this.isEqual(obj1[key], obj2[key]));
|
|
426
|
-
},
|
|
427
|
-
freeze(obj) {
|
|
428
|
-
if (!obj) return obj;
|
|
429
|
-
Object.freeze(obj);
|
|
430
|
-
Object.keys(obj).forEach(key => {
|
|
431
|
-
if (typeof obj[key] === 'object') this.freeze(obj[key]);
|
|
432
|
-
});
|
|
433
|
-
return obj;
|
|
434
|
-
},
|
|
435
|
-
seal(obj) {
|
|
436
|
-
return obj ? Object.seal(obj) : obj;
|
|
437
|
-
}
|
|
438
|
-
},
|
|
32
|
+
const stringUtils = { trim, trimLeft, trimRight, toUpperCase, toLowerCase, capitalize, camelize, hyphenate, padStart, padEnd, truncate, replaceAll, format, startsWith, endsWith, includes: includesStr, repeat, reverse, countOccurrences, escapeHtml, unescapeHtml, generateRandom, generateUUID };
|
|
439
33
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
sum(...args) {
|
|
486
|
-
const nums = args.flat().filter(this.isNumber);
|
|
487
|
-
return nums.reduce((acc, val) => acc + val, 0);
|
|
488
|
-
},
|
|
489
|
-
average(...args) {
|
|
490
|
-
const nums = args.flat().filter(this.isNumber);
|
|
491
|
-
return nums.length > 0 ? this.sum(nums) / nums.length : 0;
|
|
492
|
-
},
|
|
493
|
-
random(min = 0, max = 1) {
|
|
494
|
-
return Math.random() * (max - min) + min;
|
|
495
|
-
},
|
|
496
|
-
randomInt(min, max) {
|
|
497
|
-
return Math.floor(this.random(min, max + 1));
|
|
498
|
-
},
|
|
499
|
-
format(val, decimals = 2) {
|
|
500
|
-
if (!this.isNumber(val)) return String(val);
|
|
501
|
-
return val.toFixed(decimals);
|
|
502
|
-
},
|
|
503
|
-
formatCurrency(val, currency = 'CNY', decimals = 2) {
|
|
504
|
-
if (!this.isNumber(val)) return String(val);
|
|
505
|
-
const formatter = new Intl.NumberFormat('zh-CN', {
|
|
506
|
-
style: 'currency',
|
|
507
|
-
currency,
|
|
508
|
-
minimumFractionDigits: decimals,
|
|
509
|
-
maximumFractionDigits: decimals
|
|
510
|
-
});
|
|
511
|
-
return formatter.format(val);
|
|
512
|
-
},
|
|
513
|
-
formatPercent(val, decimals = 0) {
|
|
514
|
-
if (!this.isNumber(val)) return String(val);
|
|
515
|
-
return `${(val * 100).toFixed(decimals)}%`;
|
|
516
|
-
},
|
|
517
|
-
toFixed(val, decimals = 0) {
|
|
518
|
-
if (!this.isNumber(val)) return String(val);
|
|
519
|
-
return val.toFixed(decimals);
|
|
520
|
-
},
|
|
521
|
-
toPrecision(val, precision = 6) {
|
|
522
|
-
if (!this.isNumber(val)) return String(val);
|
|
523
|
-
return val.toPrecision(precision);
|
|
524
|
-
},
|
|
525
|
-
isNaN(val) {
|
|
526
|
-
return Number.isNaN(val);
|
|
527
|
-
},
|
|
528
|
-
isFinite(val) {
|
|
529
|
-
return Number.isFinite(val);
|
|
530
|
-
},
|
|
531
|
-
parseInt(val, radix = 10) {
|
|
532
|
-
return Number.parseInt(val, radix);
|
|
533
|
-
},
|
|
534
|
-
parseFloat(val) {
|
|
535
|
-
return Number.parseFloat(val);
|
|
536
|
-
},
|
|
537
|
-
toNumber(val, defaultValue = 0) {
|
|
538
|
-
const num = Number(val);
|
|
539
|
-
return isNaN(num) ? defaultValue : num;
|
|
540
|
-
},
|
|
541
|
-
safeDivide(a, b, defaultValue = 0) {
|
|
542
|
-
if (!this.isNumber(a) || !this.isNumber(b) || b === 0) return defaultValue;
|
|
543
|
-
return a / b;
|
|
544
|
-
},
|
|
545
|
-
safeMultiply(...args) {
|
|
546
|
-
return args.reduce((result, val) => {
|
|
547
|
-
if (!this.isNumber(result) || !this.isNumber(val)) return 0;
|
|
548
|
-
return result * val;
|
|
549
|
-
}, 1);
|
|
550
|
-
}
|
|
551
|
-
},
|
|
34
|
+
// === Array Utilities ===
|
|
35
|
+
function isArray(arr) { return Array.isArray(arr); }
|
|
36
|
+
function isEmpty(arr) { return !arr || arr.length === 0; }
|
|
37
|
+
function size(arr) { return arr ? arr.length : 0; }
|
|
38
|
+
function first(arr, defaultValue) { return arr && arr.length > 0 ? arr[0] : defaultValue; }
|
|
39
|
+
function last(arr, defaultValue) { return arr && arr.length > 0 ? arr[arr.length - 1] : defaultValue; }
|
|
40
|
+
function get(arr, index, defaultValue) { return arr && arr[index] !== undefined ? arr[index] : defaultValue; }
|
|
41
|
+
function slice(arr, start, end) { return arr ? arr.slice(start, end) : []; }
|
|
42
|
+
function concat(...args) { return args.reduce((result, arg) => result.concat(arg || []), []); }
|
|
43
|
+
function join(arr, separator = ',') { return arr ? arr.join(separator) : ''; }
|
|
44
|
+
function indexOf(arr, item, fromIndex = 0) { if (!arr) return -1; if (Number.isNaN(item)) { for (let i = fromIndex; i < arr.length; i++) { if (Number.isNaN(arr[i])) return i; } return -1; } return arr.indexOf(item, fromIndex); }
|
|
45
|
+
function lastIndexOf(arr, item, fromIndex) { if (!arr) return -1; if (Number.isNaN(item)) { const start = fromIndex !== undefined ? fromIndex : arr.length - 1; for (let i = start; i >= 0; i--) { if (Number.isNaN(arr[i])) return i; } return -1; } return arr.lastIndexOf(item, fromIndex); }
|
|
46
|
+
function includes(arr, item) { return arr ? arr.includes(item) : false; }
|
|
47
|
+
function push(arr, ...items) { if (arr) arr.push(...items); return arr; }
|
|
48
|
+
function pop(arr) { return arr ? arr.pop() : undefined; }
|
|
49
|
+
function shift(arr) { return arr ? arr.shift() : undefined; }
|
|
50
|
+
function unshift(arr, ...items) { if (arr) arr.unshift(...items); return arr; }
|
|
51
|
+
function remove(arr, item) { if (!arr) return arr; const index = Number.isNaN(item) ? arr.findIndex(v => Number.isNaN(v)) : arr.indexOf(item); if (index > -1) arr.splice(index, 1); return arr; }
|
|
52
|
+
function removeAt(arr, index) { if (!arr || index < 0 || index >= arr.length) return arr; arr.splice(index, 1); return arr; }
|
|
53
|
+
function insert(arr, index, item) { if (!arr) return arr; arr.splice(index, 0, item); return arr; }
|
|
54
|
+
function reverseArr(arr) { return arr ? arr.slice().reverse() : []; }
|
|
55
|
+
function sort(arr, compareFn) { return arr ? arr.slice().sort(compareFn) : []; }
|
|
56
|
+
function sortBy(arr, key, order = 'asc') { if (!arr) return []; return arr.slice().sort((a, b) => { const valA = typeof a === 'object' ? a[key] : a; const valB = typeof b === 'object' ? b[key] : b; if (valA < valB) return order === 'asc' ? -1 : 1; if (valA > valB) return order === 'asc' ? 1 : -1; return 0; }); }
|
|
57
|
+
function filter(arr, predicate) { return arr ? arr.filter(predicate) : []; }
|
|
58
|
+
function map(arr, fn) { return arr ? arr.map(fn) : []; }
|
|
59
|
+
function reduce(arr, fn, initialValue) { return arr ? arr.reduce(fn, initialValue) : initialValue; }
|
|
60
|
+
function forEach(arr, fn) { if (arr) arr.forEach(fn); }
|
|
61
|
+
function every(arr, predicate) { return arr ? arr.every(predicate) : true; }
|
|
62
|
+
function some(arr, predicate) { return arr ? arr.some(predicate) : false; }
|
|
63
|
+
function find(arr, predicate) { return arr ? arr.find(predicate) : undefined; }
|
|
64
|
+
function findIndex(arr, predicate) { return arr ? arr.findIndex(predicate) : -1; }
|
|
65
|
+
function flat(arr, depth = 1) { return arr ? arr.flat(depth) : []; }
|
|
66
|
+
function flattenDeep(arr) { return arr ? arr.reduce((acc, item) => Array.isArray(item) ? acc.concat(flattenDeep(item)) : acc.concat(item), []) : []; }
|
|
67
|
+
function unique(arr) { return arr ? [...new Set(arr)] : []; }
|
|
68
|
+
function uniqueBy(arr, key) { if (!arr) return []; const seen = new Set(); return arr.filter(item => { const value = typeof item === 'object' ? item[key] : item; if (seen.has(value)) return false; seen.add(value); return true; }); }
|
|
69
|
+
function chunk(arr, chunkSize) { if (!arr || chunkSize <= 0) return []; const chunks = []; for (let i = 0; i < arr.length; i += chunkSize) { chunks.push(arr.slice(i, i + chunkSize)); } return chunks; }
|
|
70
|
+
function shuffle(arr) { if (!arr) return []; const result = arr.slice(); for (let i = result.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [result[i], result[j]] = [result[j], result[i]]; } return result; }
|
|
71
|
+
function sum(arr) { return arr ? arr.reduce((acc, val) => acc + (Number(val) || 0), 0) : 0; }
|
|
72
|
+
function average(arr) { if (!arr || arr.length === 0) return 0; return sum(arr) / arr.length; }
|
|
73
|
+
function max(arr) { return arr && arr.length > 0 ? Math.max(...arr) : -Infinity; }
|
|
74
|
+
function min(arr) { return arr && arr.length > 0 ? Math.min(...arr) : Infinity; }
|
|
75
|
+
function intersection(...args) { if (args.length === 0) return []; return args.reduce((result, a) => result.filter(item => a && a.includes(item))); }
|
|
76
|
+
function union(...args) { return [...new Set(args.flat().filter(Boolean))]; }
|
|
77
|
+
function difference(arr1, arr2) { return arr1 ? arr1.filter(item => !arr2 || !arr2.includes(item)) : []; }
|
|
78
|
+
function zip(...args) { if (args.length === 0) return []; const maxLength = Math.max(...args.map(a => a ? a.length : 0)); return Array.from({ length: maxLength }, (_, i) => args.map(a => a && a[i])); }
|
|
552
79
|
|
|
553
|
-
|
|
554
|
-
now() {
|
|
555
|
-
return Date.now();
|
|
556
|
-
},
|
|
557
|
-
today() {
|
|
558
|
-
const now = new Date();
|
|
559
|
-
now.setHours(0, 0, 0, 0);
|
|
560
|
-
return now;
|
|
561
|
-
},
|
|
562
|
-
tomorrow() {
|
|
563
|
-
const date = this.today();
|
|
564
|
-
date.setDate(date.getDate() + 1);
|
|
565
|
-
return date;
|
|
566
|
-
},
|
|
567
|
-
yesterday() {
|
|
568
|
-
const date = this.today();
|
|
569
|
-
date.setDate(date.getDate() - 1);
|
|
570
|
-
return date;
|
|
571
|
-
},
|
|
572
|
-
isDate(val) {
|
|
573
|
-
return val instanceof Date && !isNaN(val.getTime());
|
|
574
|
-
},
|
|
575
|
-
isValid(date) {
|
|
576
|
-
return this.isDate(date);
|
|
577
|
-
},
|
|
578
|
-
parse(str) {
|
|
579
|
-
const date = new Date(str);
|
|
580
|
-
return this.isValid(date) ? date : null;
|
|
581
|
-
},
|
|
582
|
-
format(date, formatStr = 'YYYY-MM-DD HH:mm:ss') {
|
|
583
|
-
if (!this.isDate(date)) return '';
|
|
584
|
-
const year = date.getFullYear();
|
|
585
|
-
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
586
|
-
const day = String(date.getDate()).padStart(2, '0');
|
|
587
|
-
const hours = String(date.getHours()).padStart(2, '0');
|
|
588
|
-
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
589
|
-
const seconds = String(date.getSeconds()).padStart(2, '0');
|
|
590
|
-
const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
|
|
591
|
-
const weekDays = ['日', '一', '二', '三', '四', '五', '六'];
|
|
592
|
-
const weekDay = weekDays[date.getDay()];
|
|
593
|
-
|
|
594
|
-
return formatStr
|
|
595
|
-
.replace('YYYY', year)
|
|
596
|
-
.replace('MM', month)
|
|
597
|
-
.replace('DD', day)
|
|
598
|
-
.replace('HH', hours)
|
|
599
|
-
.replace('mm', minutes)
|
|
600
|
-
.replace('ss', seconds)
|
|
601
|
-
.replace('SSS', milliseconds)
|
|
602
|
-
.replace('D', date.getDate())
|
|
603
|
-
.replace('M', date.getMonth() + 1)
|
|
604
|
-
.replace('H', date.getHours())
|
|
605
|
-
.replace('m', date.getMinutes())
|
|
606
|
-
.replace('s', date.getSeconds())
|
|
607
|
-
.replace('W', weekDay);
|
|
608
|
-
},
|
|
609
|
-
toISO(date) {
|
|
610
|
-
return this.isDate(date) ? date.toISOString() : '';
|
|
611
|
-
},
|
|
612
|
-
toUTC(date) {
|
|
613
|
-
return this.isDate(date) ? new Date(date.toUTCString()) : null;
|
|
614
|
-
},
|
|
615
|
-
addDays(date, days) {
|
|
616
|
-
if (!this.isDate(date)) return date;
|
|
617
|
-
const newDate = new Date(date);
|
|
618
|
-
newDate.setDate(newDate.getDate() + days);
|
|
619
|
-
return newDate;
|
|
620
|
-
},
|
|
621
|
-
addHours(date, hours) {
|
|
622
|
-
if (!this.isDate(date)) return date;
|
|
623
|
-
const newDate = new Date(date);
|
|
624
|
-
newDate.setHours(newDate.getHours() + hours);
|
|
625
|
-
return newDate;
|
|
626
|
-
},
|
|
627
|
-
addMinutes(date, minutes) {
|
|
628
|
-
if (!this.isDate(date)) return date;
|
|
629
|
-
const newDate = new Date(date);
|
|
630
|
-
newDate.setMinutes(newDate.getMinutes() + minutes);
|
|
631
|
-
return newDate;
|
|
632
|
-
},
|
|
633
|
-
addSeconds(date, seconds) {
|
|
634
|
-
if (!this.isDate(date)) return date;
|
|
635
|
-
const newDate = new Date(date);
|
|
636
|
-
newDate.setSeconds(newDate.getSeconds() + seconds);
|
|
637
|
-
return newDate;
|
|
638
|
-
},
|
|
639
|
-
diffDays(date1, date2) {
|
|
640
|
-
if (!this.isDate(date1) || !this.isDate(date2)) return 0;
|
|
641
|
-
const d1 = this.today();
|
|
642
|
-
const d2 = this.today();
|
|
643
|
-
d1.setTime(date1.getTime());
|
|
644
|
-
d2.setTime(date2.getTime());
|
|
645
|
-
return Math.floor((d1.getTime() - d2.getTime()) / (1000 * 60 * 60 * 24));
|
|
646
|
-
},
|
|
647
|
-
diffHours(date1, date2) {
|
|
648
|
-
if (!this.isDate(date1) || !this.isDate(date2)) return 0;
|
|
649
|
-
return Math.floor((date1.getTime() - date2.getTime()) / (1000 * 60 * 60));
|
|
650
|
-
},
|
|
651
|
-
diffMinutes(date1, date2) {
|
|
652
|
-
if (!this.isDate(date1) || !this.isDate(date2)) return 0;
|
|
653
|
-
return Math.floor((date1.getTime() - date2.getTime()) / (1000 * 60));
|
|
654
|
-
},
|
|
655
|
-
diffSeconds(date1, date2) {
|
|
656
|
-
if (!this.isDate(date1) || !this.isDate(date2)) return 0;
|
|
657
|
-
return Math.floor((date1.getTime() - date2.getTime()) / 1000);
|
|
658
|
-
},
|
|
659
|
-
isToday(date) {
|
|
660
|
-
if (!this.isDate(date)) return false;
|
|
661
|
-
return this.diffDays(date, this.today()) === 0;
|
|
662
|
-
},
|
|
663
|
-
isYesterday(date) {
|
|
664
|
-
if (!this.isDate(date)) return false;
|
|
665
|
-
return this.diffDays(date, this.today()) === -1;
|
|
666
|
-
},
|
|
667
|
-
isTomorrow(date) {
|
|
668
|
-
if (!this.isDate(date)) return false;
|
|
669
|
-
return this.diffDays(date, this.today()) === 1;
|
|
670
|
-
},
|
|
671
|
-
isFuture(date) {
|
|
672
|
-
if (!this.isDate(date)) return false;
|
|
673
|
-
return date.getTime() > this.now();
|
|
674
|
-
},
|
|
675
|
-
isPast(date) {
|
|
676
|
-
if (!this.isDate(date)) return false;
|
|
677
|
-
return date.getTime() < this.now();
|
|
678
|
-
},
|
|
679
|
-
isLeapYear(date) {
|
|
680
|
-
if (!this.isDate(date)) return false;
|
|
681
|
-
const year = date.getFullYear();
|
|
682
|
-
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
683
|
-
},
|
|
684
|
-
getDaysInMonth(date) {
|
|
685
|
-
if (!this.isDate(date)) return 0;
|
|
686
|
-
const year = date.getFullYear();
|
|
687
|
-
const month = date.getMonth();
|
|
688
|
-
return new Date(year, month + 1, 0).getDate();
|
|
689
|
-
},
|
|
690
|
-
getWeekOfYear(date) {
|
|
691
|
-
if (!this.isDate(date)) return 0;
|
|
692
|
-
const startOfYear = new Date(date.getFullYear(), 0, 1);
|
|
693
|
-
const diff = date.getTime() - startOfYear.getTime();
|
|
694
|
-
return Math.ceil(diff / (1000 * 60 * 60 * 24 * 7));
|
|
695
|
-
},
|
|
696
|
-
getQuarter(date) {
|
|
697
|
-
if (!this.isDate(date)) return 0;
|
|
698
|
-
return Math.ceil((date.getMonth() + 1) / 3);
|
|
699
|
-
},
|
|
700
|
-
startOfDay(date) {
|
|
701
|
-
if (!this.isDate(date)) return date;
|
|
702
|
-
const newDate = new Date(date);
|
|
703
|
-
newDate.setHours(0, 0, 0, 0);
|
|
704
|
-
return newDate;
|
|
705
|
-
},
|
|
706
|
-
endOfDay(date) {
|
|
707
|
-
if (!this.isDate(date)) return date;
|
|
708
|
-
const newDate = new Date(date);
|
|
709
|
-
newDate.setHours(23, 59, 59, 999);
|
|
710
|
-
return newDate;
|
|
711
|
-
},
|
|
712
|
-
startOfMonth(date) {
|
|
713
|
-
if (!this.isDate(date)) return date;
|
|
714
|
-
return new Date(date.getFullYear(), date.getMonth(), 1);
|
|
715
|
-
},
|
|
716
|
-
endOfMonth(date) {
|
|
717
|
-
if (!this.isDate(date)) return date;
|
|
718
|
-
return new Date(date.getFullYear(), date.getMonth() + 1, 0, 23, 59, 59, 999);
|
|
719
|
-
},
|
|
720
|
-
startOfWeek(date, startDay = 1) {
|
|
721
|
-
if (!this.isDate(date)) return date;
|
|
722
|
-
const newDate = new Date(date);
|
|
723
|
-
const day = newDate.getDay();
|
|
724
|
-
const diff = day >= startDay ? day - startDay : day + (7 - startDay);
|
|
725
|
-
newDate.setDate(newDate.getDate() - diff);
|
|
726
|
-
newDate.setHours(0, 0, 0, 0);
|
|
727
|
-
return newDate;
|
|
728
|
-
},
|
|
729
|
-
endOfWeek(date, startDay = 1) {
|
|
730
|
-
if (!this.isDate(date)) return date;
|
|
731
|
-
const start = this.startOfWeek(date, startDay);
|
|
732
|
-
const end = new Date(start);
|
|
733
|
-
end.setDate(end.getDate() + 6);
|
|
734
|
-
end.setHours(23, 59, 59, 999);
|
|
735
|
-
return end;
|
|
736
|
-
},
|
|
737
|
-
getAge(birthDate) {
|
|
738
|
-
if (!this.isDate(birthDate)) return 0;
|
|
739
|
-
const now = new Date();
|
|
740
|
-
let age = now.getFullYear() - birthDate.getFullYear();
|
|
741
|
-
if (now.getMonth() < birthDate.getMonth() ||
|
|
742
|
-
(now.getMonth() === birthDate.getMonth() && now.getDate() < birthDate.getDate())) {
|
|
743
|
-
age--;
|
|
744
|
-
}
|
|
745
|
-
return Math.max(0, age);
|
|
746
|
-
},
|
|
747
|
-
fromNow(date) {
|
|
748
|
-
if (!this.isDate(date)) return '';
|
|
749
|
-
const now = this.now();
|
|
750
|
-
const diff = now - date.getTime();
|
|
751
|
-
const minute = 60 * 1000;
|
|
752
|
-
const hour = 60 * minute;
|
|
753
|
-
const day = 24 * hour;
|
|
754
|
-
const week = 7 * day;
|
|
755
|
-
const month = 30 * day;
|
|
756
|
-
const year = 365 * day;
|
|
757
|
-
|
|
758
|
-
if (diff < minute) return '刚刚';
|
|
759
|
-
if (diff < hour) return `${Math.floor(diff / minute)}分钟前`;
|
|
760
|
-
if (diff < day) return `${Math.floor(diff / hour)}小时前`;
|
|
761
|
-
if (diff < week) return `${Math.floor(diff / day)}天前`;
|
|
762
|
-
if (diff < month) return `${Math.floor(diff / week)}周前`;
|
|
763
|
-
if (diff < year) return `${Math.floor(diff / month)}个月前`;
|
|
764
|
-
return `${Math.floor(diff / year)}年前`;
|
|
765
|
-
}
|
|
766
|
-
},
|
|
80
|
+
const arrayUtils = { isArray, isEmpty, size, first, last, get, slice, concat, join, indexOf, lastIndexOf, includes, push, pop, shift, unshift, remove, removeAt, insert, reverse: reverseArr, sort, sortBy, filter, map, reduce, forEach, every, some, find, findIndex, flat, flattenDeep, unique, uniqueBy, chunk, shuffle, sum, average, max, min, intersection, union, difference, zip };
|
|
767
81
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
if (trailing && lastArgs) {
|
|
794
|
-
invokeFunc();
|
|
795
|
-
}
|
|
796
|
-
lastArgs = null;
|
|
797
|
-
lastThis = null;
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
function remainingWait() {
|
|
801
|
-
const timeSinceLastCall = Date.now() - lastCallTime;
|
|
802
|
-
return Math.max(0, wait - timeSinceLastCall);
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
return function(...args) {
|
|
806
|
-
lastArgs = args;
|
|
807
|
-
lastThis = this;
|
|
808
|
-
lastCallTime = Date.now();
|
|
809
|
-
|
|
810
|
-
if (!timeout) {
|
|
811
|
-
leadingEdge();
|
|
812
|
-
} else {
|
|
813
|
-
clearTimeout(timeout);
|
|
814
|
-
timeout = setTimeout(trailingEdge, remainingWait());
|
|
815
|
-
}
|
|
816
|
-
};
|
|
817
|
-
},
|
|
82
|
+
// === Object Utilities ===
|
|
83
|
+
function isObject(obj) { return obj !== null && typeof obj === 'object' && !Array.isArray(obj); }
|
|
84
|
+
function isEmptyObj(obj) { if (!obj || typeof obj !== 'object') return true; return Object.keys(obj).length === 0; }
|
|
85
|
+
function keys(obj) { return obj ? Object.keys(obj) : []; }
|
|
86
|
+
function values(obj) { return obj ? Object.values(obj) : []; }
|
|
87
|
+
function entries(obj) { return obj ? Object.entries(obj) : []; }
|
|
88
|
+
function has(obj, key) { return obj ? Object.prototype.hasOwnProperty.call(obj, key) : false; }
|
|
89
|
+
function getObj(obj, path, defaultValue) { if (!obj) return defaultValue; const k = path.split('.'); if (k.some(_isUnsafeKey)) return defaultValue; return k.reduce((current, key) => current && current[key], obj) ?? defaultValue; }
|
|
90
|
+
function setObj(obj, path, value) { if (!obj || typeof obj !== 'object') return obj; const k = path.split('.'); if (k.some(_isUnsafeKey)) return obj; const lastKey = k.pop(); let current = obj; k.forEach(key => { if (!current[key] || typeof current[key] !== 'object') current[key] = {}; current = current[key]; }); current[lastKey] = value; return obj; }
|
|
91
|
+
function pick(obj, keys) { if (!obj) return {}; return keys.reduce((result, key) => { if (obj[key] !== undefined) result[key] = obj[key]; return result; }, {}); }
|
|
92
|
+
function omit(obj, keys) { if (!obj) return {}; return Object.keys(obj).reduce((result, key) => { if (!keys.includes(key)) result[key] = obj[key]; return result; }, {}); }
|
|
93
|
+
function merge(...args) { return args.reduce((result, obj) => { if (obj && typeof obj === 'object') { Object.keys(obj).forEach(key => { if (_isUnsafeKey(key)) return; if (isObject(obj[key]) && isObject(result[key])) { result[key] = merge(result[key], obj[key]); } else { result[key] = obj[key]; } }); } return result; }, {}); }
|
|
94
|
+
function clone(obj) { return obj ? JSON.parse(JSON.stringify(obj)) : obj; }
|
|
95
|
+
function deepClone(obj, hash = new WeakMap()) { if (!obj || typeof obj !== 'object') return obj; if (hash.has(obj)) return hash.get(obj); if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); if (obj instanceof Map) { const m = new Map(); hash.set(obj, m); obj.forEach((val, key) => m.set(key, deepClone(val, hash))); return m; } if (obj instanceof Set) { const s = new Set(); hash.set(obj, s); obj.forEach(val => s.add(deepClone(val, hash))); return s; } if (Array.isArray(obj)) { const a = []; hash.set(obj, a); obj.forEach(item => a.push(deepClone(item, hash))); return a; } const c = {}; hash.set(obj, c); Object.keys(obj).forEach(key => { if (!_isUnsafeKey(key)) { c[key] = deepClone(obj[key], hash); } }); return c; }
|
|
96
|
+
function forEachObj(obj, fn) { if (!obj) return; Object.keys(obj).forEach(key => fn(obj[key], key, obj)); }
|
|
97
|
+
function mapObj(obj, fn) { if (!obj) return {}; const result = {}; Object.keys(obj).forEach(key => { result[key] = fn(obj[key], key, obj); }); return result; }
|
|
98
|
+
function filterObj(obj, fn) { if (!obj) return {}; const result = {}; Object.keys(obj).forEach(key => { if (fn(obj[key], key, obj)) result[key] = obj[key]; }); return result; }
|
|
99
|
+
function reduceObj(obj, fn, initialValue) { if (!obj) return initialValue; return Object.keys(obj).reduce((acc, key) => fn(acc, obj[key], key, obj), initialValue); }
|
|
100
|
+
function toArray(obj) { if (!obj) return []; return Object.keys(obj).map(key => ({ key, value: obj[key] })); }
|
|
101
|
+
function fromArray(arr, keyField, valueField) { if (!arr) return {}; return arr.reduce((result, item) => { const key = typeof item === 'object' ? item[keyField] : item; const value = valueField ? item[valueField] : item; if (key !== undefined) result[key] = value; return result; }, {}); }
|
|
102
|
+
function sizeObj(obj) { return obj ? Object.keys(obj).length : 0; }
|
|
103
|
+
function invert(obj) { if (!obj) return {}; const result = {}; Object.keys(obj).forEach(key => { result[obj[key]] = key; }); return result; }
|
|
104
|
+
function isEqual(obj1, obj2) { if (obj1 === obj2) return true; if (!obj1 || !obj2 || typeof obj1 !== 'object' || typeof obj2 !== 'object') return false; const keys1 = Object.keys(obj1); const keys2 = Object.keys(obj2); if (keys1.length !== keys2.length) return false; return keys1.every(key => isEqual(obj1[key], obj2[key])); }
|
|
105
|
+
function freeze(obj) { if (!obj) return obj; Object.freeze(obj); Object.keys(obj).forEach(key => { if (typeof obj[key] === 'object') freeze(obj[key]); }); return obj; }
|
|
106
|
+
function seal(obj) { return obj ? Object.seal(obj) : obj; }
|
|
818
107
|
|
|
819
|
-
|
|
820
|
-
let inThrottle = false;
|
|
821
|
-
const trailing = options.trailing || false;
|
|
822
|
-
let lastArgs = null;
|
|
823
|
-
let lastThis = null;
|
|
824
|
-
|
|
825
|
-
function invokeFunc() {
|
|
826
|
-
func.apply(lastThis, lastArgs);
|
|
827
|
-
lastArgs = null;
|
|
828
|
-
lastThis = null;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
return function(...args) {
|
|
832
|
-
if (!inThrottle) {
|
|
833
|
-
inThrottle = true;
|
|
834
|
-
lastArgs = args;
|
|
835
|
-
lastThis = this;
|
|
836
|
-
invokeFunc();
|
|
837
|
-
setTimeout(() => {
|
|
838
|
-
inThrottle = false;
|
|
839
|
-
if (trailing && lastArgs) {
|
|
840
|
-
invokeFunc();
|
|
841
|
-
}
|
|
842
|
-
}, limit);
|
|
843
|
-
} else if (trailing) {
|
|
844
|
-
lastArgs = args;
|
|
845
|
-
lastThis = this;
|
|
846
|
-
}
|
|
847
|
-
};
|
|
848
|
-
},
|
|
108
|
+
const objectUtils = { isObject, isEmpty: isEmptyObj, keys, values, entries, has, get: getObj, set: setObj, pick, omit, merge, clone, deepClone, forEach: forEachObj, map: mapObj, filter: filterObj, reduce: reduceObj, toArray, fromArray, size: sizeObj, invert, isEqual, freeze, seal };
|
|
849
109
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
return regex.test(str || '');
|
|
881
|
-
},
|
|
882
|
-
isCreditCard(str) {
|
|
883
|
-
const regex = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9]{2})[0-9]{12}|3[47][0-9]{13})$/;
|
|
884
|
-
const cleanStr = str.replace(/\s/g, '');
|
|
885
|
-
if (!regex.test(cleanStr)) return false;
|
|
886
|
-
let sum = 0;
|
|
887
|
-
let alternate = false;
|
|
888
|
-
for (let i = cleanStr.length - 1; i >= 0; i--) {
|
|
889
|
-
let digit = parseInt(cleanStr[i], 10);
|
|
890
|
-
if (alternate) {
|
|
891
|
-
digit *= 2;
|
|
892
|
-
if (digit > 9) digit -= 9;
|
|
893
|
-
}
|
|
894
|
-
sum += digit;
|
|
895
|
-
alternate = !alternate;
|
|
896
|
-
}
|
|
897
|
-
return sum % 10 === 0;
|
|
898
|
-
},
|
|
899
|
-
isHexColor(str) {
|
|
900
|
-
const regex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
|
|
901
|
-
return regex.test(str || '');
|
|
902
|
-
},
|
|
903
|
-
isRGB(str) {
|
|
904
|
-
const regex = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/;
|
|
905
|
-
const match = regex.exec(str || '');
|
|
906
|
-
if (!match) return false;
|
|
907
|
-
return match.slice(1).every(val => parseInt(val) >= 0 && parseInt(val) <= 255);
|
|
908
|
-
},
|
|
909
|
-
isRGBA(str) {
|
|
910
|
-
const regex = /^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*([01]|0\.\d+)\)$/;
|
|
911
|
-
const match = regex.exec(str || '');
|
|
912
|
-
if (!match) return false;
|
|
913
|
-
const [, r, g, b, a] = match;
|
|
914
|
-
return parseInt(r) >= 0 && parseInt(r) <= 255 &&
|
|
915
|
-
parseInt(g) >= 0 && parseInt(g) <= 255 &&
|
|
916
|
-
parseInt(b) >= 0 && parseInt(b) <= 255 &&
|
|
917
|
-
parseFloat(a) >= 0 && parseFloat(a) <= 1;
|
|
918
|
-
},
|
|
919
|
-
isColor(str) {
|
|
920
|
-
return this.isHexColor(str) || this.isRGB(str) || this.isRGBA(str);
|
|
921
|
-
},
|
|
922
|
-
isDate(str) {
|
|
923
|
-
return !isNaN(new Date(str).getTime());
|
|
924
|
-
},
|
|
925
|
-
isJSON(str) {
|
|
926
|
-
try {
|
|
927
|
-
JSON.parse(str);
|
|
928
|
-
return true;
|
|
929
|
-
} catch {
|
|
930
|
-
return false;
|
|
931
|
-
}
|
|
932
|
-
},
|
|
933
|
-
isEmpty(str) {
|
|
934
|
-
return !str || str.trim() === '';
|
|
935
|
-
},
|
|
936
|
-
isWhitespace(str) {
|
|
937
|
-
return /^\s+$/.test(str || '');
|
|
938
|
-
},
|
|
939
|
-
isNumber(str) {
|
|
940
|
-
return !isNaN(parseFloat(str)) && isFinite(str);
|
|
941
|
-
},
|
|
942
|
-
isInteger(str) {
|
|
943
|
-
return /^-?\d+$/.test(str || '');
|
|
944
|
-
},
|
|
945
|
-
isFloat(str) {
|
|
946
|
-
return /^-?\d+\.\d+$/.test(str || '');
|
|
947
|
-
},
|
|
948
|
-
isPositive(str) {
|
|
949
|
-
const num = parseFloat(str);
|
|
950
|
-
return !isNaN(num) && num > 0;
|
|
951
|
-
},
|
|
952
|
-
isNegative(str) {
|
|
953
|
-
const num = parseFloat(str);
|
|
954
|
-
return !isNaN(num) && num < 0;
|
|
955
|
-
},
|
|
956
|
-
isAlpha(str) {
|
|
957
|
-
return /^[a-zA-Z]+$/.test(str || '');
|
|
958
|
-
},
|
|
959
|
-
isAlphaNumeric(str) {
|
|
960
|
-
return /^[a-zA-Z0-9]+$/.test(str || '');
|
|
961
|
-
},
|
|
962
|
-
isChinese(str) {
|
|
963
|
-
return /^[\u4e00-\u9fa5]+$/.test(str || '');
|
|
964
|
-
},
|
|
965
|
-
isLength(str, min, max) {
|
|
966
|
-
const len = (str || '').length;
|
|
967
|
-
return len >= min && (max === undefined || len <= max);
|
|
968
|
-
},
|
|
969
|
-
minLength(str, min) {
|
|
970
|
-
return (str || '').length >= min;
|
|
971
|
-
},
|
|
972
|
-
maxLength(str, max) {
|
|
973
|
-
return (str || '').length <= max;
|
|
974
|
-
},
|
|
975
|
-
matches(str, regex) {
|
|
976
|
-
return (regex instanceof RegExp) ? regex.test(str || '') : false;
|
|
977
|
-
},
|
|
978
|
-
equals(str1, str2) {
|
|
979
|
-
return String(str1) === String(str2);
|
|
980
|
-
},
|
|
981
|
-
contains(str, substring) {
|
|
982
|
-
return (str || '').includes(substring);
|
|
983
|
-
},
|
|
984
|
-
notContains(str, substring) {
|
|
985
|
-
return !this.contains(str, substring);
|
|
986
|
-
},
|
|
987
|
-
isArray(arr) {
|
|
988
|
-
return Array.isArray(arr);
|
|
989
|
-
},
|
|
990
|
-
arrayLength(arr, min, max) {
|
|
991
|
-
const len = arr ? arr.length : 0;
|
|
992
|
-
return len >= min && (max === undefined || len <= max);
|
|
993
|
-
},
|
|
994
|
-
arrayMinLength(arr, min) {
|
|
995
|
-
return arr ? arr.length >= min : false;
|
|
996
|
-
},
|
|
997
|
-
arrayMaxLength(arr, max) {
|
|
998
|
-
return arr ? arr.length <= max : false;
|
|
999
|
-
},
|
|
1000
|
-
isObject(obj) {
|
|
1001
|
-
return obj !== null && typeof obj === 'object' && !Array.isArray(obj);
|
|
1002
|
-
},
|
|
1003
|
-
hasKeys(obj, keys) {
|
|
1004
|
-
if (!obj || !keys || !Array.isArray(keys)) return false;
|
|
1005
|
-
return keys.every(key => Object.prototype.hasOwnProperty.call(obj, key));
|
|
1006
|
-
},
|
|
1007
|
-
validate(obj, rules) {
|
|
1008
|
-
const errors = {};
|
|
1009
|
-
Object.keys(rules).forEach(key => {
|
|
1010
|
-
const value = obj[key];
|
|
1011
|
-
const fieldRules = rules[key];
|
|
1012
|
-
const fieldErrors = [];
|
|
1013
|
-
|
|
1014
|
-
fieldRules.forEach(rule => {
|
|
1015
|
-
if (typeof rule === 'string') {
|
|
1016
|
-
const [name, ...params] = rule.split(':');
|
|
1017
|
-
if (!this[name](value, ...params)) {
|
|
1018
|
-
fieldErrors.push(name);
|
|
1019
|
-
}
|
|
1020
|
-
} else if (typeof rule === 'function') {
|
|
1021
|
-
const result = rule(value, obj);
|
|
1022
|
-
if (result !== true) {
|
|
1023
|
-
fieldErrors.push(result || 'validation_failed');
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
});
|
|
1027
|
-
|
|
1028
|
-
if (fieldErrors.length > 0) {
|
|
1029
|
-
errors[key] = fieldErrors;
|
|
1030
|
-
}
|
|
1031
|
-
});
|
|
1032
|
-
|
|
1033
|
-
return {
|
|
1034
|
-
valid: Object.keys(errors).length === 0,
|
|
1035
|
-
errors
|
|
1036
|
-
};
|
|
1037
|
-
}
|
|
1038
|
-
},
|
|
110
|
+
// === Number Utilities ===
|
|
111
|
+
function isNumber(val) { return typeof val === 'number' && !isNaN(val); }
|
|
112
|
+
function isInteger(val) { return Number.isInteger(val); }
|
|
113
|
+
function isFloat(val) { return isNumber(val) && !Number.isInteger(val); }
|
|
114
|
+
function isPositive(val) { return isNumber(val) && val > 0; }
|
|
115
|
+
function isNegative(val) { return isNumber(val) && val < 0; }
|
|
116
|
+
function isZero(val) { return isNumber(val) && val === 0; }
|
|
117
|
+
function clamp(val, minVal, maxVal) { if (!isNumber(val)) return val; return Math.min(Math.max(val, minVal), maxVal); }
|
|
118
|
+
function round(val, precision = 0) { if (!isNumber(val)) return val; const factor = Math.pow(10, precision); return Math.round(val * factor) / factor; }
|
|
119
|
+
function floor(val) { return isNumber(val) ? Math.floor(val) : val; }
|
|
120
|
+
function ceil(val) { return isNumber(val) ? Math.ceil(val) : val; }
|
|
121
|
+
function abs(val) { return isNumber(val) ? Math.abs(val) : val; }
|
|
122
|
+
function minNum(...args) { const nums = args.filter(isNumber); return nums.length > 0 ? Math.min(...nums) : undefined; }
|
|
123
|
+
function maxNum(...args) { const nums = args.filter(isNumber); return nums.length > 0 ? Math.max(...nums) : undefined; }
|
|
124
|
+
function sumNum(...args) { const nums = args.flat().filter(isNumber); return nums.reduce((acc, val) => acc + val, 0); }
|
|
125
|
+
function averageNum(...args) { const nums = args.flat().filter(isNumber); return nums.length > 0 ? sumNum(nums) / nums.length : 0; }
|
|
126
|
+
function random(minVal = 0, maxVal = 1) { return Math.random() * (maxVal - minVal) + minVal; }
|
|
127
|
+
function randomInt(minVal, maxVal) { return Math.floor(random(minVal, maxVal + 1)); }
|
|
128
|
+
function formatNum(val, decimals = 2) { if (!isNumber(val)) return String(val); return val.toFixed(decimals); }
|
|
129
|
+
function formatCurrency(val, currency = 'CNY', decimals = 2) { if (!isNumber(val)) return String(val); const formatter = new Intl.NumberFormat('zh-CN', { style: 'currency', currency, minimumFractionDigits: decimals, maximumFractionDigits: decimals }); return formatter.format(val); }
|
|
130
|
+
function formatPercent(val, decimals = 0) { if (!isNumber(val)) return String(val); return `${(val * 100).toFixed(decimals)}%`; }
|
|
131
|
+
function toFixed(val, decimals = 0) { if (!isNumber(val)) return String(val); return val.toFixed(decimals); }
|
|
132
|
+
function toPrecision(val, precision = 6) { if (!isNumber(val)) return String(val); return val.toPrecision(precision); }
|
|
133
|
+
function isNaNVal(val) { return Number.isNaN(val); }
|
|
134
|
+
function isFiniteVal(val) { return Number.isFinite(val); }
|
|
135
|
+
function parseIntSafe(val, radix = 10) { return Number.parseInt(val, radix); }
|
|
136
|
+
function parseFloatSafe(val) { return Number.parseFloat(val); }
|
|
137
|
+
function toNumber(val, defaultValue = 0) { const num = Number(val); return isNaN(num) ? defaultValue : num; }
|
|
138
|
+
function safeDivide(a, b, defaultValue = 0) { if (!isNumber(a) || !isNumber(b) || b === 0) return defaultValue; return a / b; }
|
|
139
|
+
function safeMultiply(...args) { return args.reduce((result, val) => { if (!isNumber(result) || !isNumber(val)) return 0; return result * val; }, 1); }
|
|
1039
140
|
|
|
1040
|
-
|
|
1041
|
-
md5(str) {
|
|
1042
|
-
const message = str ? String(str) : '';
|
|
1043
|
-
const md5Array = [
|
|
1044
|
-
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
|
|
1045
|
-
];
|
|
1046
|
-
|
|
1047
|
-
const K = [
|
|
1048
|
-
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
|
1049
|
-
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
|
1050
|
-
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
|
1051
|
-
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
|
1052
|
-
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
|
1053
|
-
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
|
1054
|
-
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
|
1055
|
-
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
|
1056
|
-
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
|
1057
|
-
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
|
1058
|
-
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
|
1059
|
-
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
|
1060
|
-
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
|
1061
|
-
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
|
1062
|
-
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
|
1063
|
-
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
|
1064
|
-
];
|
|
1065
|
-
|
|
1066
|
-
const S = [
|
|
1067
|
-
[7, 12, 17, 22], [5, 9, 14, 20], [4, 11, 16, 23], [6, 10, 15, 21]
|
|
1068
|
-
];
|
|
1069
|
-
|
|
1070
|
-
function leftRotate(value, shift) {
|
|
1071
|
-
return (value << shift) | (value >>> (32 - shift));
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
function addPadding(message) {
|
|
1075
|
-
const bitLength = message.length * 8;
|
|
1076
|
-
message += '\x80';
|
|
1077
|
-
while ((message.length % 64) !== 56) {
|
|
1078
|
-
message += '\x00';
|
|
1079
|
-
}
|
|
1080
|
-
const lowBytes = bitLength & 0xffffffff;
|
|
1081
|
-
const highBytes = (bitLength >>> 32) & 0xffffffff;
|
|
1082
|
-
for (let i = 0; i < 4; i++) {
|
|
1083
|
-
message += String.fromCharCode((lowBytes >>> (8 * i)) & 0xff);
|
|
1084
|
-
}
|
|
1085
|
-
for (let i = 0; i < 4; i++) {
|
|
1086
|
-
message += String.fromCharCode((highBytes >>> (8 * i)) & 0xff);
|
|
1087
|
-
}
|
|
1088
|
-
return message;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
function processBlock(block, state) {
|
|
1092
|
-
const [a, b, c, d] = state;
|
|
1093
|
-
const words = [];
|
|
1094
|
-
for (let i = 0; i < 16; i++) {
|
|
1095
|
-
words[i] = (block.charCodeAt(i * 4) & 0xff) |
|
|
1096
|
-
((block.charCodeAt(i * 4 + 1) & 0xff) << 8) |
|
|
1097
|
-
((block.charCodeAt(i * 4 + 2) & 0xff) << 16) |
|
|
1098
|
-
((block.charCodeAt(i * 4 + 3) & 0xff) << 24);
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
let AA = a, BB = b, CC = c, DD = d;
|
|
1102
|
-
|
|
1103
|
-
for (let i = 0; i < 64; i++) {
|
|
1104
|
-
let f, g;
|
|
1105
|
-
const round = Math.floor(i / 16);
|
|
1106
|
-
const wordIndex = i % 16;
|
|
1107
|
-
|
|
1108
|
-
if (round === 0) {
|
|
1109
|
-
f = (BB & CC) | (~BB & DD);
|
|
1110
|
-
g = wordIndex;
|
|
1111
|
-
} else if (round === 1) {
|
|
1112
|
-
f = (DD & BB) | (~DD & CC);
|
|
1113
|
-
g = (5 * wordIndex + 1) % 16;
|
|
1114
|
-
} else if (round === 2) {
|
|
1115
|
-
f = BB ^ CC ^ DD;
|
|
1116
|
-
g = (3 * wordIndex + 5) % 16;
|
|
1117
|
-
} else {
|
|
1118
|
-
f = CC ^ (BB | ~DD);
|
|
1119
|
-
g = (7 * wordIndex) % 16;
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
const temp = DD;
|
|
1123
|
-
DD = CC;
|
|
1124
|
-
CC = BB;
|
|
1125
|
-
BB = BB + leftRotate((AA + f + K[i] + words[g]) & 0xffffffff, S[round][i % 4]);
|
|
1126
|
-
AA = temp;
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
return [
|
|
1130
|
-
(a + AA) & 0xffffffff,
|
|
1131
|
-
(b + BB) & 0xffffffff,
|
|
1132
|
-
(c + CC) & 0xffffffff,
|
|
1133
|
-
(d + DD) & 0xffffffff
|
|
1134
|
-
];
|
|
1135
|
-
}
|
|
1136
|
-
|
|
1137
|
-
const paddedMessage = addPadding(message);
|
|
1138
|
-
let state = [...md5Array];
|
|
1139
|
-
|
|
1140
|
-
for (let i = 0; i < paddedMessage.length; i += 64) {
|
|
1141
|
-
state = processBlock(paddedMessage.substring(i, i + 64), state);
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
let result = '';
|
|
1145
|
-
state.forEach(word => {
|
|
1146
|
-
for (let i = 0; i < 4; i++) {
|
|
1147
|
-
result += ((word >>> (8 * i)) & 0xff).toString(16).padStart(2, '0');
|
|
1148
|
-
}
|
|
1149
|
-
});
|
|
1150
|
-
|
|
1151
|
-
return result;
|
|
1152
|
-
},
|
|
1153
|
-
|
|
1154
|
-
sha256(str) {
|
|
1155
|
-
const message = str ? String(str) : '';
|
|
1156
|
-
const K = [
|
|
1157
|
-
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
|
1158
|
-
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
1159
|
-
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
1160
|
-
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
1161
|
-
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
|
1162
|
-
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
1163
|
-
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
|
1164
|
-
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
1165
|
-
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
1166
|
-
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
1167
|
-
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
|
1168
|
-
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
1169
|
-
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
|
1170
|
-
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
1171
|
-
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
1172
|
-
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
|
1173
|
-
];
|
|
1174
|
-
|
|
1175
|
-
function rightRotate(value, shift) {
|
|
1176
|
-
return (value >>> shift) | (value << (32 - shift));
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
function addPadding(message) {
|
|
1180
|
-
const bitLength = message.length * 8;
|
|
1181
|
-
message += '\x80';
|
|
1182
|
-
while ((message.length % 64) !== 56) {
|
|
1183
|
-
message += '\x00';
|
|
1184
|
-
}
|
|
1185
|
-
const lowBytes = bitLength & 0xffffffff;
|
|
1186
|
-
const highBytes = (bitLength >>> 32) & 0xffffffff;
|
|
1187
|
-
for (let i = 0; i < 4; i++) {
|
|
1188
|
-
message += String.fromCharCode((highBytes >>> (8 * i)) & 0xff);
|
|
1189
|
-
}
|
|
1190
|
-
for (let i = 0; i < 4; i++) {
|
|
1191
|
-
message += String.fromCharCode((lowBytes >>> (8 * i)) & 0xff);
|
|
1192
|
-
}
|
|
1193
|
-
return message;
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
function processBlock(block, state) {
|
|
1197
|
-
const words = [];
|
|
1198
|
-
for (let i = 0; i < 16; i++) {
|
|
1199
|
-
words[i] = (block.charCodeAt(i * 4) & 0xff) |
|
|
1200
|
-
((block.charCodeAt(i * 4 + 1) & 0xff) << 8) |
|
|
1201
|
-
((block.charCodeAt(i * 4 + 2) & 0xff) << 16) |
|
|
1202
|
-
((block.charCodeAt(i * 4 + 3) & 0xff) << 24);
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
for (let i = 16; i < 64; i++) {
|
|
1206
|
-
const s0 = rightRotate(words[i - 15], 7) ^ rightRotate(words[i - 15], 18) ^ (words[i - 15] >>> 3);
|
|
1207
|
-
const s1 = rightRotate(words[i - 2], 17) ^ rightRotate(words[i - 2], 19) ^ (words[i - 2] >>> 10);
|
|
1208
|
-
words[i] = (words[i - 16] + s0 + words[i - 7] + s1) & 0xffffffff;
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
let [a, b, c, d, e, f, g, h] = state;
|
|
1212
|
-
|
|
1213
|
-
for (let i = 0; i < 64; i++) {
|
|
1214
|
-
const S1 = rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25);
|
|
1215
|
-
const ch = (e & f) ^ (~e & g);
|
|
1216
|
-
const temp1 = (h + S1 + ch + K[i] + words[i]) & 0xffffffff;
|
|
1217
|
-
const S0 = rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22);
|
|
1218
|
-
const maj = (a & b) ^ (a & c) ^ (b & c);
|
|
1219
|
-
const temp2 = (S0 + maj) & 0xffffffff;
|
|
1220
|
-
|
|
1221
|
-
h = g;
|
|
1222
|
-
g = f;
|
|
1223
|
-
f = e;
|
|
1224
|
-
e = (d + temp1) & 0xffffffff;
|
|
1225
|
-
d = c;
|
|
1226
|
-
c = b;
|
|
1227
|
-
b = a;
|
|
1228
|
-
a = (temp1 + temp2) & 0xffffffff;
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
return [
|
|
1232
|
-
(state[0] + a) & 0xffffffff,
|
|
1233
|
-
(state[1] + b) & 0xffffffff,
|
|
1234
|
-
(state[2] + c) & 0xffffffff,
|
|
1235
|
-
(state[3] + d) & 0xffffffff,
|
|
1236
|
-
(state[4] + e) & 0xffffffff,
|
|
1237
|
-
(state[5] + f) & 0xffffffff,
|
|
1238
|
-
(state[6] + g) & 0xffffffff,
|
|
1239
|
-
(state[7] + h) & 0xffffffff
|
|
1240
|
-
];
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
const paddedMessage = addPadding(message);
|
|
1244
|
-
let state = [
|
|
1245
|
-
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
|
1246
|
-
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
|
1247
|
-
];
|
|
1248
|
-
|
|
1249
|
-
for (let i = 0; i < paddedMessage.length; i += 64) {
|
|
1250
|
-
state = processBlock(paddedMessage.substring(i, i + 64), state);
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
let result = '';
|
|
1254
|
-
state.forEach(word => {
|
|
1255
|
-
for (let i = 3; i >= 0; i--) {
|
|
1256
|
-
result += ((word >>> (8 * i)) & 0xff).toString(16).padStart(2, '0');
|
|
1257
|
-
}
|
|
1258
|
-
});
|
|
1259
|
-
|
|
1260
|
-
return result;
|
|
1261
|
-
},
|
|
1262
|
-
|
|
1263
|
-
base64Encode(str) {
|
|
1264
|
-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
1265
|
-
let result = '';
|
|
1266
|
-
let i = 0;
|
|
1267
|
-
const bytes = str ? str.split('').map(c => c.charCodeAt(0)) : [];
|
|
1268
|
-
|
|
1269
|
-
while (i < bytes.length) {
|
|
1270
|
-
const byte1 = bytes[i++];
|
|
1271
|
-
const byte2 = bytes[i++] || 0;
|
|
1272
|
-
const byte3 = bytes[i++] || 0;
|
|
1273
|
-
|
|
1274
|
-
const enc1 = byte1 >> 2;
|
|
1275
|
-
const enc2 = ((byte1 & 3) << 4) | (byte2 >> 4);
|
|
1276
|
-
const enc3 = ((byte2 & 15) << 2) | (byte3 >> 6);
|
|
1277
|
-
const enc4 = byte3 & 63;
|
|
1278
|
-
|
|
1279
|
-
result += chars[enc1] + chars[enc2] +
|
|
1280
|
-
(i > bytes.length + 1 ? '=' : chars[enc3]) +
|
|
1281
|
-
(i > bytes.length ? '=' : chars[enc4]);
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
return result;
|
|
1285
|
-
},
|
|
1286
|
-
|
|
1287
|
-
base64Decode(str) {
|
|
1288
|
-
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
1289
|
-
let result = '';
|
|
1290
|
-
let i = 0;
|
|
1291
|
-
str = str.replace(/[^A-Za-z0-9+/=]/g, '');
|
|
1292
|
-
|
|
1293
|
-
while (i < str.length) {
|
|
1294
|
-
const enc1 = chars.indexOf(str.charAt(i++));
|
|
1295
|
-
const enc2 = chars.indexOf(str.charAt(i++));
|
|
1296
|
-
const enc3 = chars.indexOf(str.charAt(i++));
|
|
1297
|
-
const enc4 = chars.indexOf(str.charAt(i++));
|
|
1298
|
-
|
|
1299
|
-
const byte1 = (enc1 << 2) | (enc2 >> 4);
|
|
1300
|
-
const byte2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
|
1301
|
-
const byte3 = ((enc3 & 3) << 6) | enc4;
|
|
1302
|
-
|
|
1303
|
-
result += String.fromCharCode(byte1);
|
|
1304
|
-
if (enc3 !== 64) result += String.fromCharCode(byte2);
|
|
1305
|
-
if (enc4 !== 64) result += String.fromCharCode(byte3);
|
|
1306
|
-
}
|
|
1307
|
-
|
|
1308
|
-
return result;
|
|
1309
|
-
},
|
|
1310
|
-
|
|
1311
|
-
uuid() {
|
|
1312
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
1313
|
-
const r = Math.random() * 16 | 0;
|
|
1314
|
-
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
1315
|
-
return v.toString(16);
|
|
1316
|
-
});
|
|
1317
|
-
}
|
|
1318
|
-
},
|
|
141
|
+
const numberUtils = { isNumber, isInteger, isFloat, isPositive, isNegative, isZero, clamp, round, floor, ceil, abs, min: minNum, max: maxNum, sum: sumNum, average: averageNum, random, randomInt, format: formatNum, formatCurrency, formatPercent, toFixed, toPrecision, isNaN: isNaNVal, isFinite: isFiniteVal, parseInt: parseIntSafe, parseFloat: parseFloatSafe, toNumber, safeDivide, safeMultiply };
|
|
1319
142
|
|
|
1320
|
-
|
|
1321
|
-
|
|
143
|
+
// === Date Utilities ===
|
|
144
|
+
function now() { return Date.now(); }
|
|
145
|
+
function today() { const d = new Date(); d.setHours(0, 0, 0, 0); return d; }
|
|
146
|
+
function tomorrow() { const d = today(); d.setDate(d.getDate() + 1); return d; }
|
|
147
|
+
function yesterday() { const d = today(); d.setDate(d.getDate() - 1); return d; }
|
|
148
|
+
function isDate(val) { return val instanceof Date && !isNaN(val.getTime()); }
|
|
149
|
+
function isValidDate(date) { return isDate(date); }
|
|
150
|
+
function parseDate(str) { const d = new Date(str); return isValidDate(d) ? d : null; }
|
|
151
|
+
function formatDate(date, formatStr = 'YYYY-MM-DD HH:mm:ss') { if (!isDate(date)) return ''; const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); const milliseconds = String(date.getMilliseconds()).padStart(3, '0'); const weekDays = ['日', '一', '二', '三', '四', '五', '六']; const weekDay = weekDays[date.getDay()]; return formatStr.replace('YYYY', year).replace('MM', month).replace('DD', day).replace('HH', hours).replace('mm', minutes).replace('ss', seconds).replace('SSS', milliseconds).replace('D', date.getDate()).replace('M', date.getMonth() + 1).replace('H', date.getHours()).replace('m', date.getMinutes()).replace('s', date.getSeconds()).replace('W', weekDay); }
|
|
152
|
+
function toISO(date) { return isDate(date) ? date.toISOString() : ''; }
|
|
153
|
+
function toUTC(date) { return isDate(date) ? new Date(date.toUTCString()) : null; }
|
|
154
|
+
function addDays(date, days) { if (!isDate(date)) return date; const d = new Date(date); d.setDate(d.getDate() + days); return d; }
|
|
155
|
+
function addHours(date, hours) { if (!isDate(date)) return date; const d = new Date(date); d.setHours(d.getHours() + hours); return d; }
|
|
156
|
+
function addMinutes(date, minutes) { if (!isDate(date)) return date; const d = new Date(date); d.setMinutes(d.getMinutes() + minutes); return d; }
|
|
157
|
+
function addSeconds(date, seconds) { if (!isDate(date)) return date; const d = new Date(date); d.setSeconds(d.getSeconds() + seconds); return d; }
|
|
158
|
+
function diffDays(date1, date2) { if (!isDate(date1) || !isDate(date2)) return 0; const d1 = new Date(date1); d1.setHours(0,0,0,0); const d2 = new Date(date2); d2.setHours(0,0,0,0); return Math.floor((d1.getTime() - d2.getTime()) / (1000 * 60 * 60 * 24)); }
|
|
159
|
+
function diffHours(date1, date2) { if (!isDate(date1) || !isDate(date2)) return 0; return Math.floor((date1.getTime() - date2.getTime()) / (1000 * 60 * 60)); }
|
|
160
|
+
function diffMinutes(date1, date2) { if (!isDate(date1) || !isDate(date2)) return 0; return Math.floor((date1.getTime() - date2.getTime()) / (1000 * 60)); }
|
|
161
|
+
function diffSeconds(date1, date2) { if (!isDate(date1) || !isDate(date2)) return 0; return Math.floor((date1.getTime() - date2.getTime()) / 1000); }
|
|
162
|
+
function isToday(date) { if (!isDate(date)) return false; return diffDays(date, today()) === 0; }
|
|
163
|
+
function isYesterday(date) { if (!isDate(date)) return false; return diffDays(date, today()) === -1; }
|
|
164
|
+
function isTomorrow(date) { if (!isDate(date)) return false; return diffDays(date, today()) === 1; }
|
|
165
|
+
function isFuture(date) { if (!isDate(date)) return false; return date.getTime() > now(); }
|
|
166
|
+
function isPast(date) { if (!isDate(date)) return false; return date.getTime() < now(); }
|
|
167
|
+
function isLeapYear(date) { if (!isDate(date)) return false; const year = date.getFullYear(); return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); }
|
|
168
|
+
function getDaysInMonth(date) { if (!isDate(date)) return 0; return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); }
|
|
169
|
+
function getWeekOfYear(date) { if (!isDate(date)) return 0; const startOfYear = new Date(date.getFullYear(), 0, 1); const diff = date.getTime() - startOfYear.getTime(); return Math.ceil(diff / (1000 * 60 * 60 * 24 * 7)); }
|
|
170
|
+
function getQuarter(date) { if (!isDate(date)) return 0; return Math.ceil((date.getMonth() + 1) / 3); }
|
|
171
|
+
function startOfDay(date) { if (!isDate(date)) return date; const d = new Date(date); d.setHours(0, 0, 0, 0); return d; }
|
|
172
|
+
function endOfDay(date) { if (!isDate(date)) return date; const d = new Date(date); d.setHours(23, 59, 59, 999); return d; }
|
|
173
|
+
function startOfMonth(date) { if (!isDate(date)) return date; return new Date(date.getFullYear(), date.getMonth(), 1); }
|
|
174
|
+
function endOfMonth(date) { if (!isDate(date)) return date; return new Date(date.getFullYear(), date.getMonth() + 1, 0, 23, 59, 59, 999); }
|
|
175
|
+
function startOfWeek(date, startDay = 1) { if (!isDate(date)) return date; const d = new Date(date); const day = d.getDay(); const diff = day >= startDay ? day - startDay : day + (7 - startDay); d.setDate(d.getDate() - diff); d.setHours(0, 0, 0, 0); return d; }
|
|
176
|
+
function endOfWeek(date, startDay = 1) { if (!isDate(date)) return date; const start = startOfWeek(date, startDay); const end = new Date(start); end.setDate(end.getDate() + 6); end.setHours(23, 59, 59, 999); return end; }
|
|
177
|
+
function getAge(birthDate) { if (!isDate(birthDate)) return 0; const n = new Date(); let age = n.getFullYear() - birthDate.getFullYear(); if (n.getMonth() < birthDate.getMonth() || (n.getMonth() === birthDate.getMonth() && n.getDate() < birthDate.getDate())) { age--; } return Math.max(0, age); }
|
|
178
|
+
function fromNow(date) { if (!isDate(date)) return ''; const n = now(); const diff = n - date.getTime(); const minute = 60 * 1000; const hour = 60 * minute; const day = 24 * hour; const week = 7 * day; const month = 30 * day; const year = 365 * day; if (diff < minute) return '刚刚'; if (diff < hour) return `${Math.floor(diff / minute)}分钟前`; if (diff < day) return `${Math.floor(diff / hour)}小时前`; if (diff < week) return `${Math.floor(diff / day)}天前`; if (diff < month) return `${Math.floor(diff / week)}周前`; if (diff < year) return `${Math.floor(diff / month)}个月前`; return `${Math.floor(diff / year)}年前`; }
|
|
1322
179
|
|
|
1323
|
-
|
|
1324
|
-
const { crossOrigin = 'anonymous' } = options;
|
|
1325
|
-
|
|
1326
|
-
if (this.cache.has(url)) {
|
|
1327
|
-
return this.cache.get(url);
|
|
1328
|
-
}
|
|
180
|
+
const dateUtils = { now, today, tomorrow, yesterday, isDate, isValid: isValidDate, parse: parseDate, format: formatDate, toISO, toUTC, addDays, addHours, addMinutes, addSeconds, diffDays, diffHours, diffMinutes, diffSeconds, isToday, isYesterday, isTomorrow, isFuture, isPast, isLeapYear, getDaysInMonth, getWeekOfYear, getQuarter, startOfDay, endOfDay, startOfMonth, endOfMonth, startOfWeek, endOfWeek, getAge, fromNow };
|
|
1329
181
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
};
|
|
1342
|
-
|
|
1343
|
-
img.src = url;
|
|
1344
|
-
});
|
|
1345
|
-
},
|
|
182
|
+
// === Timing Utilities ===
|
|
183
|
+
function debounce(func, wait, options = {}) {
|
|
184
|
+
let timeout = null, lastArgs = null, lastThis = null, lastCallTime = 0;
|
|
185
|
+
const leading = options.leading || false;
|
|
186
|
+
const trailing = options.trailing !== false;
|
|
187
|
+
function invokeFunc() { func.apply(lastThis, lastArgs); }
|
|
188
|
+
function leadingEdge() { lastCallTime = Date.now(); if (leading) { timeout = setTimeout(trailingEdge, wait); invokeFunc(); } else { timeout = setTimeout(trailingEdge, wait); } }
|
|
189
|
+
function trailingEdge() { timeout = null; if (trailing && lastArgs) { invokeFunc(); } lastArgs = null; lastThis = null; }
|
|
190
|
+
function remainingWait() { return Math.max(0, wait - (Date.now() - lastCallTime)); }
|
|
191
|
+
return function(...args) { lastArgs = args; lastThis = this; lastCallTime = Date.now(); if (!timeout) { leadingEdge(); } else { clearTimeout(timeout); timeout = setTimeout(trailingEdge, remainingWait()); } };
|
|
192
|
+
}
|
|
1346
193
|
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
const results = [];
|
|
1355
|
-
for (const url of urls) {
|
|
1356
|
-
results.push(await this.loadImage(url, options));
|
|
1357
|
-
}
|
|
1358
|
-
return results;
|
|
1359
|
-
},
|
|
194
|
+
function throttle(func, limit, options = {}) {
|
|
195
|
+
let inThrottle = false;
|
|
196
|
+
const trailing = options.trailing || false;
|
|
197
|
+
let lastArgs = null, lastThis = null;
|
|
198
|
+
function invokeFunc() { func.apply(lastThis, lastArgs); lastArgs = null; lastThis = null; }
|
|
199
|
+
return function(...args) { if (!inThrottle) { inThrottle = true; lastArgs = args; lastThis = this; invokeFunc(); setTimeout(() => { inThrottle = false; if (trailing && lastArgs) { invokeFunc(); } }, limit); } else if (trailing) { lastArgs = args; lastThis = this; } };
|
|
200
|
+
}
|
|
1360
201
|
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
202
|
+
// === Validator Utilities ===
|
|
203
|
+
function isEmail(str) { return /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(str || ''); }
|
|
204
|
+
function isPhone(str) { return /^1[3-9]\d{9}$/.test(str || ''); }
|
|
205
|
+
function isURL(str) { return /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w.-]*)*\/?$/.test(str || ''); }
|
|
206
|
+
function isIPv4(str) { return /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(str || ''); }
|
|
207
|
+
function isIPv6(str) { return /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/.test(str || ''); }
|
|
208
|
+
function isIP(str) { return isIPv4(str) || isIPv6(str); }
|
|
209
|
+
function isIDCard(str) { return /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/.test(str || ''); }
|
|
210
|
+
function isPassport(str) { return /^[A-Z][0-9]{8}$|^[A-Z]{2}[0-9]{7}$/.test(str || ''); }
|
|
211
|
+
function isCreditCard(str) { const regex = /^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9]{2})[0-9]{12}|3[47][0-9]{13})$/; const cleanStr = str.replace(/\s/g, ''); if (!regex.test(cleanStr)) return false; let s = 0, alt = false; for (let i = cleanStr.length - 1; i >= 0; i--) { let digit = parseInt(cleanStr[i], 10); if (alt) { digit *= 2; if (digit > 9) digit -= 9; } s += digit; alt = !alt; } return s % 10 === 0; }
|
|
212
|
+
function isHexColor(str) { return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(str || ''); }
|
|
213
|
+
function isRGB(str) { const m = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(str || ''); if (!m) return false; return m.slice(1).every(v => parseInt(v) >= 0 && parseInt(v) <= 255); }
|
|
214
|
+
function isRGBA(str) { const m = /^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*([01]|0\.\d+)\)$/.exec(str || ''); if (!m) return false; const [, r, g, b, a] = m; return parseInt(r) >= 0 && parseInt(r) <= 255 && parseInt(g) >= 0 && parseInt(g) <= 255 && parseInt(b) >= 0 && parseInt(b) <= 255 && parseFloat(a) >= 0 && parseFloat(a) <= 1; }
|
|
215
|
+
function isColor(str) { return isHexColor(str) || isRGB(str) || isRGBA(str); }
|
|
216
|
+
function isDateStr(str) { return !isNaN(new Date(str).getTime()); }
|
|
217
|
+
function isJSON(str) { try { JSON.parse(str); return true; } catch { return false; } }
|
|
218
|
+
function isEmptyStr(str) { return !str || str.trim() === ''; }
|
|
219
|
+
function isWhitespace(str) { return /^\s+$/.test(str || ''); }
|
|
220
|
+
function isNumberStr(str) { return !isNaN(parseFloat(str)) && isFinite(str); }
|
|
221
|
+
function isIntegerStr(str) { return /^-?\d+$/.test(str || ''); }
|
|
222
|
+
function isFloatStr(str) { return /^-?\d+\.\d+$/.test(str || ''); }
|
|
223
|
+
function isPositiveStr(str) { const num = parseFloat(str); return !isNaN(num) && num > 0; }
|
|
224
|
+
function isNegativeStr(str) { const num = parseFloat(str); return !isNaN(num) && num < 0; }
|
|
225
|
+
function isAlpha(str) { return /^[a-zA-Z]+$/.test(str || ''); }
|
|
226
|
+
function isAlphaNumeric(str) { return /^[a-zA-Z0-9]+$/.test(str || ''); }
|
|
227
|
+
function isChinese(str) { return /^[\u4e00-\u9fa5]+$/.test(str || ''); }
|
|
228
|
+
function isLength(str, minVal, maxVal) { const len = (str || '').length; return len >= minVal && (maxVal === undefined || len <= maxVal); }
|
|
229
|
+
function minLength(str, minVal) { return (str || '').length >= minVal; }
|
|
230
|
+
function maxLength(str, maxVal) { return (str || '').length <= maxVal; }
|
|
231
|
+
function matches(str, regex) { return (regex instanceof RegExp) ? regex.test(str || '') : false; }
|
|
232
|
+
function equals(str1, str2) { return String(str1) === String(str2); }
|
|
233
|
+
function contains(str, substring) { return (str || '').includes(substring); }
|
|
234
|
+
function notContains(str, substring) { return !contains(str, substring); }
|
|
235
|
+
function isArrayVal(arr) { return Array.isArray(arr); }
|
|
236
|
+
function arrayLength(arr, minVal, maxVal) { const len = arr ? arr.length : 0; return len >= minVal && (maxVal === undefined || len <= maxVal); }
|
|
237
|
+
function arrayMinLength(arr, minVal) { return arr ? arr.length >= minVal : false; }
|
|
238
|
+
function arrayMaxLength(arr, maxVal) { return arr ? arr.length <= maxVal : false; }
|
|
239
|
+
function isObjectVal(obj) { return obj !== null && typeof obj === 'object' && !Array.isArray(obj); }
|
|
240
|
+
function hasKeys(obj, keys) { if (!obj || !keys || !Array.isArray(keys)) return false; return keys.every(key => Object.prototype.hasOwnProperty.call(obj, key)); }
|
|
241
|
+
function validate(obj, rules) { const errors = {}; Object.keys(rules).forEach(key => { const value = obj[key]; const fieldRules = rules[key]; const fieldErrors = []; fieldRules.forEach(rule => { if (typeof rule === 'string') { const [name, ...params] = rule.split(':'); if (!validatorUtils[name](value, ...params)) { fieldErrors.push(name); } } else if (typeof rule === 'function') { const result = rule(value, obj); if (result !== true) { fieldErrors.push(result || 'validation_failed'); } } }); if (fieldErrors.length > 0) { errors[key] = fieldErrors; } }); return { valid: Object.keys(errors).length === 0, errors }; }
|
|
1367
242
|
|
|
1368
|
-
|
|
1369
|
-
const script = document.createElement('script');
|
|
1370
|
-
script.type = type;
|
|
1371
|
-
script.async = async;
|
|
1372
|
-
script.defer = defer;
|
|
1373
|
-
|
|
1374
|
-
script.onload = () => {
|
|
1375
|
-
this.cache.set(url, script);
|
|
1376
|
-
resolve(script);
|
|
1377
|
-
};
|
|
1378
|
-
|
|
1379
|
-
script.onerror = () => {
|
|
1380
|
-
script.remove();
|
|
1381
|
-
reject(new Error(`Failed to load script: ${url}`));
|
|
1382
|
-
};
|
|
1383
|
-
|
|
1384
|
-
script.src = url;
|
|
1385
|
-
document.head.appendChild(script);
|
|
1386
|
-
});
|
|
1387
|
-
},
|
|
243
|
+
const validatorUtils = { isEmail, isPhone, isURL, isIPv4, isIPv6, isIP, isIDCard, isPassport, isCreditCard, isHexColor, isRGB, isRGBA, isColor, isDate: isDateStr, isJSON, isEmpty: isEmptyStr, isWhitespace, isNumber: isNumberStr, isInteger: isIntegerStr, isFloat: isFloatStr, isPositive: isPositiveStr, isNegative: isNegativeStr, isAlpha, isAlphaNumeric, isChinese, isLength, minLength, maxLength, matches, equals, contains, notContains, isArray: isArrayVal, arrayLength, arrayMinLength, arrayMaxLength, isObject: isObjectVal, hasKeys, validate };
|
|
1388
244
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
245
|
+
// === Crypto Utilities ===
|
|
246
|
+
function md5(str) {
|
|
247
|
+
const message = str ? String(str) : '';
|
|
248
|
+
const md5Array = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476];
|
|
249
|
+
const K = [0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391];
|
|
250
|
+
const S = [[7,12,17,22],[5,9,14,20],[4,11,16,23],[6,10,15,21]];
|
|
251
|
+
function leftRotate(value, shift) { return (value << shift) | (value >>> (32 - shift)); }
|
|
252
|
+
function addPadding(msg) { const bitLength = msg.length * 8; msg += '\x80'; while ((msg.length % 64) !== 56) { msg += '\x00'; } const lowBytes = bitLength & 0xffffffff; const highBytes = (bitLength >>> 32) & 0xffffffff; for (let i = 0; i < 4; i++) { msg += String.fromCharCode((lowBytes >>> (8 * i)) & 0xff); } for (let i = 0; i < 4; i++) { msg += String.fromCharCode((highBytes >>> (8 * i)) & 0xff); } return msg; }
|
|
253
|
+
function processBlock(block, state) { const [a, b, c, d] = state; const words = []; for (let i = 0; i < 16; i++) { words[i] = (block.charCodeAt(i * 4) & 0xff) | ((block.charCodeAt(i * 4 + 1) & 0xff) << 8) | ((block.charCodeAt(i * 4 + 2) & 0xff) << 16) | ((block.charCodeAt(i * 4 + 3) & 0xff) << 24); } let AA = a, BB = b, CC = c, DD = d; for (let i = 0; i < 64; i++) { let f, g; const r = Math.floor(i / 16); const wi = i % 16; if (r === 0) { f = (BB & CC) | (~BB & DD); g = wi; } else if (r === 1) { f = (DD & BB) | (~DD & CC); g = (5 * wi + 1) % 16; } else if (r === 2) { f = BB ^ CC ^ DD; g = (3 * wi + 5) % 16; } else { f = CC ^ (BB | ~DD); g = (7 * wi) % 16; } const temp = DD; DD = CC; CC = BB; BB = BB + leftRotate((AA + f + K[i] + words[g]) & 0xffffffff, S[r][i % 4]); AA = temp; } return [(a + AA) & 0xffffffff, (b + BB) & 0xffffffff, (c + CC) & 0xffffffff, (d + DD) & 0xffffffff]; }
|
|
254
|
+
const paddedMessage = addPadding(message); let state = [...md5Array]; for (let i = 0; i < paddedMessage.length; i += 64) { state = processBlock(paddedMessage.substring(i, i + 64), state); } let result = ''; state.forEach(word => { for (let i = 0; i < 4; i++) { result += ((word >>> (8 * i)) & 0xff).toString(16).padStart(2, '0'); } }); return result;
|
|
255
|
+
}
|
|
1395
256
|
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
resolve(link);
|
|
1405
|
-
};
|
|
1406
|
-
|
|
1407
|
-
link.onerror = () => {
|
|
1408
|
-
link.remove();
|
|
1409
|
-
reject(new Error(`Failed to load stylesheet: ${url}`));
|
|
1410
|
-
};
|
|
1411
|
-
|
|
1412
|
-
document.head.appendChild(link);
|
|
1413
|
-
});
|
|
1414
|
-
},
|
|
257
|
+
function sha256(str) {
|
|
258
|
+
const message = str ? String(str) : '';
|
|
259
|
+
const K = [0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2];
|
|
260
|
+
function rightRotate(value, shift) { return (value >>> shift) | (value << (32 - shift)); }
|
|
261
|
+
function addPadding(msg) { const bitLength = msg.length * 8; msg += '\x80'; while ((msg.length % 64) !== 56) { msg += '\x00'; } const lowBytes = bitLength & 0xffffffff; const highBytes = (bitLength >>> 32) & 0xffffffff; for (let i = 0; i < 4; i++) { msg += String.fromCharCode((highBytes >>> (8 * i)) & 0xff); } for (let i = 0; i < 4; i++) { msg += String.fromCharCode((lowBytes >>> (8 * i)) & 0xff); } return msg; }
|
|
262
|
+
function processBlock(block, state) { const words = []; for (let i = 0; i < 16; i++) { words[i] = (block.charCodeAt(i * 4) & 0xff) | ((block.charCodeAt(i * 4 + 1) & 0xff) << 8) | ((block.charCodeAt(i * 4 + 2) & 0xff) << 16) | ((block.charCodeAt(i * 4 + 3) & 0xff) << 24); } for (let i = 16; i < 64; i++) { const s0 = rightRotate(words[i-15],7) ^ rightRotate(words[i-15],18) ^ (words[i-15]>>>3); const s1 = rightRotate(words[i-2],17) ^ rightRotate(words[i-2],19) ^ (words[i-2]>>>10); words[i] = (words[i-16]+s0+words[i-7]+s1) & 0xffffffff; } let [a,b,c,d,e,f,g,h] = state; for (let i = 0; i < 64; i++) { const S1 = rightRotate(e,6)^rightRotate(e,11)^rightRotate(e,25); const ch = (e&f)^(~e&g); const temp1 = (h+S1+ch+K[i]+words[i]) & 0xffffffff; const S0 = rightRotate(a,2)^rightRotate(a,13)^rightRotate(a,22); const maj = (a&b)^(a&c)^(b&c); const temp2 = (S0+maj) & 0xffffffff; h=g; g=f; f=e; e=(d+temp1)&0xffffffff; d=c; c=b; b=a; a=(temp1+temp2)&0xffffffff; } return [(state[0]+a)&0xffffffff,(state[1]+b)&0xffffffff,(state[2]+c)&0xffffffff,(state[3]+d)&0xffffffff,(state[4]+e)&0xffffffff,(state[5]+f)&0xffffffff,(state[6]+g)&0xffffffff,(state[7]+h)&0xffffffff]; }
|
|
263
|
+
const paddedMessage = addPadding(message); let state = [0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19]; for (let i = 0; i < paddedMessage.length; i += 64) { state = processBlock(paddedMessage.substring(i, i + 64), state); } let result = ''; state.forEach(word => { for (let i = 3; i >= 0; i--) { result += ((word >>> (8 * i)) & 0xff).toString(16).padStart(2, '0'); } }); return result;
|
|
264
|
+
}
|
|
1415
265
|
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
try {
|
|
1421
|
-
await fontFace.load();
|
|
1422
|
-
document.fonts.add(fontFace);
|
|
1423
|
-
return fontFace;
|
|
1424
|
-
} catch (e) {
|
|
1425
|
-
throw new Error(`Failed to load font: ${fontFamily}`);
|
|
1426
|
-
}
|
|
1427
|
-
},
|
|
266
|
+
function base64Encode(str) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; let result = ''; let i = 0; const bytes = str ? str.split('').map(c => c.charCodeAt(0)) : []; while (i < bytes.length) { const byte1 = bytes[i++]; const byte2 = bytes[i++] || 0; const byte3 = bytes[i++] || 0; const enc1 = byte1 >> 2; const enc2 = ((byte1 & 3) << 4) | (byte2 >> 4); const enc3 = ((byte2 & 15) << 2) | (byte3 >> 6); const enc4 = byte3 & 63; result += chars[enc1] + chars[enc2] + (i > bytes.length + 1 ? '=' : chars[enc3]) + (i > bytes.length ? '=' : chars[enc4]); } return result; }
|
|
267
|
+
function base64Decode(str) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; let result = ''; let i = 0; str = str.replace(/[^A-Za-z0-9+/=]/g, ''); while (i < str.length) { const enc1 = chars.indexOf(str.charAt(i++)); const enc2 = chars.indexOf(str.charAt(i++)); const enc3 = chars.indexOf(str.charAt(i++)); const enc4 = chars.indexOf(str.charAt(i++)); const byte1 = (enc1 << 2) | (enc2 >> 4); const byte2 = ((enc2 & 15) << 4) | (enc3 >> 2); const byte3 = ((enc3 & 3) << 6) | enc4; result += String.fromCharCode(byte1); if (enc3 !== 64) result += String.fromCharCode(byte2); if (enc4 !== 64) result += String.fromCharCode(byte3); } return result; }
|
|
268
|
+
function uuid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); }
|
|
1428
269
|
|
|
1429
|
-
|
|
1430
|
-
switch (type) {
|
|
1431
|
-
case 'image':
|
|
1432
|
-
return this.loadImage(url);
|
|
1433
|
-
case 'script':
|
|
1434
|
-
return this.loadScript(url);
|
|
1435
|
-
case 'stylesheet':
|
|
1436
|
-
case 'style':
|
|
1437
|
-
return this.loadStylesheet(url);
|
|
1438
|
-
default:
|
|
1439
|
-
throw new Error(`Unsupported preload type: ${type}`);
|
|
1440
|
-
}
|
|
1441
|
-
},
|
|
270
|
+
const cryptoUtils = { md5, sha256, base64Encode, base64Decode, uuid };
|
|
1442
271
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
272
|
+
// === Preload Utilities ===
|
|
273
|
+
const _preloadCache = new Map();
|
|
274
|
+
async function loadImage(url, options = {}) { const { crossOrigin = 'anonymous' } = options; if (_preloadCache.has(url)) return _preloadCache.get(url); return new Promise((resolve, reject) => { const img = new Image(); img.crossOrigin = crossOrigin; img.onload = () => { _preloadCache.set(url, img); resolve(img); }; img.onerror = () => { reject(new Error(`Failed to load image: ${url}`)); }; img.src = url; }); }
|
|
275
|
+
async function loadImages(urls, options = {}) { const { parallel = true } = options; if (parallel) return Promise.all(urls.map(url => loadImage(url, options))); const results = []; for (const url of urls) { results.push(await loadImage(url, options)); } return results; }
|
|
276
|
+
async function loadScript(url, options = {}) { const { type = 'text/javascript', async: isAsync = true, defer = false } = options; if (_preloadCache.has(url)) return _preloadCache.get(url); return new Promise((resolve, reject) => { const script = document.createElement('script'); script.type = type; script.async = isAsync; script.defer = defer; script.onload = () => { _preloadCache.set(url, script); resolve(script); }; script.onerror = () => { script.remove(); reject(new Error(`Failed to load script: ${url}`)); }; script.src = url; document.head.appendChild(script); }); }
|
|
277
|
+
async function loadStylesheet(url, options = {}) { const { media = 'all' } = options; if (_preloadCache.has(url)) return _preloadCache.get(url); return new Promise((resolve, reject) => { const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = url; link.media = media; link.onload = () => { _preloadCache.set(url, link); resolve(link); }; link.onerror = () => { link.remove(); reject(new Error(`Failed to load stylesheet: ${url}`)); }; document.head.appendChild(link); }); }
|
|
278
|
+
async function loadFont(fontFamily, src, options = {}) { const { weight = 'normal', style = 'normal' } = options; const fontFace = new FontFace(fontFamily, `url(${src})`, { weight, style }); try { await fontFace.load(); document.fonts.add(fontFace); return fontFace; } catch (e) { throw new Error(`Failed to load font: ${fontFamily}`); } }
|
|
279
|
+
async function preload(url, type = 'image') { switch (type) { case 'image': return loadImage(url); case 'script': return loadScript(url); case 'stylesheet': case 'style': return loadStylesheet(url); default: throw new Error(`Unsupported preload type: ${type}`); } }
|
|
280
|
+
function isLoaded(url) { return _preloadCache.has(url); }
|
|
281
|
+
function clearPreloadCache() { _preloadCache.clear(); }
|
|
282
|
+
function clearCacheByUrl(url) { _preloadCache.delete(url); }
|
|
1446
283
|
|
|
1447
|
-
|
|
1448
|
-
this.cache.clear();
|
|
1449
|
-
},
|
|
284
|
+
const preloadUtils = { loadImage, loadImages, loadScript, loadStylesheet, loadFont, preload, isLoaded, clearCache: clearPreloadCache, clearCacheByUrl };
|
|
1450
285
|
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
286
|
+
// === Backward-compatible facade ===
|
|
287
|
+
const KupolaUtils = {
|
|
288
|
+
string: stringUtils,
|
|
289
|
+
array: arrayUtils,
|
|
290
|
+
object: objectUtils,
|
|
291
|
+
number: numberUtils,
|
|
292
|
+
date: dateUtils,
|
|
293
|
+
debounce,
|
|
294
|
+
throttle,
|
|
295
|
+
validator: validatorUtils,
|
|
296
|
+
crypto: cryptoUtils,
|
|
297
|
+
preload: preloadUtils
|
|
1455
298
|
};
|
|
1456
299
|
|
|
1457
|
-
export {
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
}
|
|
300
|
+
export {
|
|
301
|
+
KupolaUtils,
|
|
302
|
+
stringUtils, arrayUtils, objectUtils, numberUtils, dateUtils,
|
|
303
|
+
debounce, throttle, validatorUtils, cryptoUtils, preloadUtils
|
|
304
|
+
};
|