Skip to content

Commit 68bc6ac

Browse files
authored
Pr device info113 (#173)
* chore: remove nyc package for dev devDependencies * update test fixtures and regex * update CLIENTHINT_MAPPING Browsers * update CLIENTS_TV added hasVRFragment * update hasAndroidTableFragment * update PICO OS and Pico devices * update detect 360 Secure Browser * fix detect trusted apple stage is gpu wmware * update fixtures * update regex for Gecko and Clecko
1 parent 9789e4f commit 68bc6ac

File tree

116 files changed

+17249
-9286
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+17249
-9286
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ detector.detect (indexes on) x 1,032 ops/sec ±0.61% (94 runs sampled)
401401
</details>
402402

403403
### What about tests?
404-
Yes we use tests, total tests 73.9k
404+
Yes we use tests, total tests 74.8k
405405

406406
### Get more information about a device (experimental)
407407
> This parser is experimental and contains few devices. (1825 devices, alias devices 3891)

client-hints.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ class ClientHints {
171171
let matches = null;
172172
while (matches = pattern.exec(value)) {
173173
let brand = matches[1];
174-
let skip = brand.indexOf('Not;A') !== -1 || brand.indexOf('Not A;') !== -1;
174+
let skip = brand.indexOf('Not;A') !== -1 || brand.indexOf('Not A;') !== -1 || brand.indexOf('Not.A') !== -1;
175175
if (skip) {
176176
continue;
177177
}

index.js

Lines changed: 90 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,10 @@ class DeviceDetector {
103103
this.deviceInfo = attr(options, 'deviceInfo', false);
104104
}
105105

106+
106107
init() {
107108
this.addParseOs(OS_PARSER, new OsParser());
109+
108110
this.addParseClient(CLIENT_PARSER_LIST.FEED_READER, new FeedReaderParser());
109111
this.addParseClient(CLIENT_PARSER_LIST.MOBILE_APP, new MobileAppParser());
110112
this.addParseClient(CLIENT_PARSER_LIST.MEDIA_PLAYER, new MediaPlayerParser());
@@ -495,11 +497,21 @@ class DeviceDetector {
495497
let clientFamily = attr(clientData, 'family', '');
496498
let deviceType = attr(deviceData, 'type', '');
497499

498-
if (
499-
deviceType === '' &&
500-
osFamily === 'Android' &&
501-
helper.matchUserAgent('Chrome/[.0-9]*', userAgent)
502-
) {
500+
/**
501+
* All devices containing VR fragment are assumed to be a wearable
502+
*/
503+
if (deviceType === '' && helper.hasVRFragment(userAgent)) {
504+
deviceType = DEVICE_TYPE.WEARABLE;
505+
}
506+
507+
/**
508+
* Chrome on Android passes the device type based on the keyword 'Mobile'
509+
* If it is present the device should be a smartphone, otherwise it's a tablet
510+
* See https://developer.chrome.com/multidevice/user-agent#chrome_for_android_user_agent
511+
* Note: We do not check for browser (family) here, as there might be mobile apps using Chrome, that won't have
512+
* a detected browser, but can still be detected. So we check the useragent for Chrome instead.
513+
*/
514+
if (deviceType === '' && osFamily === 'Android' && helper.matchUserAgent('Chrome/[.0-9]*', userAgent)) {
503515
if (helper.matchUserAgent('(Mobile|eliboM)', userAgent) !== null) {
504516
deviceType = DEVICE_TYPE.SMARTPHONE;
505517
} else{
@@ -510,24 +522,32 @@ class DeviceDetector {
510522
/**
511523
* Some UA contain the fragment 'Pad/APad', so we assume those devices as tablets
512524
*/
513-
if (deviceType === DEVICE_TYPE.SMARTPHONE
514-
&& helper.matchUserAgent('Pad/APad', userAgent)
515-
) {
525+
if (deviceType === DEVICE_TYPE.SMARTPHONE && helper.matchUserAgent('Pad/APad', userAgent)) {
516526
deviceType = DEVICE_TYPE.TABLET;
517527
}
518528

519-
if (
520-
deviceType === '' &&
521-
(helper.hasAndroidTableFragment(userAgent) ||
522-
helper.hasOperaTableFragment(userAgent))
523-
) {
529+
/**
530+
* Some UA contain the fragment 'Android; Tablet;' or 'Opera Tablet', so we assume those devices as tablets
531+
*/
532+
if (deviceType === '' && (helper.hasAndroidTableFragment(userAgent) || helper.hasOperaTableFragment(userAgent))) {
524533
deviceType = DEVICE_TYPE.TABLET;
525534
}
526535

536+
/**
537+
* Some user agents simply contain the fragment 'Android; Mobile;', so we assume those devices as smartphones
538+
*/
527539
if (deviceType === '' && helper.hasAndroidMobileFragment(userAgent)) {
528540
deviceType = DEVICE_TYPE.SMARTPHONE;
529541
}
530542

543+
/**
544+
* Android up to 3.0 was designed for smartphones only. But as 3.0, which was tablet only, was published
545+
* too late, there were a bunch of tablets running with 2.x
546+
* With 4.0 the two trees were merged and it is for smartphones and tablets
547+
*
548+
* So were are expecting that all devices running Android < 2 are smartphones
549+
* Devices running Android 3.X are tablets. Device type of Android 2.X and 4.X+ are unknown
550+
*/
531551
if (deviceType === '' && osName === 'Android' && osVersion !== '') {
532552
if (helper.versionCompare(osVersion, '2.0') === -1) {
533553
deviceType = DEVICE_TYPE.SMARTPHONE;
@@ -539,6 +559,9 @@ class DeviceDetector {
539559
}
540560
}
541561

562+
/**
563+
* All detected feature phones running android are more likely a smartphone
564+
*/
542565
if (deviceType === DEVICE_TYPE.FEATURE_PHONE && osFamily === 'Android') {
543566
deviceType = DEVICE_TYPE.SMARTPHONE;
544567
}
@@ -551,6 +574,15 @@ class DeviceDetector {
551574
deviceType = DEVICE_TYPE.FEATURE_PHONE;
552575
}
553576

577+
/**
578+
* According to http://msdn.microsoft.com/en-us/library/ie/hh920767(v=vs.85).aspx
579+
* Internet Explorer 10 introduces the "Touch" UA string token. If this token is present at the end of the
580+
* UA string, the computer has touch capability, and is running Windows 8 (or later).
581+
* This UA string will be transmitted on a touch-enabled system running Windows 8 (RT)
582+
*
583+
* As most touch enabled devices are tablets and only a smaller part are desktops/notebooks we assume that
584+
* all Windows 8 touch devices are tablets.
585+
*/
554586
if (
555587
deviceType === '' &&
556588
(osName === 'Windows RT' ||
@@ -560,14 +592,28 @@ class DeviceDetector {
560592
deviceType = DEVICE_TYPE.TABLET;
561593
}
562594

563-
// check tv fragments and tv clients
595+
/**
596+
* All devices running Opera TV Store are assumed to be a tv
597+
*/
564598
if (helper.hasOperaTVStoreFragment(userAgent)) {
565599
deviceType = DEVICE_TYPE.TV;
566-
} else if (helper.hasAndroidTVFragment(userAgent)) {
600+
}
601+
/**
602+
* All devices that contain Andr0id in string are assumed to be a tv
603+
*/
604+
if (helper.hasAndroidTVFragment(userAgent)) {
567605
deviceType = DEVICE_TYPE.TV;
568-
} else if (deviceType === '' && helper.hasTVFragment(userAgent)) {
606+
}
607+
/**
608+
* All devices running Tizen TV or SmartTV are assumed to be a tv
609+
*/
610+
if (deviceType === '' && helper.hasTVFragment(userAgent)) {
569611
deviceType = DEVICE_TYPE.TV;
570-
} else if (CLIENT_TV_LIST.indexOf(clientName) !== -1) {
612+
}
613+
/**
614+
* Devices running those clients are assumed to be a TV
615+
*/
616+
if (CLIENT_TV_LIST.indexOf(clientName) !== -1) {
571617
deviceType = DEVICE_TYPE.TV;
572618
}
573619

@@ -701,10 +747,8 @@ class DeviceDetector {
701747
}
702748

703749
// client hints
704-
if (result.model === '') {
705-
if (clientHints.device && clientHints.device.model !== '') {
706-
result.model = clientHints.device.model;
707-
}
750+
if (result.model === '' && clientHints.device && clientHints.device.model !== '') {
751+
result.model = clientHints.device.model;
708752
}
709753

710754
// device info or deviceTrusted
@@ -759,43 +803,25 @@ class DeviceDetector {
759803
*/
760804
parseClient(userAgent, clientHints) {
761805
const extendParsers = [CLIENT_PARSER_LIST.MOBILE_APP, CLIENT_PARSER_LIST.BROWSER];
762-
763-
let result = {};
764806
for (let name in this.clientParserList) {
765807
let parser = this.clientParserList[name];
808+
766809
if (this.clientIndexes && extendParsers.includes(name)) {
767810
let hash = parser.parseFromHashHintsApp(clientHints);
768811
let hint = parser.parseFromClientHints(clientHints);
769812
let data = parser.parseUserAgentByPositions(userAgent);
770813
let result = parser.prepareParseResult(userAgent, data, hint, hash);
771814
if (result !== null && result.name) {
772-
return result;
815+
return Object.assign({}, result);
773816
}
774-
continue;
775817
}
776818

777-
let resultMerge = parser.parse(userAgent, clientHints);
778-
if (resultMerge) {
779-
return Object.assign(result, resultMerge);
819+
let result = parser.parse(userAgent, clientHints);
820+
if (result && result.name) {
821+
return Object.assign({}, result);
780822
}
781823
}
782-
783-
if (this.clientIndexes) {
784-
for (let i = 0, l = extendParsers.length; i < l; i++) {
785-
let name = extendParsers[i];
786-
let parser = this.clientParserList[name];
787-
if (!parser) {
788-
continue;
789-
}
790-
791-
let resultMerge = parser.parse(userAgent, clientHints);
792-
if (resultMerge) {
793-
return Object.assign(result, resultMerge);
794-
}
795-
}
796-
}
797-
798-
return result;
824+
return {};
799825
}
800826

801827
prepareDetectResult(
@@ -805,34 +831,35 @@ class DeviceDetector {
805831
deviceData,
806832
clientHints
807833
) {
808-
let deviceDataType = this.parseDeviceType(
809-
userAgent,
810-
osData,
811-
clientData,
812-
deviceData,
813-
clientHints
814-
);
815834

816-
deviceData = Object.assign(deviceData, deviceDataType);
817835
/**
818-
* if it's fake UA then it's best not to identify it as Apple running Android OS
836+
* if it's fake UA then it's best not to identify it as Apple running Android OS or GNU/Linux
819837
*/
820-
if ('Android' === osData.name && 'Apple' === deviceData.brand) {
838+
if (deviceData.brand === 'Apple' && APPLE_OS_LIST.indexOf(osData.name) === -1) {
821839
deviceData.id = '';
822840
deviceData.brand = '';
823841
deviceData.model = '';
824842
deviceData.type = '';
825843
}
826-
/** Assume all devices running iOS / Mac OS are from Apple */
827-
if (
828-
deviceData.brand === '' &&
829-
osData.name !== '' &&
830-
APPLE_OS_LIST.indexOf(osData.name) !== -1
831-
) {
832-
deviceData.id = 'AP';
844+
845+
/**
846+
* Assume all devices running iOS / Mac OS are from Apple
847+
*/
848+
if (deviceData.brand === '' && APPLE_OS_LIST.indexOf(osData.name) !== -1) {
833849
deviceData.brand = 'Apple';
834850
}
835851

852+
let deviceDataType = this.parseDeviceType(
853+
userAgent,
854+
osData,
855+
clientData,
856+
deviceData,
857+
clientHints
858+
);
859+
860+
deviceData = Object.assign(deviceData, deviceDataType);
861+
862+
836863
if (this.deviceTrusted) {
837864
deviceData.trusted = DeviceTrusted.check(osData, clientData, deviceData, clientHints);
838865
} else {
@@ -888,7 +915,7 @@ class DeviceDetector {
888915
/**
889916
* detect os, client and device for sync
890917
* @param {string} userAgent - string from request header['user-agent']
891-
* @param clientHints
918+
* @param {{}} clientHints
892919
* @return {DetectResult}
893920
*/
894921
detect(userAgent, clientHints = {}) {

0 commit comments

Comments
 (0)