@etsoo/shared 1.2.41 → 1.2.43
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/.github/workflows/main.yml +3 -3
- package/README.md +3 -0
- package/__tests__/DomUtils.ts +89 -0
- package/__tests__/Utils.ts +7 -0
- package/lib/cjs/DomUtils.d.ts +43 -0
- package/lib/cjs/DomUtils.js +99 -0
- package/lib/cjs/Utils.d.ts +5 -0
- package/lib/cjs/Utils.js +23 -9
- package/lib/mjs/DomUtils.d.ts +43 -0
- package/lib/mjs/DomUtils.js +99 -0
- package/lib/mjs/Utils.d.ts +5 -0
- package/lib/mjs/Utils.js +23 -9
- package/package.json +1 -1
- package/src/DomUtils.ts +156 -0
- package/src/Utils.ts +23 -11
|
@@ -22,13 +22,13 @@ jobs:
|
|
|
22
22
|
steps:
|
|
23
23
|
# https://github.com/actions/checkout, This action checks-out your repository under $GITHUB_WORKSPACE
|
|
24
24
|
# so your workflow can access it.
|
|
25
|
-
- uses: actions/checkout@
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
26
|
|
|
27
27
|
# Set up your GitHub Actions workflow with a specific version of node.js
|
|
28
28
|
# Setup .npmrc file to publish to npm
|
|
29
|
-
- uses: actions/setup-node@
|
|
29
|
+
- uses: actions/setup-node@v4
|
|
30
30
|
with:
|
|
31
|
-
node-version: '
|
|
31
|
+
node-version: '20.x'
|
|
32
32
|
registry-url: 'https://registry.npmjs.org'
|
|
33
33
|
|
|
34
34
|
# Named after Continuous Integration, installs dependencies directly from package-lock.json
|
package/README.md
CHANGED
|
@@ -224,8 +224,10 @@ DOM/window related utilities
|
|
|
224
224
|
|headersToObject|Convert headers to object|
|
|
225
225
|
|isFormData|Is IFormData type guard|
|
|
226
226
|
|isJSONContentType|Is JSON content type|
|
|
227
|
+
|isWechatClient|Is Wechat client|
|
|
227
228
|
|mergeFormData|Merge form data to primary one|
|
|
228
229
|
|mergeURLSearchParams|Merge URL search parameters|
|
|
230
|
+
|parseUserAgent|parseUserAgent|
|
|
229
231
|
|setFocus|Set HTML element focus by name|
|
|
230
232
|
|setupLogging|Setup frontend logging|
|
|
231
233
|
|verifyPermission|Verify file system permission|
|
|
@@ -298,6 +300,7 @@ String and other related utilities
|
|
|
298
300
|
|parseJsonArray|Try to parse JSON input to array|
|
|
299
301
|
|parsePath|Parse path similar with node.js path.parse|
|
|
300
302
|
|parseString|Parse string (JSON) to specific type|
|
|
303
|
+
|removeEmptyValues|Remove empty values (null, undefined, '') from the input object|
|
|
301
304
|
|removeNonLetters|Remove non letters (0-9, a-z, A-Z)|
|
|
302
305
|
|replaceNullOrEmpty|Replace null or empty with default value|
|
|
303
306
|
|setLabels|Set source with new labels|
|
package/__tests__/DomUtils.ts
CHANGED
|
@@ -328,6 +328,95 @@ test('Tests for getInputValue', () => {
|
|
|
328
328
|
}
|
|
329
329
|
});
|
|
330
330
|
|
|
331
|
+
test('Tests for getUserAgentData 1', () => {
|
|
332
|
+
const data = DomUtils.parseUserAgent(
|
|
333
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'
|
|
334
|
+
);
|
|
335
|
+
expect(data?.device).toBe('Desktop');
|
|
336
|
+
expect(data?.platform).toBe('Windows NT');
|
|
337
|
+
expect(data?.platformVersion).toBe('10.0');
|
|
338
|
+
expect(data?.brands.find((b) => b.brand === 'Chrome')?.version).toBe('124');
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test('Tests for getUserAgentData 2', () => {
|
|
342
|
+
const data = DomUtils.parseUserAgent(
|
|
343
|
+
'Mozilla/5.0 (Linux; U; Android 2.3.6; zh-cn; GT-S5660 Build/GINGERBREAD) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 MicroMessenger/4.5.255'
|
|
344
|
+
);
|
|
345
|
+
expect(data?.device).toBe('GT-S5660');
|
|
346
|
+
expect(data?.platform).toBe('Android');
|
|
347
|
+
expect(data?.platformVersion).toBe('2.3.6');
|
|
348
|
+
expect(data?.mobile).toBeTruthy();
|
|
349
|
+
expect(DomUtils.isWechatClient(data)).toBeTruthy();
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test('Tests for getUserAgentData 3', () => {
|
|
353
|
+
const data = DomUtils.parseUserAgent(
|
|
354
|
+
'Mozilla/5.0 (Linux; Android 7.1.1;MEIZU E3 Build/NGI77B; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/9.6 TBS/044428 Mobile Safari/537.36 MicroMessenger/6.6.7.1321(0x26060739) NetType/WIFI Language/zh_CN'
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
expect(data?.device).toBe('MEIZU E3');
|
|
358
|
+
expect(data?.platform).toBe('Android');
|
|
359
|
+
expect(data?.platformVersion).toBe('7.1.1');
|
|
360
|
+
expect(data?.mobile).toBeTruthy();
|
|
361
|
+
expect(DomUtils.isWechatClient(data)).toBeTruthy();
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test('Tests for getUserAgentData 4', () => {
|
|
365
|
+
const data = DomUtils.parseUserAgent(
|
|
366
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Mobile/15E148 Safari/604.1'
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
expect(data?.device).toBe('iPhone');
|
|
370
|
+
expect(data?.platform).toBe('iPhone OS');
|
|
371
|
+
expect(data?.platformVersion).toBe('17.5.1');
|
|
372
|
+
expect(data?.mobile).toBeTruthy();
|
|
373
|
+
expect(DomUtils.isWechatClient(data)).toBeFalsy();
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test('Tests for getUserAgentData 5', () => {
|
|
377
|
+
const data = DomUtils.parseUserAgent(
|
|
378
|
+
'Mozilla/5.0 (SMART-TV; Linux; Tizen 2.3) AppleWebkit/538.1 (KHTML, like Gecko) SamsungBrowser/1.0 TV Safari/538.1'
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
expect(data?.device).toBe('SMART-TV');
|
|
382
|
+
expect(data?.platform).toBe('Tizen');
|
|
383
|
+
expect(data?.platformVersion).toBe('2.3');
|
|
384
|
+
expect(data?.mobile).toBeFalsy();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test('Tests for getUserAgentData 6', () => {
|
|
388
|
+
const data = DomUtils.parseUserAgent(
|
|
389
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15'
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
expect(data?.device).toBe('Macintosh');
|
|
393
|
+
expect(data?.platform).toBe('Mac OS X');
|
|
394
|
+
expect(data?.platformVersion).toBe('10.15');
|
|
395
|
+
expect(data?.mobile).toBeFalsy();
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test('Tests for getUserAgentData 7', () => {
|
|
399
|
+
const data = DomUtils.parseUserAgent(
|
|
400
|
+
'Mozilla/5.0 (Linux; Android 8.1; LEO-DLXXE Build/HONORLRA-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 HuaweiBrowser/9.1.1.308 Mobile Safari/537.36'
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
expect(data?.device).toBe('LEO-DLXXE');
|
|
404
|
+
expect(data?.platform).toBe('Android');
|
|
405
|
+
expect(data?.platformVersion).toBe('8.1');
|
|
406
|
+
expect(data?.mobile).toBeTruthy();
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test('Tests for getUserAgentData 8', () => {
|
|
410
|
+
const data = DomUtils.parseUserAgent(
|
|
411
|
+
'Mozilla/5.0 (Linux; Android 9; SM-R825F Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36'
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
expect(data?.device).toBe('SM-R825F');
|
|
415
|
+
expect(data?.platform).toBe('Android');
|
|
416
|
+
expect(data?.platformVersion).toBe('9');
|
|
417
|
+
expect(data?.mobile).toBeTruthy();
|
|
418
|
+
});
|
|
419
|
+
|
|
331
420
|
test('Tests for setupLogging', async () => {
|
|
332
421
|
// Arrange
|
|
333
422
|
const action = jest.fn((data: ErrorData) => {
|
package/__tests__/Utils.ts
CHANGED
|
@@ -296,6 +296,12 @@ test('Tests for parsePath, Windows path', () => {
|
|
|
296
296
|
expect(result.name).toBe('file');
|
|
297
297
|
});
|
|
298
298
|
|
|
299
|
+
test('Tests for removeEmptyValues', () => {
|
|
300
|
+
const obj = { a: 1, b: '', c: null, d: undefined, e: 'e' };
|
|
301
|
+
Utils.removeEmptyValues(obj);
|
|
302
|
+
expect(obj).toEqual({ a: 1, e: 'e' });
|
|
303
|
+
});
|
|
304
|
+
|
|
299
305
|
test('Tests for setNestedValue', () => {
|
|
300
306
|
const obj = { jsonData: { photoSize: [200, 100], supportResizing: true } };
|
|
301
307
|
|
|
@@ -355,4 +361,5 @@ test('Tests for trimEnd', () => {
|
|
|
355
361
|
expect(Utils.trimEnd('//a/', '/')).toBe('//a');
|
|
356
362
|
expect(Utils.trimEnd('/*/a*/', ...['/', '*'])).toBe('/*/a');
|
|
357
363
|
expect(Utils.trimEnd('abc', ...['/', '*'])).toBe('abc');
|
|
364
|
+
expect(Utils.trimEnd('12.0.0.0', '.0')).toBe('12');
|
|
358
365
|
});
|
package/lib/cjs/DomUtils.d.ts
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
import { DataTypes } from './DataTypes';
|
|
3
3
|
import { ErrorData, ErrorType } from './types/ErrorData';
|
|
4
4
|
import { FormDataFieldValue, IFormData } from './types/FormData';
|
|
5
|
+
/**
|
|
6
|
+
* User agent data, maybe replaced by navigator.userAgentData in future
|
|
7
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgentData
|
|
8
|
+
*/
|
|
9
|
+
export type UserAgentData = {
|
|
10
|
+
/**
|
|
11
|
+
* Browser brands
|
|
12
|
+
*/
|
|
13
|
+
brands: {
|
|
14
|
+
brand: string;
|
|
15
|
+
version: string;
|
|
16
|
+
}[];
|
|
17
|
+
/**
|
|
18
|
+
* Is mobile device
|
|
19
|
+
*/
|
|
20
|
+
mobile: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Device brand (name)
|
|
23
|
+
*/
|
|
24
|
+
device: string;
|
|
25
|
+
/**
|
|
26
|
+
* Platform (OS)
|
|
27
|
+
*/
|
|
28
|
+
platform: string;
|
|
29
|
+
/**
|
|
30
|
+
* Platform version
|
|
31
|
+
*/
|
|
32
|
+
platformVersion?: string;
|
|
33
|
+
};
|
|
5
34
|
/**
|
|
6
35
|
* Dom Utilities
|
|
7
36
|
* Not all methods support Node
|
|
@@ -71,6 +100,12 @@ export declare namespace DomUtils {
|
|
|
71
100
|
* @returns Object
|
|
72
101
|
*/
|
|
73
102
|
function formDataToObject(form: IFormData): Record<string, FormDataFieldValue | FormDataFieldValue[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Is wechat client
|
|
105
|
+
* @param data User agent data
|
|
106
|
+
* @returns Result
|
|
107
|
+
*/
|
|
108
|
+
function isWechatClient(data?: UserAgentData | null): boolean;
|
|
74
109
|
/**
|
|
75
110
|
* Culture match case Enum
|
|
76
111
|
*/
|
|
@@ -144,6 +179,14 @@ export declare namespace DomUtils {
|
|
|
144
179
|
* @param data New simple object data to merge
|
|
145
180
|
*/
|
|
146
181
|
function mergeURLSearchParams(base: URLSearchParams, data: DataTypes.SimpleObject): URLSearchParams;
|
|
182
|
+
/**
|
|
183
|
+
* Parse navigator's user agent string
|
|
184
|
+
* Lightweight User-Agent string parser
|
|
185
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent
|
|
186
|
+
* @param ua User agent string
|
|
187
|
+
* @returns User agent data
|
|
188
|
+
*/
|
|
189
|
+
function parseUserAgent(ua?: string): UserAgentData | null;
|
|
147
190
|
/**
|
|
148
191
|
* Set HTML element focus by name
|
|
149
192
|
* @param name Element name or first collection item
|
package/lib/cjs/DomUtils.js
CHANGED
|
@@ -307,6 +307,18 @@ var DomUtils;
|
|
|
307
307
|
return dic;
|
|
308
308
|
}
|
|
309
309
|
DomUtils.formDataToObject = formDataToObject;
|
|
310
|
+
/**
|
|
311
|
+
* Is wechat client
|
|
312
|
+
* @param data User agent data
|
|
313
|
+
* @returns Result
|
|
314
|
+
*/
|
|
315
|
+
function isWechatClient(data) {
|
|
316
|
+
data ?? (data = parseUserAgent());
|
|
317
|
+
if (!data)
|
|
318
|
+
return false;
|
|
319
|
+
return data.brands.some((item) => item.brand.toLowerCase() === 'micromessenger');
|
|
320
|
+
}
|
|
321
|
+
DomUtils.isWechatClient = isWechatClient;
|
|
310
322
|
/**
|
|
311
323
|
* Culture match case Enum
|
|
312
324
|
*/
|
|
@@ -483,6 +495,93 @@ var DomUtils;
|
|
|
483
495
|
return base;
|
|
484
496
|
}
|
|
485
497
|
DomUtils.mergeURLSearchParams = mergeURLSearchParams;
|
|
498
|
+
/**
|
|
499
|
+
* Parse navigator's user agent string
|
|
500
|
+
* Lightweight User-Agent string parser
|
|
501
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent
|
|
502
|
+
* @param ua User agent string
|
|
503
|
+
* @returns User agent data
|
|
504
|
+
*/
|
|
505
|
+
function parseUserAgent(ua) {
|
|
506
|
+
ua ?? (ua = globalThis.navigator.userAgent);
|
|
507
|
+
if (!ua) {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
const parts = ua.split(/(?!\(.*)\s+(?!\()(?![^(]*?\))/g);
|
|
511
|
+
let mobile = false;
|
|
512
|
+
let platform = '';
|
|
513
|
+
let platformVersion;
|
|
514
|
+
let device = 'Desktop';
|
|
515
|
+
const brands = [];
|
|
516
|
+
// with the 'g' will causing failures for multiple calls
|
|
517
|
+
const platformVersionReg = /^[a-zA-Z0-9-\s]+\s+(0|\d+)(\.(0|\d+)){0,3}(\(|$)/;
|
|
518
|
+
const versionReg = /^[a-zA-Z0-9]+\/(0|\d+)(\.(0|\d+)){0,3}(\(|$)/;
|
|
519
|
+
parts.forEach((part) => {
|
|
520
|
+
const pl = part.toLowerCase();
|
|
521
|
+
if (pl.startsWith('mozilla/')) {
|
|
522
|
+
const data = /\((.*)\)$/.exec(part);
|
|
523
|
+
if (data && data.length > 1) {
|
|
524
|
+
const pfItems = data[1].split(/;\s*/);
|
|
525
|
+
// Platform + Version
|
|
526
|
+
const pfIndex = pfItems.findIndex((item) => platformVersionReg.test(item));
|
|
527
|
+
if (pfIndex !== -1) {
|
|
528
|
+
const pfParts = pfItems[pfIndex].split(/\s+/);
|
|
529
|
+
platformVersion = pfParts.pop();
|
|
530
|
+
platform = pfParts.join(' ');
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
const appleVersionReg = /((iPhone|Mac)\s+OS(\s+\w+)?)\s+((0|\d+)(_(0|\d+)){0,3})/i;
|
|
534
|
+
for (let i = 0; i < pfItems.length; i++) {
|
|
535
|
+
const match = appleVersionReg.exec(pfItems[i]);
|
|
536
|
+
if (match && match.length > 4) {
|
|
537
|
+
platform = match[1];
|
|
538
|
+
platformVersion = match[4].replace(/_/g, '.');
|
|
539
|
+
pfItems.splice(i, 1);
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
// Device
|
|
545
|
+
const deviceIndex = pfItems.findIndex((item) => item.includes(' Build/'));
|
|
546
|
+
if (deviceIndex === -1) {
|
|
547
|
+
const firstItem = pfItems[0];
|
|
548
|
+
if (firstItem.toLowerCase() !== 'linux' &&
|
|
549
|
+
!firstItem.startsWith(platform)) {
|
|
550
|
+
device = firstItem;
|
|
551
|
+
pfItems.shift();
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
device = pfItems[deviceIndex].split(' Build/')[0];
|
|
556
|
+
pfItems.splice(deviceIndex, 1);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
if (pl === 'mobile' || pl.startsWith('mobile/')) {
|
|
562
|
+
mobile = true;
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
if (pl === 'version' || pl.startsWith('version/')) {
|
|
566
|
+
// No process
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
if (versionReg.test(part)) {
|
|
570
|
+
let [brand, version] = part.split('/');
|
|
571
|
+
const pindex = version.indexOf('(');
|
|
572
|
+
if (pindex > 0) {
|
|
573
|
+
version = version.substring(0, pindex);
|
|
574
|
+
}
|
|
575
|
+
brands.push({
|
|
576
|
+
brand,
|
|
577
|
+
version: Utils_1.Utils.trimEnd(version, '.0')
|
|
578
|
+
});
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
return { mobile, platform, platformVersion, brands, device };
|
|
583
|
+
}
|
|
584
|
+
DomUtils.parseUserAgent = parseUserAgent;
|
|
486
585
|
/**
|
|
487
586
|
* Set HTML element focus by name
|
|
488
587
|
* @param name Element name or first collection item
|
package/lib/cjs/Utils.d.ts
CHANGED
|
@@ -236,6 +236,11 @@ export declare namespace Utils {
|
|
|
236
236
|
* @returns Parsed value
|
|
237
237
|
*/
|
|
238
238
|
function parseString<T>(input: string | undefined | null, defaultValue: T): T;
|
|
239
|
+
/**
|
|
240
|
+
* Remove empty values (null, undefined, '') from the input object
|
|
241
|
+
* @param input Input object
|
|
242
|
+
*/
|
|
243
|
+
function removeEmptyValues(input: object): void;
|
|
239
244
|
/**
|
|
240
245
|
* Remove non letters
|
|
241
246
|
* @param input Input string
|
package/lib/cjs/Utils.js
CHANGED
|
@@ -477,6 +477,19 @@ var Utils;
|
|
|
477
477
|
}
|
|
478
478
|
}
|
|
479
479
|
Utils.parseString = parseString;
|
|
480
|
+
/**
|
|
481
|
+
* Remove empty values (null, undefined, '') from the input object
|
|
482
|
+
* @param input Input object
|
|
483
|
+
*/
|
|
484
|
+
function removeEmptyValues(input) {
|
|
485
|
+
Object.keys(input).forEach((key) => {
|
|
486
|
+
const value = Reflect.get(input, key);
|
|
487
|
+
if (value == null || value === '') {
|
|
488
|
+
Reflect.deleteProperty(input, key);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
Utils.removeEmptyValues = removeEmptyValues;
|
|
480
493
|
/**
|
|
481
494
|
* Remove non letters
|
|
482
495
|
* @param input Input string
|
|
@@ -655,10 +668,11 @@ var Utils;
|
|
|
655
668
|
* @returns Result
|
|
656
669
|
*/
|
|
657
670
|
Utils.trimEnd = (input, ...chars) => {
|
|
658
|
-
let
|
|
659
|
-
while (
|
|
660
|
-
|
|
661
|
-
|
|
671
|
+
let char;
|
|
672
|
+
while ((char = chars.find((char) => input.endsWith(char))) != null) {
|
|
673
|
+
input = input.substring(0, input.length - char.length);
|
|
674
|
+
}
|
|
675
|
+
return input;
|
|
662
676
|
};
|
|
663
677
|
/**
|
|
664
678
|
* Trim start chars
|
|
@@ -667,10 +681,10 @@ var Utils;
|
|
|
667
681
|
* @returns Result
|
|
668
682
|
*/
|
|
669
683
|
Utils.trimStart = (input, ...chars) => {
|
|
670
|
-
let
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
return input
|
|
684
|
+
let char;
|
|
685
|
+
while ((char = chars.find((char) => input.startsWith(char))) != null) {
|
|
686
|
+
input = input.substring(char.length);
|
|
687
|
+
}
|
|
688
|
+
return input;
|
|
675
689
|
};
|
|
676
690
|
})(Utils || (exports.Utils = Utils = {}));
|
package/lib/mjs/DomUtils.d.ts
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
import { DataTypes } from './DataTypes';
|
|
3
3
|
import { ErrorData, ErrorType } from './types/ErrorData';
|
|
4
4
|
import { FormDataFieldValue, IFormData } from './types/FormData';
|
|
5
|
+
/**
|
|
6
|
+
* User agent data, maybe replaced by navigator.userAgentData in future
|
|
7
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgentData
|
|
8
|
+
*/
|
|
9
|
+
export type UserAgentData = {
|
|
10
|
+
/**
|
|
11
|
+
* Browser brands
|
|
12
|
+
*/
|
|
13
|
+
brands: {
|
|
14
|
+
brand: string;
|
|
15
|
+
version: string;
|
|
16
|
+
}[];
|
|
17
|
+
/**
|
|
18
|
+
* Is mobile device
|
|
19
|
+
*/
|
|
20
|
+
mobile: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Device brand (name)
|
|
23
|
+
*/
|
|
24
|
+
device: string;
|
|
25
|
+
/**
|
|
26
|
+
* Platform (OS)
|
|
27
|
+
*/
|
|
28
|
+
platform: string;
|
|
29
|
+
/**
|
|
30
|
+
* Platform version
|
|
31
|
+
*/
|
|
32
|
+
platformVersion?: string;
|
|
33
|
+
};
|
|
5
34
|
/**
|
|
6
35
|
* Dom Utilities
|
|
7
36
|
* Not all methods support Node
|
|
@@ -71,6 +100,12 @@ export declare namespace DomUtils {
|
|
|
71
100
|
* @returns Object
|
|
72
101
|
*/
|
|
73
102
|
function formDataToObject(form: IFormData): Record<string, FormDataFieldValue | FormDataFieldValue[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Is wechat client
|
|
105
|
+
* @param data User agent data
|
|
106
|
+
* @returns Result
|
|
107
|
+
*/
|
|
108
|
+
function isWechatClient(data?: UserAgentData | null): boolean;
|
|
74
109
|
/**
|
|
75
110
|
* Culture match case Enum
|
|
76
111
|
*/
|
|
@@ -144,6 +179,14 @@ export declare namespace DomUtils {
|
|
|
144
179
|
* @param data New simple object data to merge
|
|
145
180
|
*/
|
|
146
181
|
function mergeURLSearchParams(base: URLSearchParams, data: DataTypes.SimpleObject): URLSearchParams;
|
|
182
|
+
/**
|
|
183
|
+
* Parse navigator's user agent string
|
|
184
|
+
* Lightweight User-Agent string parser
|
|
185
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent
|
|
186
|
+
* @param ua User agent string
|
|
187
|
+
* @returns User agent data
|
|
188
|
+
*/
|
|
189
|
+
function parseUserAgent(ua?: string): UserAgentData | null;
|
|
147
190
|
/**
|
|
148
191
|
* Set HTML element focus by name
|
|
149
192
|
* @param name Element name or first collection item
|
package/lib/mjs/DomUtils.js
CHANGED
|
@@ -304,6 +304,18 @@ export var DomUtils;
|
|
|
304
304
|
return dic;
|
|
305
305
|
}
|
|
306
306
|
DomUtils.formDataToObject = formDataToObject;
|
|
307
|
+
/**
|
|
308
|
+
* Is wechat client
|
|
309
|
+
* @param data User agent data
|
|
310
|
+
* @returns Result
|
|
311
|
+
*/
|
|
312
|
+
function isWechatClient(data) {
|
|
313
|
+
data ?? (data = parseUserAgent());
|
|
314
|
+
if (!data)
|
|
315
|
+
return false;
|
|
316
|
+
return data.brands.some((item) => item.brand.toLowerCase() === 'micromessenger');
|
|
317
|
+
}
|
|
318
|
+
DomUtils.isWechatClient = isWechatClient;
|
|
307
319
|
/**
|
|
308
320
|
* Culture match case Enum
|
|
309
321
|
*/
|
|
@@ -480,6 +492,93 @@ export var DomUtils;
|
|
|
480
492
|
return base;
|
|
481
493
|
}
|
|
482
494
|
DomUtils.mergeURLSearchParams = mergeURLSearchParams;
|
|
495
|
+
/**
|
|
496
|
+
* Parse navigator's user agent string
|
|
497
|
+
* Lightweight User-Agent string parser
|
|
498
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent
|
|
499
|
+
* @param ua User agent string
|
|
500
|
+
* @returns User agent data
|
|
501
|
+
*/
|
|
502
|
+
function parseUserAgent(ua) {
|
|
503
|
+
ua ?? (ua = globalThis.navigator.userAgent);
|
|
504
|
+
if (!ua) {
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
const parts = ua.split(/(?!\(.*)\s+(?!\()(?![^(]*?\))/g);
|
|
508
|
+
let mobile = false;
|
|
509
|
+
let platform = '';
|
|
510
|
+
let platformVersion;
|
|
511
|
+
let device = 'Desktop';
|
|
512
|
+
const brands = [];
|
|
513
|
+
// with the 'g' will causing failures for multiple calls
|
|
514
|
+
const platformVersionReg = /^[a-zA-Z0-9-\s]+\s+(0|\d+)(\.(0|\d+)){0,3}(\(|$)/;
|
|
515
|
+
const versionReg = /^[a-zA-Z0-9]+\/(0|\d+)(\.(0|\d+)){0,3}(\(|$)/;
|
|
516
|
+
parts.forEach((part) => {
|
|
517
|
+
const pl = part.toLowerCase();
|
|
518
|
+
if (pl.startsWith('mozilla/')) {
|
|
519
|
+
const data = /\((.*)\)$/.exec(part);
|
|
520
|
+
if (data && data.length > 1) {
|
|
521
|
+
const pfItems = data[1].split(/;\s*/);
|
|
522
|
+
// Platform + Version
|
|
523
|
+
const pfIndex = pfItems.findIndex((item) => platformVersionReg.test(item));
|
|
524
|
+
if (pfIndex !== -1) {
|
|
525
|
+
const pfParts = pfItems[pfIndex].split(/\s+/);
|
|
526
|
+
platformVersion = pfParts.pop();
|
|
527
|
+
platform = pfParts.join(' ');
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
const appleVersionReg = /((iPhone|Mac)\s+OS(\s+\w+)?)\s+((0|\d+)(_(0|\d+)){0,3})/i;
|
|
531
|
+
for (let i = 0; i < pfItems.length; i++) {
|
|
532
|
+
const match = appleVersionReg.exec(pfItems[i]);
|
|
533
|
+
if (match && match.length > 4) {
|
|
534
|
+
platform = match[1];
|
|
535
|
+
platformVersion = match[4].replace(/_/g, '.');
|
|
536
|
+
pfItems.splice(i, 1);
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
// Device
|
|
542
|
+
const deviceIndex = pfItems.findIndex((item) => item.includes(' Build/'));
|
|
543
|
+
if (deviceIndex === -1) {
|
|
544
|
+
const firstItem = pfItems[0];
|
|
545
|
+
if (firstItem.toLowerCase() !== 'linux' &&
|
|
546
|
+
!firstItem.startsWith(platform)) {
|
|
547
|
+
device = firstItem;
|
|
548
|
+
pfItems.shift();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
device = pfItems[deviceIndex].split(' Build/')[0];
|
|
553
|
+
pfItems.splice(deviceIndex, 1);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (pl === 'mobile' || pl.startsWith('mobile/')) {
|
|
559
|
+
mobile = true;
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (pl === 'version' || pl.startsWith('version/')) {
|
|
563
|
+
// No process
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
if (versionReg.test(part)) {
|
|
567
|
+
let [brand, version] = part.split('/');
|
|
568
|
+
const pindex = version.indexOf('(');
|
|
569
|
+
if (pindex > 0) {
|
|
570
|
+
version = version.substring(0, pindex);
|
|
571
|
+
}
|
|
572
|
+
brands.push({
|
|
573
|
+
brand,
|
|
574
|
+
version: Utils.trimEnd(version, '.0')
|
|
575
|
+
});
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
return { mobile, platform, platformVersion, brands, device };
|
|
580
|
+
}
|
|
581
|
+
DomUtils.parseUserAgent = parseUserAgent;
|
|
483
582
|
/**
|
|
484
583
|
* Set HTML element focus by name
|
|
485
584
|
* @param name Element name or first collection item
|
package/lib/mjs/Utils.d.ts
CHANGED
|
@@ -236,6 +236,11 @@ export declare namespace Utils {
|
|
|
236
236
|
* @returns Parsed value
|
|
237
237
|
*/
|
|
238
238
|
function parseString<T>(input: string | undefined | null, defaultValue: T): T;
|
|
239
|
+
/**
|
|
240
|
+
* Remove empty values (null, undefined, '') from the input object
|
|
241
|
+
* @param input Input object
|
|
242
|
+
*/
|
|
243
|
+
function removeEmptyValues(input: object): void;
|
|
239
244
|
/**
|
|
240
245
|
* Remove non letters
|
|
241
246
|
* @param input Input string
|
package/lib/mjs/Utils.js
CHANGED
|
@@ -471,6 +471,19 @@ export var Utils;
|
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
473
|
Utils.parseString = parseString;
|
|
474
|
+
/**
|
|
475
|
+
* Remove empty values (null, undefined, '') from the input object
|
|
476
|
+
* @param input Input object
|
|
477
|
+
*/
|
|
478
|
+
function removeEmptyValues(input) {
|
|
479
|
+
Object.keys(input).forEach((key) => {
|
|
480
|
+
const value = Reflect.get(input, key);
|
|
481
|
+
if (value == null || value === '') {
|
|
482
|
+
Reflect.deleteProperty(input, key);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
Utils.removeEmptyValues = removeEmptyValues;
|
|
474
487
|
/**
|
|
475
488
|
* Remove non letters
|
|
476
489
|
* @param input Input string
|
|
@@ -649,10 +662,11 @@ export var Utils;
|
|
|
649
662
|
* @returns Result
|
|
650
663
|
*/
|
|
651
664
|
Utils.trimEnd = (input, ...chars) => {
|
|
652
|
-
let
|
|
653
|
-
while (
|
|
654
|
-
|
|
655
|
-
|
|
665
|
+
let char;
|
|
666
|
+
while ((char = chars.find((char) => input.endsWith(char))) != null) {
|
|
667
|
+
input = input.substring(0, input.length - char.length);
|
|
668
|
+
}
|
|
669
|
+
return input;
|
|
656
670
|
};
|
|
657
671
|
/**
|
|
658
672
|
* Trim start chars
|
|
@@ -661,10 +675,10 @@ export var Utils;
|
|
|
661
675
|
* @returns Result
|
|
662
676
|
*/
|
|
663
677
|
Utils.trimStart = (input, ...chars) => {
|
|
664
|
-
let
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
return input
|
|
678
|
+
let char;
|
|
679
|
+
while ((char = chars.find((char) => input.startsWith(char))) != null) {
|
|
680
|
+
input = input.substring(char.length);
|
|
681
|
+
}
|
|
682
|
+
return input;
|
|
669
683
|
};
|
|
670
684
|
})(Utils || (Utils = {}));
|
package/package.json
CHANGED
package/src/DomUtils.ts
CHANGED
|
@@ -11,6 +11,40 @@ if (typeof navigator === 'undefined') {
|
|
|
11
11
|
globalThis.location = { href: 'http://localhost/' } as any;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* User agent data, maybe replaced by navigator.userAgentData in future
|
|
16
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgentData
|
|
17
|
+
*/
|
|
18
|
+
export type UserAgentData = {
|
|
19
|
+
/**
|
|
20
|
+
* Browser brands
|
|
21
|
+
*/
|
|
22
|
+
brands: {
|
|
23
|
+
brand: string;
|
|
24
|
+
version: string;
|
|
25
|
+
}[];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Is mobile device
|
|
29
|
+
*/
|
|
30
|
+
mobile: boolean;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Device brand (name)
|
|
34
|
+
*/
|
|
35
|
+
device: string;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Platform (OS)
|
|
39
|
+
*/
|
|
40
|
+
platform: string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Platform version
|
|
44
|
+
*/
|
|
45
|
+
platformVersion?: string;
|
|
46
|
+
};
|
|
47
|
+
|
|
14
48
|
/**
|
|
15
49
|
* Dom Utilities
|
|
16
50
|
* Not all methods support Node
|
|
@@ -367,6 +401,20 @@ export namespace DomUtils {
|
|
|
367
401
|
return dic;
|
|
368
402
|
}
|
|
369
403
|
|
|
404
|
+
/**
|
|
405
|
+
* Is wechat client
|
|
406
|
+
* @param data User agent data
|
|
407
|
+
* @returns Result
|
|
408
|
+
*/
|
|
409
|
+
export function isWechatClient(data?: UserAgentData | null) {
|
|
410
|
+
data ??= parseUserAgent();
|
|
411
|
+
if (!data) return false;
|
|
412
|
+
|
|
413
|
+
return data.brands.some(
|
|
414
|
+
(item) => item.brand.toLowerCase() === 'micromessenger'
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
370
418
|
/**
|
|
371
419
|
* Culture match case Enum
|
|
372
420
|
*/
|
|
@@ -583,6 +631,114 @@ export namespace DomUtils {
|
|
|
583
631
|
return base;
|
|
584
632
|
}
|
|
585
633
|
|
|
634
|
+
/**
|
|
635
|
+
* Parse navigator's user agent string
|
|
636
|
+
* Lightweight User-Agent string parser
|
|
637
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent
|
|
638
|
+
* @param ua User agent string
|
|
639
|
+
* @returns User agent data
|
|
640
|
+
*/
|
|
641
|
+
export function parseUserAgent(ua?: string): UserAgentData | null {
|
|
642
|
+
ua ??= globalThis.navigator.userAgent;
|
|
643
|
+
|
|
644
|
+
if (!ua) {
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const parts = ua.split(/(?!\(.*)\s+(?!\()(?![^(]*?\))/g);
|
|
649
|
+
|
|
650
|
+
let mobile = false;
|
|
651
|
+
let platform = '';
|
|
652
|
+
let platformVersion: string | undefined;
|
|
653
|
+
let device = 'Desktop';
|
|
654
|
+
const brands: UserAgentData['brands'] = [];
|
|
655
|
+
|
|
656
|
+
// with the 'g' will causing failures for multiple calls
|
|
657
|
+
const platformVersionReg =
|
|
658
|
+
/^[a-zA-Z0-9-\s]+\s+(0|\d+)(\.(0|\d+)){0,3}(\(|$)/;
|
|
659
|
+
const versionReg = /^[a-zA-Z0-9]+\/(0|\d+)(\.(0|\d+)){0,3}(\(|$)/;
|
|
660
|
+
|
|
661
|
+
parts.forEach((part) => {
|
|
662
|
+
const pl = part.toLowerCase();
|
|
663
|
+
|
|
664
|
+
if (pl.startsWith('mozilla/')) {
|
|
665
|
+
const data = /\((.*)\)$/.exec(part);
|
|
666
|
+
if (data && data.length > 1) {
|
|
667
|
+
const pfItems = data[1].split(/;\s*/);
|
|
668
|
+
|
|
669
|
+
// Platform + Version
|
|
670
|
+
const pfIndex = pfItems.findIndex((item) =>
|
|
671
|
+
platformVersionReg.test(item)
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
if (pfIndex !== -1) {
|
|
675
|
+
const pfParts = pfItems[pfIndex].split(/\s+/);
|
|
676
|
+
platformVersion = pfParts.pop();
|
|
677
|
+
platform = pfParts.join(' ');
|
|
678
|
+
} else {
|
|
679
|
+
const appleVersionReg =
|
|
680
|
+
/((iPhone|Mac)\s+OS(\s+\w+)?)\s+((0|\d+)(_(0|\d+)){0,3})/i;
|
|
681
|
+
|
|
682
|
+
for (let i = 0; i < pfItems.length; i++) {
|
|
683
|
+
const match = appleVersionReg.exec(pfItems[i]);
|
|
684
|
+
if (match && match.length > 4) {
|
|
685
|
+
platform = match[1];
|
|
686
|
+
platformVersion = match[4].replace(/_/g, '.');
|
|
687
|
+
|
|
688
|
+
pfItems.splice(i, 1);
|
|
689
|
+
break;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Device
|
|
695
|
+
const deviceIndex = pfItems.findIndex((item) =>
|
|
696
|
+
item.includes(' Build/')
|
|
697
|
+
);
|
|
698
|
+
if (deviceIndex === -1) {
|
|
699
|
+
const firstItem = pfItems[0];
|
|
700
|
+
if (
|
|
701
|
+
firstItem.toLowerCase() !== 'linux' &&
|
|
702
|
+
!firstItem.startsWith(platform)
|
|
703
|
+
) {
|
|
704
|
+
device = firstItem;
|
|
705
|
+
pfItems.shift();
|
|
706
|
+
}
|
|
707
|
+
} else {
|
|
708
|
+
device = pfItems[deviceIndex].split(' Build/')[0];
|
|
709
|
+
pfItems.splice(deviceIndex, 1);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (pl === 'mobile' || pl.startsWith('mobile/')) {
|
|
716
|
+
mobile = true;
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
if (pl === 'version' || pl.startsWith('version/')) {
|
|
721
|
+
// No process
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
if (versionReg.test(part)) {
|
|
726
|
+
let [brand, version] = part.split('/');
|
|
727
|
+
const pindex = version.indexOf('(');
|
|
728
|
+
if (pindex > 0) {
|
|
729
|
+
version = version.substring(0, pindex);
|
|
730
|
+
}
|
|
731
|
+
brands.push({
|
|
732
|
+
brand,
|
|
733
|
+
version: Utils.trimEnd(version, '.0')
|
|
734
|
+
});
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
return { mobile, platform, platformVersion, brands, device };
|
|
740
|
+
}
|
|
741
|
+
|
|
586
742
|
/**
|
|
587
743
|
* Set HTML element focus by name
|
|
588
744
|
* @param name Element name or first collection item
|
package/src/Utils.ts
CHANGED
|
@@ -657,6 +657,19 @@ export namespace Utils {
|
|
|
657
657
|
}
|
|
658
658
|
}
|
|
659
659
|
|
|
660
|
+
/**
|
|
661
|
+
* Remove empty values (null, undefined, '') from the input object
|
|
662
|
+
* @param input Input object
|
|
663
|
+
*/
|
|
664
|
+
export function removeEmptyValues(input: object) {
|
|
665
|
+
Object.keys(input).forEach((key) => {
|
|
666
|
+
const value = Reflect.get(input, key);
|
|
667
|
+
if (value == null || value === '') {
|
|
668
|
+
Reflect.deleteProperty(input, key);
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
|
|
660
673
|
/**
|
|
661
674
|
* Remove non letters
|
|
662
675
|
* @param input Input string
|
|
@@ -870,11 +883,11 @@ export namespace Utils {
|
|
|
870
883
|
* @returns Result
|
|
871
884
|
*/
|
|
872
885
|
export const trimEnd = (input: string, ...chars: string[]) => {
|
|
873
|
-
let
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
return input
|
|
886
|
+
let char: string | undefined;
|
|
887
|
+
while ((char = chars.find((char) => input.endsWith(char))) != null) {
|
|
888
|
+
input = input.substring(0, input.length - char.length);
|
|
889
|
+
}
|
|
890
|
+
return input;
|
|
878
891
|
};
|
|
879
892
|
|
|
880
893
|
/**
|
|
@@ -884,11 +897,10 @@ export namespace Utils {
|
|
|
884
897
|
* @returns Result
|
|
885
898
|
*/
|
|
886
899
|
export const trimStart = (input: string, ...chars: string[]) => {
|
|
887
|
-
let
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
return input.substring(start);
|
|
900
|
+
let char: string | undefined;
|
|
901
|
+
while ((char = chars.find((char) => input.startsWith(char))) != null) {
|
|
902
|
+
input = input.substring(char.length);
|
|
903
|
+
}
|
|
904
|
+
return input;
|
|
893
905
|
};
|
|
894
906
|
}
|