mazey 3.6.0 → 3.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts DELETED
@@ -1,2543 +0,0 @@
1
- /**
2
- * @author: Mazey Chu
3
- */
4
-
5
- /**
6
- * EN: Computes the longest common substring of two strings.
7
- *
8
- * ZH: 计算两个字符串的最长公共子串
9
- *
10
- * Usage:
11
- *
12
- * ```
13
- * calLongestCommonSubstring('fish', 'finish');
14
- * ```
15
- *
16
- * Output:
17
- *
18
- * ```
19
- * 3
20
- * ```
21
- *
22
- * @param {string} aStr String
23
- * @param {string} bStr String
24
- * @returns {number} Length
25
- * @category Calculate and Formula
26
- */
27
- export function calLongestCommonSubstring(aStr: string, bStr: string): number {
28
- const aLen = aStr.length;
29
- const bLen = bStr.length;
30
- // 创建二维数组并且深拷贝
31
- const arr = deepCopyObject(new Array(aLen).fill(new Array(bLen).fill(0)));
32
- for (let i = 0; i < aLen; ++i) {
33
- for (let j = 0; j < bLen; ++j) {
34
- if (aStr[i] === bStr[j]) {
35
- let baseNum = 0;
36
- if (i > 0 && j > 0) {
37
- baseNum = arr[i - 1][j - 1];
38
- }
39
- arr[i][j] = baseNum + 1;
40
- }
41
- }
42
- }
43
- // 二维数组转一维数组
44
- const arr1 = Array.prototype.concat.apply([], arr);
45
- // 获取最长公共子串
46
- const maxLong = Math.max(...arr1);
47
- return maxLong;
48
- }
49
-
50
- /**
51
- * EN: Computes the longest common subsequence of two strings.
52
- *
53
- * ZH: 计算两个字符串的最长公共子序列
54
- *
55
- * Usage:
56
- *
57
- * ```
58
- * calLongestCommonSubsequence('fish', 'finish');
59
- * ```
60
- *
61
- * Output:
62
- *
63
- * ```
64
- * 4
65
- * ```
66
- *
67
- * @param {string} aStr 字符串
68
- * @param {string} bStr 字符串
69
- * @returns {number} 长度
70
- * @category Calculate and Formula
71
- */
72
- export function calLongestCommonSubsequence(
73
- aStr: string,
74
- bStr: string
75
- ): number {
76
- const aLen = aStr.length;
77
- const bLen = bStr.length;
78
- // 创建二维数组并且深拷贝
79
- const arr = deepCopyObject(new Array(aLen).fill(new Array(bLen).fill(0)));
80
- for (let i = 0; i < aLen; ++i) {
81
- for (let j = 0; j < bLen; ++j) {
82
- if (aStr[i] === bStr[j]) {
83
- let baseNum = 0;
84
- if (i > 0 && j > 0) {
85
- baseNum = arr[i - 1][j - 1];
86
- }
87
- arr[i][j] = baseNum + 1;
88
- } else {
89
- let [leftValue, topValue] = [0, 0];
90
- if (j > 0) {
91
- leftValue = arr[i][j - 1];
92
- }
93
- if (i > 0) {
94
- topValue = arr[i - 1][j];
95
- }
96
- arr[i][j] = Math.max(leftValue, topValue);
97
- }
98
- }
99
- }
100
- // 二维数组转一维数组
101
- const arr1 = Array.prototype.concat.apply([], arr);
102
- // 获取最长公共子串
103
- const maxLong = Math.max(...arr1);
104
- return maxLong;
105
- }
106
-
107
- /**
108
- * Get the query param's value of the current Web URL(`location.search`).
109
- *
110
- * ```
111
- * // http://example.com/?t1=1&t2=2&t3=3&t4=4#2333
112
- * // ?t1=1&t2=2&t3=3&t4=4
113
- * getQueryParam('t3'); // 3
114
- * getQueryParam('t4'); // 4
115
- * ```
116
- *
117
- * @param {string} param Query param.
118
- * @returns {string} value
119
- * @category URL
120
- */
121
- export function getQueryParam(param: string): string {
122
- const reg = new RegExp('(^|&)' + param + '=([^&]*)(&|$)');
123
- const r = location.search.substr(1).match(reg);
124
- if (r !== null) {
125
- // return decodeURIComponent(unescape(r[2]));
126
- return decodeURIComponent(r[2]);
127
- }
128
- return '';
129
- }
130
-
131
- /**
132
- * Get the query param's value of the input URL.
133
- *
134
- * ```
135
- * getUrlParam('http://example.com/?t1=1&t2=2&t3=3&t4=4', 't3'); // 3
136
- * getUrlParam('http://example.com/?t1=1&t2=2&t3=3&t4=4', 't4'); // 4
137
- * ```
138
- *
139
- * @param {string} url URL string.
140
- * @param {string} param Query param.
141
- * @returns {string} value
142
- * @category URL
143
- */
144
- export function getUrlParam(url: string, param: string): string | string[] {
145
- const result: {
146
- [key: string]: string | string[] | any;
147
- } = {};
148
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
149
- url.replace(/\??(\w+)=([^&]*)&?/g, function(a, k, v): any {
150
- if (result[k] !== undefined) {
151
- const t = result[k];
152
- result[k] = [].concat(t, v);
153
- } else {
154
- result[k] = v;
155
- }
156
- });
157
- // if (param === undefined) {
158
- // return result;
159
- // } else {
160
- // return result[param] || '';
161
- // }
162
- return result[param] || '';
163
- }
164
-
165
- /**
166
- * Update the query param's value of the input URL.
167
- *
168
- * Usage:
169
- *
170
- * ```
171
- * updateQueryParam('http://example.com/?t1=1&t2=2&t3=3&t4=4', 't3', 'three');
172
- * updateQueryParam('http://example.com/?t1=1&t2=2&t3=3&t4=4', 't4', 'four');
173
- * ```
174
- *
175
- * Output:
176
- *
177
- * ```
178
- * http://example.com/?t1=1&t2=2&t3=three&t4=4
179
- * http://example.com/?t1=1&t2=2&t3=3&t4=four
180
- * ```
181
- *
182
- * @param {string} url URL string.
183
- * @param {string} param Query param.
184
- * @param {string} value Param's value.
185
- * @returns {string} URL.
186
- * @category URL
187
- */
188
- export function updateQueryParam(
189
- url: string,
190
- param: string,
191
- value: string
192
- ): string {
193
- const re = new RegExp('([?&])' + param + '=.*?(&|$)', 'i');
194
- const separator = url.indexOf('?') !== -1 ? '&' : '?';
195
- if (url.match(re)) {
196
- return url.replace(re, '$1' + param + '=' + value + '$2');
197
- } else {
198
- return url + separator + param + '=' + value;
199
- }
200
- }
201
-
202
- /**
203
- * Get the hash query param's value of the current Web URL(`location.hash`).
204
- *
205
- * Usage:
206
- *
207
- * ```
208
- * // http://example.com/?#2333?t1=1&t2=2&t3=3&t4=4
209
- * // #2333?t1=1&t2=2&t3=3&t4=4
210
- * getHashQueryParam('t3');
211
- * getHashQueryParam('t4');
212
- * ```
213
- *
214
- * Output:
215
- *
216
- * ```
217
- * 3
218
- * 4
219
- * ```
220
- *
221
- * @param {string} param Query param.
222
- * @returns {string} value
223
- * @category URL
224
- */
225
- export function getHashQueryParam(param: string): string {
226
- const hashs = location.hash.split('?');
227
- if (hashs.length === 1) {
228
- return '';
229
- }
230
- const reg = new RegExp(`(^|&)${param}=([^&]*)(&|$)`);
231
- const ret = hashs[1].match(reg);
232
- return ret ? ret[2] : '';
233
- }
234
-
235
- /**
236
- * Get the domain of URL, and other params.
237
- *
238
- * Usage:
239
- *
240
- * ```
241
- * getDomain('http://example.com/?t1=1&t2=2&t3=3&t4=4');
242
- * getDomain('http://example.com/test/thanks?t1=1&t2=2&t3=3&t4=4', ['hostname', 'pathname']);
243
- * ```
244
- *
245
- * Output:
246
- *
247
- * ```
248
- * example.com
249
- * example.com/test/thanks
250
- * ```
251
- *
252
- * @param {string} url
253
- * @param {array} rules Object.keys(location), ['href', 'protocol', 'host', 'hostname', 'port', 'pathname', 'search', 'hash'], ['hostname', 'pathname'] = 'km.mazey.net/plugins/servlet/mobile'
254
- * @category URL
255
- */
256
- export function getDomain(url: string, rules = ['hostname']): string {
257
- const aEl: any = document.createElement('a');
258
- aEl.href = url;
259
- return rules.reduce((ret, v) => {
260
- ret += aEl[v];
261
- return ret;
262
- }, '');
263
- }
264
-
265
- /**
266
- * Transfer CamelCase to KebabCase.
267
- *
268
- * ```
269
- * camelCaseToKebabCase('ABC'); // a-b-c
270
- * camelCaseToKebabCase('aBC'); // a-b-c
271
- * ```
272
- *
273
- * @param {string} camelCase 'aBC' or 'ABC'
274
- * @returns {string} 'a-b-c'
275
- * @category Util
276
- */
277
- export function camelCaseToKebabCase(camelCase: string): string {
278
- const kebabCase = camelCase.replace(/([A-Z])/g, '-$1').toLowerCase();
279
- return kebabCase[0] === '-' ? kebabCase.substr(1) : kebabCase;
280
- }
281
-
282
- /**
283
- * Transfer CamelCase to Underscore.
284
- *
285
- * ```
286
- * camelCase2Underscore('ABC'); // a_b_c
287
- * camelCase2Underscore('aBC'); // a_b_c
288
- * ```
289
- *
290
- * @param {string} camelCase 'aBC' or 'ABC'
291
- * @returns {string} 'a_b_c'
292
- * @category Util
293
- */
294
- export function camelCase2Underscore(camelCase: string): string {
295
- const kebabCase = camelCase.replace(/([A-Z])/g, '_$1').toLowerCase();
296
- return kebabCase[0] === '_' ? kebabCase.substr(1) : kebabCase;
297
- }
298
-
299
- /**
300
- * Remove leading and trailing whitespace or specified characters from string.
301
- *
302
- * ```
303
- * mTrim(' 1 2 3 '); // '1 2 3'
304
- * mTrim('abc '); // 'abc'
305
- * ```
306
- *
307
- * @param {string} str The string to trim.
308
- * @returns {string} Trimmed string.
309
- * @category Util
310
- */
311
- export function mTrim(str: string): string {
312
- str = str.replace(/^\s+/, ''); // 去除头部空格
313
- let end = str.length - 1;
314
- const ws = /\s/;
315
- while (ws.test(str.charAt(end))) {
316
- end--; // 最后一个非空格字符的索引
317
- }
318
- return str.slice(0, end + 1);
319
- }
320
-
321
- /**
322
- * Make a newline of HTML.
323
- *
324
- * ```
325
- * newLine('a\nb\nc'); // 'a<br />b<br />c'
326
- * newLine('a\n\nbc'); // 'a<br /><br />bc'
327
- * ```
328
- *
329
- * @param {string} str The string to make a newline.
330
- * @returns {string} A newline with `br`.
331
- * @category DOM
332
- */
333
- export function newLine(str: string): string {
334
- if (!str) {
335
- return '';
336
- }
337
- const reg = new RegExp('\\n', 'g');
338
- return str.replace(reg, '<br />');
339
- }
340
-
341
- /**
342
- * Clone Object deeply.
343
- *
344
- * Usage:
345
- *
346
- * ```
347
- * deepCopyObject(['a', 'b', 'c']);
348
- * deepCopyObject('abc');
349
- * ```
350
- *
351
- * Output:
352
- *
353
- * ```
354
- * ['a', 'b', 'c']
355
- * 'abc'
356
- * ```
357
- *
358
- * @param {object} obj The value to clone.
359
- * @returns {object} Returns the deep cloned value.
360
- * @category Util
361
- */
362
- export function deepCopyObject(obj: any): any {
363
- return JSON.parse(JSON.stringify(obj));
364
- }
365
-
366
- /**
367
- * Check whether it is a valid JSON string.
368
- *
369
- * Usage:
370
- *
371
- * ```
372
- * isJsonString(`['a', 'b', 'c']`);
373
- * isJsonString(`["a", "b", "c"]`);
374
- * ```
375
- *
376
- * Output:
377
- *
378
- * ```
379
- * false
380
- * true
381
- * ```
382
- *
383
- * @param {string} str The string to check.
384
- * @returns {boolean} Return the result of checking.
385
- * @category Util
386
- */
387
- export function isJsonString(str: string): boolean {
388
- try {
389
- if (typeof JSON.parse(str) === 'object') {
390
- return true;
391
- }
392
- } catch (e) {
393
- /* pass */
394
- }
395
- return false;
396
- }
397
-
398
- /**
399
- * Produce a random string of number, `generateRndNum(7)` => '7658495'.
400
- *
401
- * ```
402
- * generateRndNum(4); // '9730'
403
- * generateRndNum(7); // '2262490'
404
- * ```
405
- *
406
- * @param {number} n Length
407
- * @returns {string} Return the random string.
408
- * @category Util
409
- */
410
- export function generateRndNum(n = 5): string {
411
- let ret = '';
412
- while (n--) {
413
- ret += Math.floor(Math.random() * 10);
414
- }
415
- return ret;
416
- }
417
-
418
- /**
419
- * @method generateUniqueNum
420
- * @description 根据时间生成唯一标志的数字 mGenerateUniqueNum() => 1538324722364123。
421
- * @param {number} n 随机数的长度
422
- */
423
- export function generateUniqueNum(n = 3): string {
424
- const [now, rnd] = [mNow(), generateRndNum(n || 3)];
425
- return now + rnd;
426
- }
427
-
428
- /**
429
- * @method floatToPercent
430
- * @description 浮点数转为百分比 0.2 => 20%。
431
- * @param {number} num 浮点数
432
- * @param {number} fixSize 保留几位浮点数
433
- */
434
- export function floatToPercent(num: number, fixSize = 0): string {
435
- let ret = '';
436
- if (fixSize) {
437
- ret = (num * 100).toFixed(fixSize);
438
- } else {
439
- ret = String(Math.floor(num * 100));
440
- }
441
- return `${ret}%`;
442
- }
443
-
444
- /**
445
- * @method floatFixed
446
- * @description 浮点数保留指定位。
447
- */
448
- export function floatFixed(num: string, size = 0): string {
449
- return parseFloat(num).toFixed(size);
450
- }
451
-
452
- /**
453
- * @method cancelBubble
454
- * @description 阻止冒泡。
455
- */
456
- export function cancelBubble(e: any): void {
457
- const ev = e || window.event;
458
- if (ev.stopPropagation) {
459
- // W3C
460
- ev.stopPropagation();
461
- } else {
462
- // IE
463
- ev.cancelBubble = true;
464
- }
465
- }
466
-
467
- /**
468
- * Modify `class`.
469
- *
470
- * ```
471
- * const dom = document.querySelector('#box');
472
- *
473
- * // Determine `class`
474
- * hasClass(dom, 'test');
475
- * // Add `class`
476
- * addClass(dom, 'test');
477
- * // Remove `class`
478
- * removeClass(dom, 'test');
479
- * ```
480
- *
481
- * @category DOM
482
- */
483
- export function hasClass(obj: any, cls: string): boolean {
484
- const oriCls = obj.className; // 获取对象的class值
485
- const oriClsArr = oriCls.split(/\s+/); // 分隔空格转换成数组
486
- for (let i = 0; i < oriClsArr.length; i++) {
487
- if (oriClsArr[i] === cls) {
488
- return true; // 若匹配到class则返回True
489
- }
490
- }
491
- return false; // 否则返回False
492
- }
493
-
494
- /**
495
- * Modify `class`.
496
- *
497
- * ```
498
- * const dom = document.querySelector('#box');
499
- *
500
- * // Determine `class`
501
- * hasClass(dom, 'test');
502
- * // Add `class`
503
- * addClass(dom, 'test');
504
- * // Remove `class`
505
- * removeClass(dom, 'test');
506
- * ```
507
- *
508
- * @category DOM
509
- */
510
- export function addClass(obj: any, cls: string): void {
511
- const oriCls = obj.className;
512
- let space = '';
513
- let newCls = ''; // 获取对象的class值
514
- if (oriCls !== '') {
515
- space = ' '; // 若原来的class不为空,跟一个空格
516
- }
517
- newCls = oriCls + space + cls; // 将新的class加进去
518
- obj.className = newCls; // 替换新class
519
- }
520
-
521
- /**
522
- * Modify `class`.
523
- *
524
- * ```
525
- * const dom = document.querySelector('#box');
526
- *
527
- * // Determine `class`
528
- * hasClass(dom, 'test');
529
- * // Add `class`
530
- * addClass(dom, 'test');
531
- * // Remove `class`
532
- * removeClass(dom, 'test');
533
- * ```
534
- *
535
- * @category DOM
536
- */
537
- export function removeClass(obj: any, cls: string): void {
538
- const oriCls = obj.className;
539
- let newCls; // 获取对象的class值
540
- newCls = ' ' + oriCls + ' '; // 前后加空格
541
- newCls = newCls.replace(/(\s+)/gi, ' '); // 将多余的空格替换成一个空格
542
- newCls = newCls.replace(' ' + cls + ' ', ' '); // 将加了前后空格的cls替换成空格' '
543
- newCls = newCls.replace(/(^\s+)|(\s+$)/g, ''); // 去掉前后空格
544
- obj.className = newCls;
545
- }
546
-
547
- /**
548
- * EN: Throttle
549
- *
550
- * ZH: 节流
551
- *
552
- * ```
553
- * const foo = throttle(() => {
554
- * console.log('The function will be invoked at most once per every wait 1000 milliseconds.');
555
- * }, 1000, { leading: true });
556
- * ```
557
- *
558
- * Reference: [Lodash](https://lodash.com/docs/4.17.15#throttle)
559
- *
560
- * @category Util
561
- */
562
- export function throttle(
563
- func: any,
564
- wait: number,
565
- options: { leading?: boolean; trailing?: boolean } = {}
566
- ): any {
567
- options = Object.assign({}, options);
568
- // timeout: setTimeout Handle
569
- // previous: 上次时间戳
570
- let context: any = null;
571
- let args: any = null;
572
- let timeout: any = null;
573
- let [result, previous] = [null, 0];
574
- const later = function() {
575
- previous = options.leading === false ? 0 : mNow();
576
- timeout = null;
577
- result = func.apply(context, args);
578
- if (!timeout) {
579
- context = args = null;
580
- }
581
- };
582
- return function(...argRest: Array<any>) {
583
- const now = mNow();
584
- if (!previous && options.leading === false) {
585
- previous = now;
586
- }
587
- const remaining = wait - (now - previous);
588
- context = this;
589
- args = argRest;
590
- if (remaining <= 0 || remaining > wait) {
591
- if (timeout) {
592
- clearTimeout(timeout);
593
- timeout = null;
594
- }
595
- previous = now;
596
- result = func.apply(context, args);
597
- if (!timeout) {
598
- context = args = null;
599
- }
600
- } else if (!timeout && options.trailing !== false) {
601
- timeout = setTimeout(later, remaining);
602
- }
603
- return result;
604
- };
605
- }
606
-
607
- /**
608
- * EN: Debounce
609
- *
610
- * ZH: 去抖
611
- *
612
- * ```
613
- * const foo = debounce(() => {
614
- * console.log('The debounced function will only be invoked in 1000 milliseconds, the other invoking will disappear during the wait time.');
615
- * }, 1000, true);
616
- * ```
617
- *
618
- * @category Util
619
- */
620
- export function debounce(func: any, wait: number, immediate?: any): any {
621
- let context: any = null;
622
- let timeout: any = null;
623
- let timestamp: any = null;
624
- let args: any = null;
625
- let [result] = [null];
626
- const later = function() {
627
- const last = mNow() - timestamp;
628
- if (last < wait && last >= 0) {
629
- timeout = setTimeout(later, wait - last);
630
- } else {
631
- timeout = null;
632
- if (!immediate) {
633
- result = func.apply(context, args);
634
- if (!timeout) {
635
- context = args = null;
636
- }
637
- }
638
- }
639
- };
640
- return function(...argRest: Array<any>) {
641
- context = this;
642
- args = argRest;
643
- timestamp = mNow();
644
- const callNow = immediate && !timeout;
645
- if (!timeout) {
646
- timeout = setTimeout(later, wait);
647
- }
648
- if (callNow) {
649
- result = func.apply(context, args);
650
- context = args = null;
651
- }
652
- return result;
653
- };
654
- }
655
-
656
- /**
657
- * @method friendlyInterval
658
- * @description 获取间隔时间。
659
- * @param {number/Date} start 开始时间戳 1585325367122
660
- * @param {number/Date} end 结束时间戳 1585325367122
661
- * @param {string} options.type 返回类型 d: 2(天) text: 2 天 4 时...
662
- * @returns {string/number} 取决于 type
663
- */
664
- export function friendlyInterval(
665
- start = 0,
666
- end = 0,
667
- options: { type?: string } = { type: 'd' }
668
- ): number | string {
669
- const { type } = options;
670
- if (!isNumber(start)) start = new Date(start).getTime();
671
- if (!isNumber(end)) end = new Date(end).getTime();
672
- const t = end - start;
673
- let ret = '';
674
- let [d, h, m, s] = new Array(4).fill(0);
675
- if (t >= 0) {
676
- d = Math.floor(t / 1000 / 60 / 60 / 24);
677
- h = Math.floor(t / 1000 / 60 / 60);
678
- m = Math.floor(t / 1000 / 60);
679
- s = Math.floor(t / 1000);
680
- switch (type) {
681
- case 'd':
682
- ret = d;
683
- break;
684
- case 'text':
685
- d = Math.floor(t / 1000 / 60 / 60 / 24);
686
- h = Math.floor((t / 1000 / 60 / 60) % 24);
687
- m = Math.floor((t / 1000 / 60) % 60);
688
- s = Math.floor((t / 1000) % 60);
689
- ret = d + ' 天 ' + h + ' 时 ' + m + ' 分 ' + s + ' 秒';
690
- break;
691
- default:
692
- ret = s;
693
- }
694
- }
695
- return ret;
696
- }
697
-
698
- /**
699
- * EN: Check whether it is a right number.
700
- *
701
- * ZH: 判断是否有效数字
702
- *
703
- * ```
704
- * isNumber(123); // true
705
- * isNumber('123'); // false
706
- * // Default: NaN, Infinity is not Number
707
- * isNumber(Infinity); // false
708
- * isNumber(Infinity, { isUnFiniteAsNumber: true }); // true
709
- * isNumber(NaN); // false
710
- * isNumber(NaN, { isNaNAsNumber: true, isUnFiniteAsNumber: true }); // true
711
- * ```
712
- *
713
- * @param {*} num 被判断的值
714
- * @param {boolean} options.isNaNAsNumber 是否 NaN 算数字(默认不算)
715
- * @param {boolean} options.isUnFiniteAsNumber 是否 无限 算数字(默认不算)
716
- * @returns {boolean} true 是数字
717
- * @category Util
718
- */
719
- export function isNumber(
720
- num: any,
721
- options: { isNaNAsNumber?: boolean; isUnFiniteAsNumber?: boolean } = {
722
- isNaNAsNumber: false,
723
- isUnFiniteAsNumber: false
724
- }
725
- ): boolean {
726
- const { isNaNAsNumber, isUnFiniteAsNumber } = Object.assign(
727
- { isNaNAsNumber: false, isUnFiniteAsNumber: false },
728
- options
729
- );
730
- let ret = true;
731
- // 数字类型
732
- if (typeof num !== 'number') {
733
- ret = false;
734
- }
735
- // 无限值
736
- if (isUnFiniteAsNumber === false && !isFinite(num)) {
737
- // console.log('1333');
738
- ret = false;
739
- }
740
- // NaN
741
- if (isNaNAsNumber === false && isNaN(num)) {
742
- // console.log('2333');
743
- ret = false;
744
- }
745
- return ret;
746
- }
747
-
748
- /**
749
- * @method doFn
750
- * @description 执行有效函数
751
- * @param {function} fn 等待被执行的未知是否有效的函数
752
- */
753
- export function doFn(fn: any, ...params: any[]): any {
754
- let ret = null;
755
- if (fn && typeof fn === 'function') {
756
- ret = fn(...params);
757
- }
758
- return ret;
759
- }
760
-
761
- /**
762
- * EN: Handle Storage (Keep fit for JSON, it can tansfer format automatically).
763
- *
764
- * ZH: 存储/获取数据到 sessionStorage/localStorage
765
- *
766
- * ```
767
- * setSessionStorage('test', '123');
768
- * getSessionStorage('test'); // 123
769
- * setLocalStorage('test', '123');
770
- * getLocalStorage('test'); // 123
771
- *
772
- * // or package in usage
773
- * const projectName = 'mazey';
774
- * function mSetLocalStorage (key, value) {
775
- * return setLocalStorage(`${projectName}_${key}`, value);
776
- * }
777
- *
778
- * function mGetLocalStorage (key) {
779
- * return getLocalStorage(`${projectName}_${key}`);
780
- * }
781
- * ```
782
- *
783
- * @param {string} key 键
784
- * @returns {any} 返回值
785
- * @category Cache Data
786
- */
787
- export function setSessionStorage(key: string, value: any = null): void {
788
- if (key) {
789
- sessionStorage.setItem(key, JSON.stringify(value));
790
- }
791
- }
792
-
793
- /**
794
- * EN: Handle Storage (Keep fit for JSON, it can tansfer format automatically).
795
- *
796
- * ZH: 存储/获取数据到 sessionStorage/localStorage
797
- *
798
- * ```
799
- * setSessionStorage('test', '123');
800
- * getSessionStorage('test'); // 123
801
- * setLocalStorage('test', '123');
802
- * getLocalStorage('test'); // 123
803
- *
804
- * // or package in usage
805
- * const projectName = 'mazey';
806
- * function mSetLocalStorage (key, value) {
807
- * return setLocalStorage(`${projectName}_${key}`, value);
808
- * }
809
- *
810
- * function mGetLocalStorage (key) {
811
- * return getLocalStorage(`${projectName}_${key}`);
812
- * }
813
- * ```
814
- *
815
- * @param {string} key 键
816
- * @returns {any} 返回值
817
- * @category Cache Data
818
- */
819
- export function getSessionStorage(key: string): any {
820
- let ret = null;
821
- if (key) {
822
- const value = sessionStorage.getItem(key);
823
- if (value) {
824
- ret = JSON.parse(value);
825
- }
826
- }
827
- return ret;
828
- }
829
-
830
- /**
831
- * EN: Handle Storage (Keep fit for JSON, it can tansfer format automatically).
832
- *
833
- * ZH: 存储/获取数据到 sessionStorage/localStorage
834
- *
835
- * ```
836
- * setSessionStorage('test', '123');
837
- * getSessionStorage('test'); // 123
838
- * setLocalStorage('test', '123');
839
- * getLocalStorage('test'); // 123
840
- *
841
- * // or package in usage
842
- * const projectName = 'mazey';
843
- * function mSetLocalStorage (key, value) {
844
- * return setLocalStorage(`${projectName}_${key}`, value);
845
- * }
846
- *
847
- * function mGetLocalStorage (key) {
848
- * return getLocalStorage(`${projectName}_${key}`);
849
- * }
850
- * ```
851
- *
852
- * @param {string} key 键
853
- * @returns {any} 返回值
854
- * @category Cache Data
855
- */
856
- export function setLocalStorage(key: string, value: any = null): void {
857
- if (key) {
858
- localStorage.setItem(key, JSON.stringify(value));
859
- }
860
- }
861
-
862
- /**
863
- * EN: Handle Storage (Keep fit for JSON, it can tansfer format automatically).
864
- *
865
- * ZH: 存储/获取数据到 sessionStorage/localStorage
866
- *
867
- * ```
868
- * setSessionStorage('test', '123');
869
- * getSessionStorage('test'); // 123
870
- * setLocalStorage('test', '123');
871
- * getLocalStorage('test'); // 123
872
- *
873
- * // or package in usage
874
- * const projectName = 'mazey';
875
- * function mSetLocalStorage (key, value) {
876
- * return setLocalStorage(`${projectName}_${key}`, value);
877
- * }
878
- *
879
- * function mGetLocalStorage (key) {
880
- * return getLocalStorage(`${projectName}_${key}`);
881
- * }
882
- * ```
883
- *
884
- * @param {string} key 键
885
- * @returns {any} 返回值
886
- * @category Cache Data
887
- */
888
- export function getLocalStorage(key: string): any {
889
- let ret = null;
890
- if (key) {
891
- const value = localStorage.getItem(key);
892
- if (value) {
893
- ret = JSON.parse(value);
894
- }
895
- }
896
- return ret;
897
- }
898
-
899
- /**
900
- * EN: Load a CSS file from the server.
901
- *
902
- * ZH: 动态加载 CSS 文件
903
- *
904
- * ```
905
- * loadCSS(
906
- * 'http://example.com/css/mazey-base.css',
907
- * {
908
- * id: 'iamid', // Optional, link ID, default none
909
- * }
910
- * )
911
- * .then(
912
- * res => {
913
- * console.log(`Load CSS Success: ${res}`);
914
- * }
915
- * )
916
- * .catch(
917
- * err => {
918
- * console.error(`Load CSS Fail: ${err.message}`)
919
- * }
920
- * );
921
- * ```
922
- *
923
- * @param {string} url -- css资源路径
924
- * @param {string} options.id -- link标签id
925
- * @returns {Promise<boolean>} true -- 加载成功
926
- * @category Load Resource
927
- */
928
- export function loadCSS(
929
- url: string,
930
- options: { id?: string } = { id: '' }
931
- ): Promise<boolean | Error | any> {
932
- const { id } = options;
933
- let success: (v: boolean) => void;
934
- let fail: (v: Error) => void;
935
- const status = new Promise((resolve, reject) => {
936
- [success, fail] = [resolve, reject];
937
- });
938
- // const tempCB = (typeof callback === 'function' ? callback : function () { });
939
- const callback = function() {
940
- // doFn(success, true);
941
- success(true);
942
- };
943
- let node: any = document.createElement('link');
944
- const supportOnload = 'onload' in node;
945
- const isOldWebKit =
946
- +navigator.userAgent.replace(
947
- /.*(?:AppleWebKit|AndroidWebKit)\/?(\d+).*/i,
948
- '$1'
949
- ) < 536; // webkit旧内核做特殊处理
950
- const protectNum = 300000; // 阈值10分钟,一秒钟执行pollCss 500次
951
-
952
- node.rel = 'stylesheet';
953
- node.type = 'text/css';
954
- node.href = url;
955
- if (typeof id !== 'undefined') {
956
- node.id = id;
957
- }
958
- document.getElementsByTagName('head')[0].appendChild(node);
959
-
960
- // for Old WebKit and Old Firefox
961
- if (isOldWebKit || !supportOnload) {
962
- // Begin after node insertion
963
- setTimeout(function() {
964
- pollCss(node, callback, 0);
965
- }, 1);
966
- return status;
967
- }
968
-
969
- if (supportOnload) {
970
- node.onload = onload;
971
- node.onerror = function() {
972
- // 加载失败(404)
973
- onload();
974
- };
975
- } else {
976
- // todo: 和 !supportOnload 重复
977
- node.onreadystatechange = function() {
978
- if (/loaded|complete/.test(node.readyState)) {
979
- onload();
980
- }
981
- };
982
- }
983
-
984
- function onload() {
985
- // 确保只跑一次下载操作
986
- node.onload = node.onerror = node.onreadystatechange = null;
987
-
988
- // 清空node引用,在低版本IE,不清除会造成内存泄露
989
- node = null;
990
-
991
- callback();
992
- }
993
-
994
- // 循环判断css是否已加载成功
995
- /*
996
- * @param node -- link节点
997
- * @param callback -- 回调函数
998
- * @param step -- 计步器,避免无限循环
999
- */
1000
- function pollCss(node: any, callback: any, step: number) {
1001
- const sheet = node.sheet;
1002
- let isLoaded: any;
1003
-
1004
- step += 1;
1005
-
1006
- // 保护,大于10分钟,则不再轮询
1007
- if (step > protectNum) {
1008
- isLoaded = true;
1009
-
1010
- // 清空node引用
1011
- node = null;
1012
-
1013
- callback();
1014
- return;
1015
- }
1016
-
1017
- if (isOldWebKit) {
1018
- // for WebKit < 536
1019
- if (sheet) {
1020
- isLoaded = true;
1021
- }
1022
- } else if (sheet) {
1023
- // for Firefox < 9.0
1024
- try {
1025
- if (sheet.cssRules) {
1026
- isLoaded = true;
1027
- }
1028
- } catch (ex) {
1029
- // 火狐特殊版本,通过特定值获知是否下载成功
1030
- // The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
1031
- // to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
1032
- // in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
1033
- if ((ex as any).name === 'NS_ERROR_DOM_SECURITY_ERR') {
1034
- isLoaded = true;
1035
- }
1036
- }
1037
- }
1038
-
1039
- setTimeout(function() {
1040
- if (isLoaded) {
1041
- // 延迟20ms是为了给下载的样式留够渲染的时间
1042
- callback();
1043
- } else {
1044
- pollCss(node, callback, step);
1045
- }
1046
- }, 20);
1047
- }
1048
- return status;
1049
- }
1050
-
1051
- /**
1052
- * EN: Load a JavaScript file from the server and execute it.
1053
- *
1054
- * ZH: 动态加载 JavaScript 文件
1055
- *
1056
- * ```
1057
- * loadScript(
1058
- * 'http://example.com/static/js/plugin-2.1.1.min.js',
1059
- * {
1060
- * id: 'iamid', // (Optional) script ID, default none
1061
- * timeout: 5000, // (Optional) timeout, default `5000`
1062
- * isDefer: false, // (Optional) defer, default `false`
1063
- * }
1064
- * )
1065
- * .then(
1066
- * res => {
1067
- * console.log(`Load JavaScript script: ${res}`);
1068
- * }
1069
- * )
1070
- * .catch(
1071
- * err => {
1072
- * console.error(`Load JavaScript script: ${err.message}`)
1073
- * }
1074
- * );
1075
- * ```
1076
- *
1077
- * @param {string} url -- JavaScript 资源路径
1078
- * @param {string} options.id -- DOM ID
1079
- * @param {function} options.callback -- 加载后回调函数
1080
- * @param {number} options.timeout -- 超时时长
1081
- * @param {boolean} options.isDefer -- 是否添加 defer 标签
1082
- * @returns {Promise<boolean>} -- true 成功
1083
- * @category Load Resource
1084
- */
1085
- export function loadScript(
1086
- url: string,
1087
- options: {
1088
- id?: string;
1089
- callback?: (...params: any[]) => any;
1090
- timeout?: number;
1091
- isDefer?: boolean;
1092
- } = {
1093
- id: '',
1094
- callback: function() {
1095
- /* pass */
1096
- },
1097
- timeout: 5000,
1098
- isDefer: false
1099
- }
1100
- ): Promise<boolean | string | Error> {
1101
- const { id, callback, timeout, isDefer } = Object.assign(
1102
- {
1103
- id: '',
1104
- callback: function() {
1105
- /* pass */
1106
- },
1107
- timeout: 5000,
1108
- isDefer: false
1109
- },
1110
- options
1111
- );
1112
- let success: any = null;
1113
- let fail: any = null;
1114
- const script: any = document.createElement('script');
1115
- // 如果没有 script 标签,那么代码就不会运行。可以利用这一事实,在页面的第一个 script 标签上使用 insertBefore()。
1116
- const firstScript: any = document.getElementsByTagName('script')[0];
1117
- script.type = 'text/javascript';
1118
- if (isDefer) {
1119
- script.defer = 'defer';
1120
- }
1121
- if (id) {
1122
- script.id = id;
1123
- }
1124
- if (script.readyState) {
1125
- // IE
1126
- script.onreadystatechange = function() {
1127
- if (script.readyState === 'loaded' || script.readyState === 'complete') {
1128
- script.onreadystatechange = null;
1129
- doFn(callback);
1130
- doFn(success, true);
1131
- }
1132
- };
1133
- } else {
1134
- // Others
1135
- script.onload = function() {
1136
- doFn(callback);
1137
- doFn(success, true);
1138
- };
1139
- }
1140
- script.src = url;
1141
- firstScript && firstScript.parentNode.insertBefore(script, firstScript);
1142
- return new Promise((resolve, reject) => {
1143
- [success, fail] = [resolve, reject];
1144
- if (timeout) {
1145
- setTimeout(fail.bind(null, Error('timeout')), timeout);
1146
- }
1147
- });
1148
- }
1149
-
1150
- /**
1151
- * @method mNow
1152
- * @description 获取时间戳
1153
- */
1154
- export function mNow(): number {
1155
- let ret = 0;
1156
- if (Date.now) {
1157
- ret = Date.now();
1158
- } else {
1159
- ret = new Date().getTime();
1160
- }
1161
- return ret;
1162
- }
1163
-
1164
- /**
1165
- * EN: Handle Cookie.
1166
- *
1167
- * ZH: 设置/获取 Cookie
1168
- *
1169
- * ```
1170
- * setCookie('test', '123', 30, 'example.com'); // key value day domain
1171
- * getCookie('test'); // 123
1172
- * ```
1173
- *
1174
- * @category Cache Data
1175
- */
1176
- export function setCookie(
1177
- name: string,
1178
- value: string,
1179
- days: number,
1180
- domain: string
1181
- ): void {
1182
- let domainParts, expires;
1183
- let date: any;
1184
- if (days) {
1185
- date = new Date();
1186
- date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
1187
- expires = '; expires=' + date.toGMTString();
1188
- } else {
1189
- expires = '';
1190
- }
1191
- const host = location.host;
1192
- if (host.split('.').length === 1) {
1193
- // no "." in a domain - it's localhost or something similar
1194
- document.cookie = name + '=' + value + expires + '; path=/';
1195
- } else {
1196
- // Remember the cookie on all subdomains.
1197
- //
1198
- // Start with trying to set cookie to the top domain.
1199
- // (example: if user is on foo.com, try to set
1200
- // cookie to domain ".com")
1201
- //
1202
- // If the cookie will not be set, it means ".com"
1203
- // is a top level domain and we need to
1204
- // set the cookie to ".foo.com"
1205
- domainParts = host.split('.');
1206
- domainParts.shift();
1207
- domain = domain || '.' + domainParts.join('.');
1208
- document.cookie =
1209
- name + '=' + value + expires + '; path=/; domain=' + domain;
1210
- // check if cookie was successfuly set to the given domain
1211
- // (otherwise it was a Top-Level Domain)
1212
- if (getCookie(name) === null || getCookie(name) !== value) {
1213
- // append "." to current domain
1214
- domain = domain || '.' + host;
1215
- document.cookie =
1216
- name + '=' + value + expires + '; path=/; domain=' + domain;
1217
- }
1218
- }
1219
- }
1220
-
1221
- /**
1222
- * EN: Handle Cookie.
1223
- *
1224
- * ZH: 设置/获取 Cookie
1225
- *
1226
- * ```
1227
- * setCookie('test', '123', 30, 'example.com'); // key value day domain
1228
- * getCookie('test'); // 123
1229
- * ```
1230
- *
1231
- * @category Cache Data
1232
- */
1233
- export function getCookie(name: string): string {
1234
- const nameEQ = name + '=';
1235
- const ca = document.cookie.split(';');
1236
- for (let i = 0; i < ca.length; i++) {
1237
- let c = ca[i];
1238
- while (c.charAt(0) == ' ') {
1239
- c = c.substring(1, c.length);
1240
- }
1241
- if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
1242
- }
1243
- return '';
1244
- }
1245
-
1246
- /**
1247
- * @hidden
1248
- */
1249
- interface WebPerformance {
1250
- [key: string]: string | number;
1251
- }
1252
-
1253
- /**
1254
- * EN: Get page load time(PerformanceTiming).
1255
- *
1256
- * ZH: 获取页面加载相关的各项数据
1257
- *
1258
- * Usage:
1259
- *
1260
- * ```
1261
- * // `camelCase:false` (Default) Return underline data.
1262
- * // `camelCase:true` Return hump data.
1263
- * getPerformance()
1264
- * .then(res => {
1265
- * console.log(JSON.stringify(res));
1266
- * })
1267
- * .catch(console.error);
1268
- * ```
1269
- *
1270
- * Output:
1271
- *
1272
- * ```
1273
- * {"os":"ios","os_version":"13_2_3","device_type":"phone","network":"4g","unload_time":0,"redirect_time":0,"dns_time":0,"tcp_time":0,"response_time":289,"download_time":762,"first_paint_time":469,"first_contentful_paint_time":469,"domready_time":1318,"onload_time":2767,"white_time":299,"render_time":2768,"decoded_body_size":979570,"encoded_body_size":324938}
1274
- * ```
1275
- *
1276
- * Results:
1277
- *
1278
- * | Index | Field | Description |
1279
- * | --- | --- | --- |
1280
- * | DNS lookup | dns_time | domainLookupEnd - domainLookupStart |
1281
- * | Connection negotiation | tcp_time | connectEnd - connectStart |
1282
- * | Requests and responses | response_time | responseStart - requestStart |
1283
- * | White screen | white_time | responseStart - navigationStart |
1284
- * | DomReady | domready_time | domContentLoadedEventStart - navigationStart |
1285
- * | Onload | onload_time | loadEventStart - navigationStart |
1286
- * | EventEnd | render_time | loadEventEnd -navigationStart |
1287
- * | Unload | unload_time | (Optional) unloadEventEnd - unloadEventStart |
1288
- * | Redirect | redirect_time | (Optional) redirectEnd - redirectStart |
1289
- * | SSL | ssl_time | (Optional) connectEnd - secureConnectionStart |
1290
- * | Download | download_time | (Optional) responseEnd - responseStart |
1291
- *
1292
- * @param {boolean} camelCase -- false(默认) 以下划线形式返回数据 true 以驼峰形式返回数据
1293
- * @returns {Promise<object>} 加载数据
1294
- * @category Web Performance
1295
- */
1296
- export function getPerformance(
1297
- camelCase = false
1298
- ): Promise<WebPerformance | Error> {
1299
- let success: (v: WebPerformance) => void;
1300
- let fail: (v: Error) => void;
1301
- const status: Promise<WebPerformance> = new Promise((resolve, reject) => {
1302
- [success, fail] = [resolve, reject];
1303
- });
1304
- const timing = window.performance.timing;
1305
- const startTime = timing.navigationStart || timing.fetchStart;
1306
- let firstPaintTime: any;
1307
- let firstContentfulPaintTime: any;
1308
- // 是否已经形成数据(页面加载完成之后)
1309
- if (
1310
- window.performance &&
1311
- window.performance.timing &&
1312
- window.performance.timing.loadEventEnd > 0
1313
- ) {
1314
- // console.log('created')
1315
- getTiming();
1316
- } else {
1317
- // console.log('creating')
1318
- window.addEventListener('load', function() {
1319
- //不能影响最后的时间计算
1320
- window.setTimeout(function() {
1321
- getTiming();
1322
- }, 0);
1323
- });
1324
- }
1325
- // performance
1326
- function getTiming() {
1327
- // 获取首次渲染时间
1328
- try {
1329
- if (window.performance && Boolean(window.performance.getEntriesByType)) {
1330
- const performance = window.performance;
1331
- const performanceEntries = performance.getEntriesByType('paint');
1332
- performanceEntries.forEach((performanceEntry, i, entries) => {
1333
- const startTime = Math.round(performanceEntry.startTime);
1334
- if (performanceEntry.name === 'first-paint')
1335
- firstPaintTime = startTime;
1336
- else if (performanceEntry.name === 'first-contentful-paint')
1337
- firstContentfulPaintTime = startTime;
1338
- });
1339
- } else {
1340
- console.error('paint');
1341
- }
1342
- } catch (e) {
1343
- console.error((e as any).message);
1344
- }
1345
- // 获取加载时间
1346
- if (
1347
- window.performance &&
1348
- typeof window.performance.getEntries === 'function'
1349
- ) {
1350
- const performanceNavigationTiming: any =
1351
- (window.performance.getEntries() || [])[0] || {};
1352
- const data: any = {
1353
- // url: encodeURI(location.href),
1354
- // ua: navigator.userAgent,
1355
- os: getOS(),
1356
- osVersion: getOSVersion(),
1357
- deviceType: getDeviceType(),
1358
- network: getNetWork(),
1359
- screenDirection: getOrientationStatu(),
1360
- unloadTime: timing.unloadEventEnd - timing.unloadEventStart, //上个文档的卸载时间
1361
- redirectTime: timing.redirectEnd - timing.redirectStart, //*重定向时间
1362
- dnsTime: timing.domainLookupEnd - timing.domainLookupStart, //*DNS查询时间
1363
- tcpTime: timing.connectEnd - timing.connectStart, //*服务器连接时间
1364
- sslTime: getSSLTime(timing.connectEnd, timing.secureConnectionStart), //*ssl连接时间
1365
- responseTime: timing.responseStart - timing.requestStart, //*服务器响应时间
1366
- downloadTime: timing.responseEnd - timing.responseStart, //*网页下载时间
1367
- firstPaintTime: firstPaintTime, //*首次渲染时间
1368
- firstContentfulPaintTime: firstContentfulPaintTime, //*首次渲染内容时间
1369
- domreadyTime: timing.domContentLoadedEventStart - startTime || 0, //*dom ready时间(总和)
1370
- onloadTime: timing.loadEventStart - startTime || 0, //*onload时间(总和)
1371
- whiteTime: timing.responseStart - startTime, //*白屏时间
1372
- renderTime: timing.loadEventEnd - startTime || 0, //整个过程的时间之和
1373
- decodedBodySize: performanceNavigationTiming.decodedBodySize || '', //页面压缩前大小
1374
- encodedBodySize: performanceNavigationTiming.encodedBodySize || '' //页面压缩后大小
1375
- };
1376
- // 过滤异常数据
1377
- Object.keys(data).forEach(k => {
1378
- // 过滤掉 <0 的数据
1379
- if (isNumber(data[k]) && data[k] < 0) {
1380
- data[k] = 0;
1381
- }
1382
- });
1383
- // 过滤掉白屏时间 > onload 的数据
1384
- if (isNumber(data.whiteTime) && data.whiteTime > data.onloadTime) {
1385
- data.whiteTime = 0;
1386
- }
1387
- if (startTime > 0) {
1388
- let Underscore: any;
1389
- if (!camelCase) {
1390
- Object.keys(data).forEach(k => {
1391
- if (!Underscore) Underscore = {};
1392
- // console.log('camelCase2Underscore', k, data, )
1393
- Underscore[camelCase2Underscore(k)] = data[k];
1394
- });
1395
- }
1396
- success(Underscore || data);
1397
- } else {
1398
- fail(Error('startTime'));
1399
- }
1400
- } else {
1401
- fail(Error('getEntries'));
1402
- }
1403
- }
1404
- //获取当前操作系统
1405
- function getOS() {
1406
- let os;
1407
- if (
1408
- navigator.userAgent.indexOf('Android') > -1 ||
1409
- navigator.userAgent.indexOf('Linux') > -1
1410
- ) {
1411
- os = 'android';
1412
- } else if (navigator.userAgent.indexOf('iPhone') > -1) {
1413
- os = 'ios';
1414
- } else if (navigator.userAgent.indexOf('Windows Phone') > -1) {
1415
- os = 'wp';
1416
- } else {
1417
- os = 'others';
1418
- }
1419
- return os;
1420
- }
1421
- // 获取操作系统版本
1422
- function getOSVersion() {
1423
- let OSVision: any;
1424
- const u = navigator.userAgent;
1425
- const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //Android
1426
- const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
1427
- if (isAndroid) {
1428
- OSVision = (navigator.userAgent.split(';')[1].match(/\d+\.\d+/g) ||
1429
- [])[0];
1430
- }
1431
- if (isIOS) {
1432
- OSVision = (navigator.userAgent
1433
- .split(';')[1]
1434
- .match(/(\d+)_(\d+)_?(\d+)?/) || [])[0];
1435
- }
1436
- return OSVision;
1437
- }
1438
- //获取设备类型
1439
- function getDeviceType() {
1440
- let deviceType;
1441
- const sUserAgent = navigator.userAgent.toLowerCase();
1442
- const bIsIpad = sUserAgent.match(/(ipad)/i) && 'ipad';
1443
- const bIsIphoneOs = sUserAgent.match(/iphone os/i) && 'iphone os';
1444
- const bIsMidp = sUserAgent.match(/midp/i) && 'midp';
1445
- const bIsUc7 = sUserAgent.match(/rv:1.2.3.4/i) && 'rv:1.2.3.4';
1446
- const bIsUc = sUserAgent.match(/ucweb/i) && 'ucweb';
1447
- const bIsAndroid = sUserAgent.match(/android/i) && 'android';
1448
- const bIsCE = sUserAgent.match(/windows ce/i) && 'windows ce';
1449
- const bIsWM = sUserAgent.match(/windows mobile/i) && 'windows mobile';
1450
- if (
1451
- !(
1452
- bIsIpad ||
1453
- bIsIphoneOs ||
1454
- bIsMidp ||
1455
- bIsUc7 ||
1456
- bIsUc ||
1457
- bIsAndroid ||
1458
- bIsCE ||
1459
- bIsWM
1460
- )
1461
- ) {
1462
- deviceType = 'pc'; //pc
1463
- } else if (
1464
- bIsIphoneOs ||
1465
- bIsMidp ||
1466
- bIsUc7 ||
1467
- bIsUc ||
1468
- bIsAndroid ||
1469
- bIsCE ||
1470
- bIsWM
1471
- ) {
1472
- deviceType = 'phone'; //phone
1473
- } else if (bIsIpad) {
1474
- deviceType = 'ipad'; //ipad
1475
- } else {
1476
- deviceType = undefined;
1477
- }
1478
- return deviceType;
1479
- }
1480
- // 获取网络状态
1481
- function getNetWork() {
1482
- let netWork: any;
1483
- if (
1484
- (navigator as any).connection &&
1485
- (navigator as any).connection.effectiveType
1486
- ) {
1487
- switch ((navigator as any).connection.effectiveType) {
1488
- case 'wifi':
1489
- netWork = 'wifi'; // wifi
1490
- break;
1491
- case '4g':
1492
- netWork = '4g'; // 4g
1493
- break;
1494
- case '2g':
1495
- netWork = '2g'; // 2g
1496
- break;
1497
- case '3g':
1498
- netWork = '3g'; // 3g
1499
- break;
1500
- case 'ethernet':
1501
- netWork = 'ethernet'; // ethernet
1502
- break;
1503
- case 'default':
1504
- netWork = undefined; // 未知
1505
- break;
1506
- }
1507
- }
1508
- return netWork;
1509
- }
1510
- // 获取横竖屏状态
1511
- function getOrientationStatu() {
1512
- let orientationStatu: any;
1513
- if (
1514
- window.screen &&
1515
- window.screen.orientation &&
1516
- window.screen.orientation.angle
1517
- ) {
1518
- if (
1519
- window.screen.orientation.angle === 180 ||
1520
- window.screen.orientation.angle === 0
1521
- ) {
1522
- // 竖屏
1523
- orientationStatu = '|';
1524
- }
1525
- if (
1526
- window.screen.orientation.angle === 90 ||
1527
- window.screen.orientation.angle === -90
1528
- ) {
1529
- // 横屏
1530
- orientationStatu = '-';
1531
- }
1532
- }
1533
- return orientationStatu;
1534
- }
1535
- // 获取ssl连接时间
1536
- function getSSLTime(connectEnd: any, secureConnectionStart: any) {
1537
- let ssl_time: any;
1538
- if (secureConnectionStart) {
1539
- ssl_time = connectEnd - secureConnectionStart;
1540
- }
1541
- return ssl_time;
1542
- }
1543
- return status;
1544
- }
1545
-
1546
- /**
1547
- * EN: Hit probability (1% ~ 100%).
1548
- *
1549
- * ZH: 百分位概率
1550
- *
1551
- * Usage:
1552
- *
1553
- * ```
1554
- * inRate(0.5); // 0.01 ~ 1 true/false
1555
- * ```
1556
- *
1557
- * Output:
1558
- *
1559
- * ```
1560
- * true
1561
- * ```
1562
- *
1563
- * Example: Test the precision.
1564
- *
1565
- * ```
1566
- * // Test
1567
- * let trueCount = 0;
1568
- * let falseCount = 0;
1569
- * new Array(1000000).fill(0).forEach(() => {
1570
- * if (inRate(0.5)) {
1571
- * trueCount++;
1572
- * } else {
1573
- * falseCount++;
1574
- * }
1575
- * });
1576
- * console.log(trueCount, falseCount); // 499994 500006
1577
- * ```
1578
- *
1579
- * @param {number} rate -- 0.1 ~ 1 => 1% ~ 100%
1580
- * @returns {boolean} true 命中
1581
- * @category Calculate and Formula
1582
- */
1583
- export function inRate(rate: number): boolean {
1584
- if (Math.random() < rate) {
1585
- return true;
1586
- }
1587
- return false;
1588
- }
1589
-
1590
- /**
1591
- * EN: Detect the margin of Safety. Determine if it is a secure PWA environment that it can run.
1592
- *
1593
- * ZH: 判断是否是安全的 PWA 环境
1594
- *
1595
- * Usage:
1596
- *
1597
- * ```
1598
- * isSafePWAEnv();
1599
- * ```
1600
- *
1601
- * Output:
1602
- *
1603
- * ```
1604
- * true
1605
- * ```
1606
- *
1607
- * @returns {boolean} true 是
1608
- * @category Browser Information
1609
- */
1610
- export function isSafePWAEnv(): boolean {
1611
- // 判断是否支持 async await
1612
- function isSupportAsyncAwait() {
1613
- let isSupportAsyncAwaitFunc;
1614
- try {
1615
- // eval("func = async function(){};");
1616
- const fn = new Function('return async function(){};');
1617
- isSupportAsyncAwaitFunc = fn();
1618
- // console.log('isSupportAsyncAwaitFunc', isSupportAsyncAwaitFunc);
1619
- // 由于async函数的构造器不是全局对象,所以我们需要由下面代码来获取async函数的构造器
1620
- // 具体可以查看以下MDN上有关于AsyncFunction的说明,
1621
- // 地址:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction
1622
- return Object.getPrototypeOf(isSupportAsyncAwaitFunc).constructor != null;
1623
- } catch (e) {
1624
- return false;
1625
- }
1626
- }
1627
- // 判断是否支持 Promise
1628
- function isSupportPromise() {
1629
- if (
1630
- typeof Promise !== 'undefined' &&
1631
- Promise.toString().indexOf('[native code]') !== -1
1632
- ) {
1633
- return true;
1634
- }
1635
- return false;
1636
- }
1637
- // 浏览器信息
1638
- const BrowserType = getBrowserInfo();
1639
- if (
1640
- 'serviceWorker' in navigator &&
1641
- isSupportAsyncAwait() &&
1642
- isSupportPromise() &&
1643
- Boolean(window.fetch) &&
1644
- Boolean(window.indexedDB) &&
1645
- Boolean(window.caches) &&
1646
- !BrowserType['shell']
1647
- ) {
1648
- return true;
1649
- }
1650
- return false;
1651
- }
1652
-
1653
- /**
1654
- * EN: Browser Information
1655
- *
1656
- * ZH: 返回浏览器信息 https://github.com/JowayYoung/juejin-code/blob/master/browser-type.js
1657
- *
1658
- * Usage:
1659
- *
1660
- * ```
1661
- * getBrowserInfo();
1662
- * ```
1663
- *
1664
- * Output:
1665
- *
1666
- * ```
1667
- * {"engine":"webkit","engineVs":"537.36","platform":"desktop","supporter":"chrome","supporterVs":"85.0.4183.121","system":"windows","systemVs":"10"}
1668
- * ```
1669
- *
1670
- * Results:
1671
- *
1672
- * | Attribute | Description | Type | Values |
1673
- * | --- | --- | --- | --- |
1674
- * | **system** | System | string | android, ios, windows, macos, linux |
1675
- * | systemVs | System version | string | windows: 2000, xp, 2003, vista, 7, 8, 8.1, 10 <br />macos: ... |
1676
- * | platform | Platform | string | desktop, mobile |
1677
- * | engine | Engine | string | webkit, gecko, presto, trident |
1678
- * | engineVs | Engine version | string | - |
1679
- * | supporter | Supporter | string | edge, opera, chrome, safari, firefox, iexplore |
1680
- * | supporterVs | Supporter version | string | - |
1681
- * | shell | Shell | string | (Optional) wechat, qq_browser, qq_app, uc, 360, 2345, sougou, liebao, maxthon, bilibili |
1682
- * | shellVs | Shell version | string | (Optional) 20/... |
1683
- * | appleType | Apple device type | string | (Optional) ipad, iphone, ipod, iwatch |
1684
- *
1685
- * Example: Determine the environment of the mobile QQ.
1686
- *
1687
- * ```
1688
- * const { system, shell } = getBrowserInfo();
1689
- * const isMobileQQ = ['android', 'ios'].includes(system) && ['qq_browser', 'qq_app'].includes(shell);
1690
- * ```
1691
- *
1692
- * @returns 浏览器信息
1693
- * @category Browser Information
1694
- */
1695
- export function getBrowserInfo(): {
1696
- engine: string; // webkit gecko presto trident
1697
- engineVs: string;
1698
- platform: string; // desktop mobile
1699
- supporter: string; // chrome safari firefox opera iexplore edge
1700
- supporterVs: string;
1701
- system: string; // windows macos linux android ios
1702
- systemVs: string;
1703
- shell?: string; // wechat qq uc 360 2345 sougou liebao maxthon
1704
- shellVs?: string;
1705
- appleType?: string;
1706
- } {
1707
- try {
1708
- // 权重:系统 + 系统版本 > 平台 > 内核 + 载体 + 内核版本 + 载体版本 > 外壳 + 外壳版本
1709
- const ua: string = navigator.userAgent.toLowerCase();
1710
- const testUa: (regexp: RegExp) => boolean = regexp => regexp.test(ua);
1711
- const testVs: (regexp: RegExp) => string = regexp => {
1712
- const matchRes = ua.match(regexp);
1713
- let ret = '';
1714
- if (matchRes) {
1715
- ret = matchRes
1716
- .toString()
1717
- .replace(/[^0-9|_.]/g, '')
1718
- .replace(/_/g, '.');
1719
- }
1720
- return ret;
1721
- };
1722
- // 系统
1723
- let system = '';
1724
- // Apple device type.
1725
- let appleType = '';
1726
- if (testUa(/windows|win32|win64|wow32|wow64/g)) {
1727
- system = 'windows'; // windows系统
1728
- } else if (testUa(/macintosh|macintel/g)) {
1729
- system = 'macos'; // macos系统
1730
- } else if (testUa(/x11/g)) {
1731
- system = 'linux'; // linux系统
1732
- } else if (testUa(/android|adr/g)) {
1733
- system = 'android'; // android系统
1734
- } else if (testUa(/ios|iphone|ipad|ipod|iwatch/g)) {
1735
- system = 'ios'; // ios系统
1736
- if (testUa(/ipad/g)) {
1737
- appleType = 'ipad';
1738
- } else if (testUa(/iphone/g)) {
1739
- appleType = 'iphone';
1740
- } else if (testUa(/iwatch/g)) {
1741
- appleType = 'iwatch';
1742
- } else if (testUa(/ipod/g)) {
1743
- appleType = 'ipod';
1744
- }
1745
- }
1746
- // 系统版本
1747
- let systemVs = '';
1748
- if (system === 'windows') {
1749
- if (testUa(/windows nt 5.0|windows 2000/g)) {
1750
- systemVs = '2000';
1751
- } else if (testUa(/windows nt 5.1|windows xp/g)) {
1752
- systemVs = 'xp';
1753
- } else if (testUa(/windows nt 5.2|windows 2003/g)) {
1754
- systemVs = '2003';
1755
- } else if (testUa(/windows nt 6.0|windows vista/g)) {
1756
- systemVs = 'vista';
1757
- } else if (testUa(/windows nt 6.1|windows 7/g)) {
1758
- systemVs = '7';
1759
- } else if (testUa(/windows nt 6.2|windows 8/g)) {
1760
- systemVs = '8';
1761
- } else if (testUa(/windows nt 6.3|windows 8.1/g)) {
1762
- systemVs = '8.1';
1763
- } else if (testUa(/windows nt 10.0|windows 10/g)) {
1764
- systemVs = '10';
1765
- }
1766
- } else if (system === 'macos') {
1767
- systemVs = testVs(/os x [\d._]+/g);
1768
- } else if (system === 'android') {
1769
- systemVs = testVs(/android [\d._]+/g);
1770
- } else if (system === 'ios') {
1771
- systemVs = testVs(/os [\d._]+/g);
1772
- }
1773
- // 平台
1774
- let platform = '';
1775
- if (system === 'windows' || system === 'macos' || system === 'linux') {
1776
- platform = 'desktop'; // 桌面端
1777
- } else if (system === 'android' || system === 'ios' || testUa(/mobile/g)) {
1778
- platform = 'mobile'; // 移动端
1779
- }
1780
- // 内核和载体
1781
- let engine = '';
1782
- let supporter = '';
1783
- if (testUa(/applewebkit/g)) {
1784
- engine = 'webkit'; // webkit内核
1785
- if (testUa(/edge/g)) {
1786
- supporter = 'edge'; // edge浏览器
1787
- } else if (testUa(/opr/g)) {
1788
- supporter = 'opera'; // opera浏览器
1789
- } else if (testUa(/chrome/g)) {
1790
- supporter = 'chrome'; // chrome浏览器
1791
- } else if (testUa(/safari/g)) {
1792
- supporter = 'safari'; // safari浏览器
1793
- }
1794
- } else if (testUa(/gecko/g) && testUa(/firefox/g)) {
1795
- engine = 'gecko'; // gecko内核
1796
- supporter = 'firefox'; // firefox浏览器
1797
- } else if (testUa(/presto/g)) {
1798
- engine = 'presto'; // presto内核
1799
- supporter = 'opera'; // opera浏览器
1800
- } else if (testUa(/trident|compatible|msie/g)) {
1801
- engine = 'trident'; // trident内核
1802
- supporter = 'iexplore'; // iexplore浏览器
1803
- }
1804
- // 内核版本
1805
- let engineVs = '';
1806
- if (engine === 'webkit') {
1807
- engineVs = testVs(/applewebkit\/[\d._]+/g);
1808
- } else if (engine === 'gecko') {
1809
- engineVs = testVs(/gecko\/[\d._]+/g);
1810
- } else if (engine === 'presto') {
1811
- engineVs = testVs(/presto\/[\d._]+/g);
1812
- } else if (engine === 'trident') {
1813
- engineVs = testVs(/trident\/[\d._]+/g);
1814
- }
1815
- // 载体版本
1816
- let supporterVs = '';
1817
- if (supporter === 'chrome') {
1818
- supporterVs = testVs(/chrome\/[\d._]+/g);
1819
- } else if (supporter === 'safari') {
1820
- supporterVs = testVs(/version\/[\d._]+/g);
1821
- } else if (supporter === 'firefox') {
1822
- supporterVs = testVs(/firefox\/[\d._]+/g);
1823
- } else if (supporter === 'opera') {
1824
- supporterVs = testVs(/opr\/[\d._]+/g);
1825
- } else if (supporter === 'iexplore') {
1826
- supporterVs = testVs(/(msie [\d._]+)|(rv:[\d._]+)/g);
1827
- } else if (supporter === 'edge') {
1828
- supporterVs = testVs(/edge\/[\d._]+/g);
1829
- }
1830
- // 外壳和外壳版本
1831
- let shell = '';
1832
- let shellVs = '';
1833
- if (testUa(/micromessenger/g)) {
1834
- shell = 'wechat'; // 微信浏览器
1835
- shellVs = testVs(/micromessenger\/[\d._]+/g);
1836
- } else if (testUa(/qqbrowser/g)) {
1837
- shell = 'qq_browser'; // QQ Browser
1838
- shellVs = testVs(/qqbrowser\/[\d._]+/g);
1839
- } else if (testUa(/\sqq/g)) {
1840
- shell = 'qq_app'; // QQ APP
1841
- } else if (testUa(/ucbrowser/g)) {
1842
- shell = 'uc'; // UC浏览器
1843
- shellVs = testVs(/ucbrowser\/[\d._]+/g);
1844
- } else if (testUa(/qihu 360se/g)) {
1845
- shell = '360'; // 360浏览器(无版本)
1846
- } else if (testUa(/2345explorer/g)) {
1847
- shell = '2345'; // 2345浏览器
1848
- shellVs = testVs(/2345explorer\/[\d._]+/g);
1849
- } else if (testUa(/metasr/g)) {
1850
- shell = 'sougou'; // 搜狗浏览器(无版本)
1851
- } else if (testUa(/lbbrowser/g)) {
1852
- shell = 'liebao'; // 猎豹浏览器(无版本)
1853
- } else if (testUa(/maxthon/g)) {
1854
- shell = 'maxthon'; // 遨游浏览器
1855
- shellVs = testVs(/maxthon\/[\d._]+/g);
1856
- } else if (testUa(/biliapp/g)) {
1857
- shell = 'bilibili'; // 哔哩哔哩
1858
- }
1859
- return Object.assign(
1860
- {
1861
- engine, // webkit gecko presto trident
1862
- engineVs,
1863
- platform, // desktop mobile
1864
- supporter, // chrome safari firefox opera iexplore edge
1865
- supporterVs,
1866
- system, // windows macos linux android ios
1867
- systemVs
1868
- },
1869
- {
1870
- shell, // wechat qq uc 360 2345 sougou liebao maxthon
1871
- shellVs,
1872
- appleType
1873
- }
1874
- );
1875
- } catch (err) {
1876
- console.warn('mazey:', err);
1877
- return {
1878
- engine: '', // webkit gecko presto trident
1879
- engineVs: '',
1880
- platform: '', // desktop mobile
1881
- supporter: '', // chrome safari firefox opera iexplore edge
1882
- supporterVs: '',
1883
- system: '', // windows macos linux android ios
1884
- systemVs: ''
1885
- };
1886
- }
1887
- }
1888
-
1889
- /**
1890
- * @method clearHtml
1891
- * @description 去除HTML标签
1892
- * @param {string} str 带html标签的字符串
1893
- * @returns {string} 字符串
1894
- */
1895
- export function clearHtml(str: string): string {
1896
- return str.replace(/<\/?.+?>/g, '').replace(/[\r\n]/g, '');
1897
- }
1898
-
1899
- /**
1900
- * @method cutCHSString
1901
- * @description 截取字符串,中文算2个字节
1902
- * @param {string} str 要截取的字符串
1903
- * @param {number} len
1904
- * @param {boolean} hasDot
1905
- * @returnss {string} 返回截取后的字符串
1906
- */
1907
- export function cutCHSString(str: string, len: number, hasDot = false): string {
1908
- if (str == '' || !str) {
1909
- return '';
1910
- } else {
1911
- let newLength = 0;
1912
- let newStr = '';
1913
- // eslint-disable-next-line no-control-regex
1914
- const chineseRegex = /[^\x00-\xff]/g;
1915
- let singleChar = '';
1916
- const strLength = str.replace(chineseRegex, '**').length;
1917
- for (let i = 0; i < strLength; i++) {
1918
- singleChar = str.charAt(i).toString();
1919
- if (singleChar.match(chineseRegex) != null) {
1920
- newLength += 2;
1921
- } else {
1922
- newLength++;
1923
- }
1924
- if (newLength > len) {
1925
- break;
1926
- }
1927
- newStr += singleChar;
1928
- }
1929
-
1930
- if (hasDot && strLength > len) {
1931
- newStr += '...';
1932
- }
1933
- return newStr;
1934
- }
1935
- }
1936
-
1937
- /**
1938
- * EN: Check whether the page is loaded successfully (Keepe the compatibility in case that browser's `load` event has been triggered).
1939
- *
1940
- * ZH: 页面加载完成
1941
- *
1942
- * ```
1943
- * windowLoaded(30) // second
1944
- * .then(res => {
1945
- * console.log(`Load Success: ${res}`); // Load Success: load
1946
- * })
1947
- * .catch(err => {
1948
- * console.log(`Load Timeout or Fail: ${err.message}`);
1949
- * });
1950
- * ```
1951
- *
1952
- * @param {number} timeout 超时时间 / 单位:秒
1953
- * @returns {Promise<string>} document is loaded? 'complete' 'load' / 'timeout'
1954
- * @category Load Resource
1955
- */
1956
- export function windowLoaded(timeout = 90): Promise<string | Error> {
1957
- let loaded: (value: string) => void = () => undefined;
1958
- let loadFail: (value: Error) => void;
1959
- const status = new Promise(
1960
- (resolve: (value: string) => void, reject: (value: Error) => void) => {
1961
- loaded = resolve;
1962
- loadFail = reject;
1963
- }
1964
- );
1965
- if (document.readyState === 'complete') {
1966
- loaded('complete');
1967
- } else {
1968
- window.addEventListener('load', () => loaded('load'));
1969
- }
1970
- // 超过 timeout 秒后加载失败
1971
- setTimeout(() => loadFail(Error('timeout')), timeout * 1000);
1972
- return status;
1973
- }
1974
-
1975
- /**
1976
- * EN: Add `<style>` in `<head>`.
1977
- *
1978
- * ZH: 添加样式标签; style: 样式标签内的字符串; id: `<style>` 标签的 `id`; 返回: 添加成功/失败
1979
- *
1980
- * Example 1: Add the `<style>` with `id`, and repeated invoking will update the content instead of adding a new one.
1981
- *
1982
- * ```
1983
- * addStyle(
1984
- * `
1985
- * body {
1986
- * background-color: #333;
1987
- * }
1988
- * `,
1989
- * {
1990
- * id: 'test',
1991
- * }
1992
- * );
1993
- * // <style id="test">
1994
- * // body {
1995
- * // background-color: #333;
1996
- * // }
1997
- * // </style>
1998
- * ```
1999
- *
2000
- * Example 2: Add the `<style>` without `id`, and repeated invoking will adding a new one.
2001
- *
2002
- * ```
2003
- * addStyle(
2004
- * `
2005
- * body {
2006
- * background-color: #444;
2007
- * }
2008
- * `
2009
- * );
2010
- * // <style>
2011
- * // body {
2012
- * // background-color: #444;
2013
- * // }
2014
- * // </style>
2015
- * ```
2016
- *
2017
- * @category DOM
2018
- */
2019
- export function addStyle(
2020
- style: string,
2021
- options: { id?: string } = { id: '' }
2022
- ): boolean {
2023
- // console.log('_ style', style);
2024
- // console.log('_ options', options);
2025
- if (!style) {
2026
- return false;
2027
- }
2028
- // 创建 style 文档碎片
2029
- const styleFrag = document.createDocumentFragment();
2030
- let idDom: HTMLElement | null = null;
2031
- let domId = '';
2032
- // Custom Style
2033
- const customStyle = document.createElement('style');
2034
- // 如果需要 ID
2035
- if (options.id) {
2036
- domId = `${options.id}`;
2037
- idDom = document.getElementById(domId);
2038
- // 如果 Dom 不存在,插入 style
2039
- if (!idDom) {
2040
- customStyle.setAttribute('id', options.id);
2041
- customStyle.innerHTML = style;
2042
- styleFrag.appendChild(customStyle);
2043
- document.head.appendChild(styleFrag);
2044
- } else {
2045
- // 如果 Dom 存在,直接更新
2046
- idDom.innerHTML = style;
2047
- }
2048
- } else {
2049
- // 不需要 ID,直接添加新标签
2050
- customStyle.innerHTML = style;
2051
- styleFrag.appendChild(customStyle);
2052
- document.head.appendChild(styleFrag);
2053
- }
2054
- return true;
2055
- }
2056
-
2057
- /**
2058
- * EN: Custom console printing (`console`).
2059
- *
2060
- * ZH: 生成自定义控制台打印
2061
- *
2062
- * Usage:
2063
- *
2064
- * ```
2065
- * const myConsole = genCustomConsole('MazeyLog:');
2066
- * myConsole.log('I am string.');
2067
- * myConsole.info('I am boolean.', true);
2068
- * myConsole.info('I am number.', 123, 456);
2069
- * myConsole.info('I am object.', { a: 123, b: 456});
2070
- * ```
2071
- *
2072
- * Output:
2073
- *
2074
- * ```
2075
- * MazeyLog: I am string.
2076
- * MazeyLog: I am boolean. true
2077
- * MazeyLog: I am number. 123 456
2078
- * MazeyLog: I am object. {a: 123, b: 456}
2079
- * ```
2080
- *
2081
- * @param {string} prefix 前缀
2082
- * @param {string} locales A locale string.
2083
- * @param {function} logFn The function with Log.
2084
- * @param {function} errorFn The function with Error.
2085
- * @returns {object} 新实例
2086
- * @category Debug
2087
- */
2088
- export function genCustomConsole(
2089
- prefix = '',
2090
- options: {
2091
- isClosed?: boolean;
2092
- showWrap?: boolean;
2093
- showDate?: boolean;
2094
- locales?: string;
2095
- logFn?: () => void;
2096
- errorFn?: () => void;
2097
- } = {
2098
- isClosed: false,
2099
- showWrap: false,
2100
- showDate: false,
2101
- locales: 'en-US',
2102
- logFn: () => undefined,
2103
- errorFn: () => undefined
2104
- }
2105
- ): Console {
2106
- const {
2107
- isClosed,
2108
- showWrap,
2109
- showDate,
2110
- locales,
2111
- logFn,
2112
- errorFn
2113
- } = Object.assign(
2114
- {
2115
- isClosed: false,
2116
- showWrap: false,
2117
- showDate: false,
2118
- locales: 'en-US',
2119
- logFn: () => undefined,
2120
- errorFn: () => undefined
2121
- },
2122
- options
2123
- );
2124
- const methods = ['log', 'info', 'warn', 'error'];
2125
- const newConsole = Object.create(null);
2126
- // https://stackoverflow.com/questions/3552461/how-do-i-format-a-date-in-javascript
2127
- const formatDate = () => {
2128
- const dateOptions: Intl.DateTimeFormatOptions = {
2129
- weekday: 'short',
2130
- year: 'numeric',
2131
- month: 'short',
2132
- day: 'numeric',
2133
- hour: 'numeric',
2134
- // hourCycle: 'h24',
2135
- minute: 'numeric',
2136
- second: 'numeric'
2137
- };
2138
- const todayDateIns = new Date();
2139
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString
2140
- // https://datatracker.ietf.org/doc/html/rfc4647
2141
- const dateStr = todayDateIns.toLocaleDateString(locales, dateOptions);
2142
- return dateStr;
2143
- };
2144
- // if (showDate) {
2145
- // prefix = `${dateStr} ${prefix}`;
2146
- // }
2147
- methods.forEach(method => {
2148
- newConsole[method] = function(...argu: any) {
2149
- if (isClosed) {
2150
- return false;
2151
- }
2152
- let elaboratePrefix = prefix;
2153
- let datePrefix = prefix;
2154
- if (typeof prefix === 'string' && prefix.length >= 2) {
2155
- const len = prefix.length;
2156
- if (prefix[len - 1] === ':') {
2157
- elaboratePrefix = prefix.substring(0, len - 1);
2158
- } else {
2159
- elaboratePrefix = prefix;
2160
- }
2161
- }
2162
- if (showWrap) {
2163
- console.log(`--- ${elaboratePrefix} - begin ---`);
2164
- }
2165
- if (showDate) {
2166
- if (prefix) {
2167
- datePrefix = `${formatDate()} ${prefix}`;
2168
- } else {
2169
- datePrefix = `${formatDate()}`;
2170
- }
2171
- }
2172
- if (prefix || showDate) {
2173
- (console as any)[method](datePrefix, ...argu);
2174
- } else {
2175
- (console as any)[method](...argu);
2176
- }
2177
- if (method === 'log') {
2178
- logFn();
2179
- }
2180
- if (method === 'error') {
2181
- errorFn();
2182
- }
2183
- if (showWrap) {
2184
- console.log(`--- ${elaboratePrefix} - end ---`);
2185
- }
2186
- };
2187
- });
2188
- return newConsole;
2189
- }
2190
-
2191
- /**
2192
- * Verify the validity of axios response.
2193
- *
2194
- * Reference: [Handling Errors](https://axios-http.com/docs/handling_errors)
2195
- */
2196
- export function zAxiosIsValidRes(
2197
- res: any,
2198
- options: {
2199
- validStatusRange?: number[];
2200
- validCode?: number[];
2201
- } = {
2202
- validStatusRange: [200, 300],
2203
- validCode: [0]
2204
- }
2205
- ): boolean {
2206
- const { validStatusRange, validCode } = Object.assign(
2207
- {
2208
- validStatusRange: [200, 300],
2209
- validCode: [0]
2210
- },
2211
- options
2212
- );
2213
- if (validStatusRange.length !== 2) {
2214
- console.error('valid validStatusRange is required');
2215
- }
2216
- let ret = false;
2217
- if (
2218
- res &&
2219
- res.status &&
2220
- validStatusRange.length === 2 &&
2221
- res.status >= validStatusRange[0] &&
2222
- res.status < validStatusRange[1]
2223
- ) {
2224
- const resData = res.data;
2225
- if (resData && validCode.includes(resData.code)) {
2226
- ret = true;
2227
- }
2228
- }
2229
- return ret;
2230
- }
2231
-
2232
- /**
2233
- * Verify the validity of a non-empty array.
2234
- */
2235
- export function isNonEmptyArray(arr: any[]): boolean {
2236
- let ret = false;
2237
- if (Array.isArray(arr) && arr.length) {
2238
- ret = true;
2239
- }
2240
- return ret;
2241
- }
2242
-
2243
- /**
2244
- * Determine the validity of the data.
2245
- *
2246
- * Usage:
2247
- *
2248
- * ```
2249
- * const validData = {
2250
- * a: {
2251
- * b: {
2252
- * c: 413
2253
- * }
2254
- * }
2255
- * };
2256
- *
2257
- * const isValidDataResA = isValidData(validData, ['a', 'b', 'c'], 2333);
2258
- * const isValidDataResB = isValidData(validData, ['a', 'b', 'c'], 413);
2259
- * const isValidDataResC = isValidData(validData, ['d', 'd'], 413);
2260
- *
2261
- * console.log('isValidDataResA:', isValidDataResA);
2262
- * console.log('isValidDataResB:', isValidDataResB);
2263
- * console.log('isValidDataResC:', isValidDataResC);
2264
- * ```
2265
- *
2266
- * Output:
2267
- *
2268
- * ```
2269
- * isValidDataResA: false
2270
- * isValidDataResB: true
2271
- * isValidDataResC: false
2272
- * ```
2273
- *
2274
- * @param {any} data Original Data
2275
- * @param {string[]} attributes Data Attributes
2276
- * @param {any} validValue Given Value for verifying.
2277
- * @returns {boolean} Return TRUE if the data is valid.
2278
- * @category Util
2279
- */
2280
- export function isValidData(
2281
- data: any,
2282
- attributes: string[],
2283
- validValue: any
2284
- ): boolean {
2285
- let ret = false;
2286
- const foundRet = attributes.reduce((foundValue, curr) => {
2287
- if (foundValue[curr]) {
2288
- foundValue = foundValue[curr];
2289
- } else {
2290
- return Object.create(null);
2291
- }
2292
- // console.log('foundValue', foundValue);
2293
- return foundValue;
2294
- }, data);
2295
- // console.log('foundRet', foundRet);
2296
- if (foundRet === validValue) {
2297
- ret = true;
2298
- }
2299
- return ret;
2300
- }
2301
-
2302
- /**
2303
- * 语义化文件大小, 把字节转换成正常文件大小.
2304
- */
2305
- export function getFileSize(size: number): string {
2306
- const toCeilStr: (v: number) => string = n => String(Math.ceil(n));
2307
- if (!size) return '';
2308
- const num = 1024.0; // byte
2309
- if (size < num) {
2310
- return size + ' B';
2311
- }
2312
- if (size < Math.pow(num, 2)) {
2313
- return toCeilStr(size / num) + ' KB';
2314
- } // kb
2315
- if (size < Math.pow(num, 3)) {
2316
- return toCeilStr(size / Math.pow(num, 2)) + ' MB';
2317
- } // M
2318
- if (size < Math.pow(num, 4)) {
2319
- return toCeilStr(size / Math.pow(num, 3)) + ' G';
2320
- } // G
2321
- return toCeilStr(size / Math.pow(num, 4)) + ' T';
2322
- }
2323
-
2324
- /**
2325
- * Detect webp support.
2326
- *
2327
- * Reference: [Detect WEBP Support with JavaScript](https://davidwalsh.name/detect-webp)
2328
- */
2329
- export function isSupportWebp(): Promise<boolean> {
2330
- const fn = (resolve: (v: boolean) => void) => {
2331
- const img = new Image();
2332
- img.onload = () => {
2333
- resolve(img.width > 0 && img.height > 0);
2334
- };
2335
- img.onerror = () => {
2336
- resolve(false);
2337
- };
2338
- img.src =
2339
- 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=';
2340
- };
2341
- return new Promise(fn);
2342
- }
2343
-
2344
- /**
2345
- * Generate a Hash Code from a string.
2346
- *
2347
- * Reference: [Generate a Hash from string in Javascript](https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery)
2348
- */
2349
- export function genHashCode(str: string): number {
2350
- let hash = 0,
2351
- i,
2352
- chr;
2353
- for (i = 0; i < str.length; i++) {
2354
- chr = str.charCodeAt(i);
2355
- hash = (hash << 5) - hash + chr;
2356
- hash |= 0;
2357
- }
2358
- return hash;
2359
- }
2360
-
2361
- /**
2362
- * Return the formatted date string in the given format.
2363
- *
2364
- * Usage:
2365
- *
2366
- * ```
2367
- * console.log('Default formatDate value:', formatDate());
2368
- * console.log('String formatDate value:', formatDate('Tue Jan 11 2022 14:12:26 GMT+0800 (China Standard Time)', 'yyyy-MM-dd hh:mm:ss'));
2369
- * console.log('Number formatDate value:', formatDate(1641881235000, 'yyyy-MM-dd hh:mm:ss'));
2370
- * console.log('Date formatDate value:', formatDate(new Date(2014, 1, 11), 'MM/dd/yyyy'));
2371
- * ```
2372
- *
2373
- * Output:
2374
- *
2375
- * ```
2376
- * Default formatDate value: 2023-01-11
2377
- * String formatDate value: 2022-01-11 14:12:26
2378
- * Number formatDate value: 2022-01-11 14:07:15
2379
- * Date formatDate value: 02/11/2014
2380
- * ```
2381
- *
2382
- * @param {Date|number|string} dateIns Original Date
2383
- * @param {string} format Format String
2384
- * @returns {string} Return the formatted date string.
2385
- * @category Util
2386
- */
2387
- export function formatDate(
2388
- dateIns?: Date | number | string,
2389
- format = 'yyyy-MM-dd'
2390
- ): string {
2391
- if (!dateIns) {
2392
- dateIns = new Date();
2393
- }
2394
- const tempDate = new Date(dateIns);
2395
- const o: {
2396
- [key: string]: string | number;
2397
- } = {
2398
- yyyy: tempDate.getFullYear(),
2399
- MM: tempDate.getMonth() + 1,
2400
- dd: tempDate.getDate(),
2401
- hh:
2402
- tempDate.getHours() < 10
2403
- ? '0' + tempDate.getHours()
2404
- : tempDate.getHours(),
2405
- mm:
2406
- tempDate.getMinutes() < 10
2407
- ? '0' + tempDate.getMinutes()
2408
- : tempDate.getMinutes(),
2409
- ss:
2410
- tempDate.getSeconds() < 10
2411
- ? '0' + tempDate.getSeconds()
2412
- : tempDate.getSeconds()
2413
- };
2414
- let tempFormat = format || 'yyyy-MM-dd';
2415
- Object.keys(o).forEach(key => {
2416
- let value = o[key];
2417
- if (key === 'MM' && Number(value) <= 9) {
2418
- value = `0${value}`;
2419
- }
2420
- tempFormat = tempFormat.replace(key, String(value));
2421
- });
2422
- return tempFormat;
2423
- }
2424
-
2425
- /**
2426
- * Get event container.
2427
- *
2428
- * @category Event
2429
- * @hidden
2430
- */
2431
- export function getDefineListeners(): DefineListeners {
2432
- let defineListeners = window.MAZEY_DEFINE_LISTENERS;
2433
- if (typeof defineListeners !== 'object') {
2434
- defineListeners = {};
2435
- window.MAZEY_DEFINE_LISTENERS = defineListeners;
2436
- }
2437
- return defineListeners;
2438
- }
2439
-
2440
- /**
2441
- * Add event.
2442
- *
2443
- * @param type
2444
- * @param fn
2445
- * @category Event
2446
- */
2447
- export function addEvent(type: string, fn: any): void {
2448
- const defineListeners = getDefineListeners();
2449
- if (typeof defineListeners[type] === 'undefined') {
2450
- defineListeners[type] = [];
2451
- }
2452
- if (typeof fn === 'function') {
2453
- defineListeners[type].push(fn);
2454
- }
2455
- }
2456
-
2457
- /**
2458
- * Invoke event.
2459
- *
2460
- * @param type
2461
- * @category Event
2462
- */
2463
- export function invokeEvent(type: string): void {
2464
- const defineListeners = getDefineListeners();
2465
- const arrayEvent = defineListeners[type];
2466
- if (arrayEvent instanceof Array) {
2467
- for (let i = 0, length = arrayEvent.length; i < length; i++) {
2468
- if (typeof arrayEvent[i] === 'function') {
2469
- arrayEvent[i]({
2470
- type: type
2471
- });
2472
- }
2473
- }
2474
- }
2475
- }
2476
-
2477
- /**
2478
- * Remove event.
2479
- *
2480
- * @param type
2481
- * @param fn
2482
- * @category Event
2483
- */
2484
- export function removeEvent(type: string, fn: any): void {
2485
- const defineListeners = getDefineListeners();
2486
- const arrayEvent = defineListeners[type];
2487
- if (typeof type === 'string' && arrayEvent instanceof Array) {
2488
- if (typeof fn === 'function') {
2489
- for (let i = 0, length = arrayEvent.length; i < length; i++) {
2490
- if (arrayEvent[i] === fn) {
2491
- defineListeners[type].splice(i, 1);
2492
- break;
2493
- }
2494
- }
2495
- } else {
2496
- delete defineListeners[type];
2497
- }
2498
- }
2499
- }
2500
-
2501
- /**
2502
- * Check if the given string is a valid url.
2503
- *
2504
- * @example
2505
- * ```js
2506
- * console.log(isUrl('https://www.google.com')); // true
2507
- * console.log(isUrl('https://www.google.com/')); // true
2508
- * console.log(isUrl('https://www.google.com/?q=hello')); // true
2509
- * console.log(isUrl('https://www.google.com/?q=hello#world')); // true
2510
- * console.log(isUrl('https://www.google.com/#world')); // true
2511
- * console.log(isUrl('https://www.google.com/#')); // true
2512
- * console.log(isUrl('https://www.google.com/#?q=hello')); // true
2513
- * console.log(isUrl('https://www.google.com/#?q=hello#world')); // true
2514
- * ```
2515
- *
2516
- * @param url
2517
- * @returns {boolean} Return true if the given url is a valid url.
2518
- * @category URL
2519
- */
2520
- export function isValidUrl(url: string): boolean {
2521
- const reg = /[a-zA-Z0-9]+:\/\/[-a-zA-Z0-9@:%._+~#=]{1,256}\b([-a-zA-Z0-9\u4E00-\u9FA5()!@:%_+.~#?&//=]*)/gm;
2522
- return reg.test(url);
2523
- }
2524
-
2525
- /**
2526
- * Check if the given string is a mobile phone number.
2527
- *
2528
- * @example
2529
- * ```js
2530
- * console.log(isMobile('13800138000')); // true
2531
- * console.log(isMobile('1380013800')); // false
2532
- * console.log(isMobile('138001380000')); // false
2533
- * console.log(isMobile('1380013800a')); // false
2534
- * ```
2535
- *
2536
- * @param mobile
2537
- * @returns {boolean} Return true if the given string is a mobile phone number.
2538
- * @category Util
2539
- */
2540
- export function isValidPhoneNumber(mobile: string): boolean {
2541
- const reg = /^1[3456789]\d{9}$/;
2542
- return reg.test(mobile);
2543
- }