ua-parser-js 2.0.0-alpha.2 → 2.0.0-beta.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,23 +1,26 @@
1
1
  /////////////////////////////////////////////////////////////////////////////////
2
- /* UAParser.js v2.0.0-alpha.2
2
+ /* UAParser.js v2.0.0-beta.1
3
3
  Copyright © 2012-2023 Faisal Salman <f@faisalman.com>
4
- MIT License *//*
4
+ AGPLv3 License *//*
5
5
  Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
6
6
  Supports browser & node.js environment.
7
7
  Demo : https://faisalman.github.io/ua-parser-js
8
8
  Source : https://github.com/faisalman/ua-parser-js */
9
9
  /////////////////////////////////////////////////////////////////////////////////
10
10
 
11
+ /* jshint esversion: 3 */
12
+ /* globals window */
13
+
11
14
  (function (window, undefined) {
12
15
 
13
16
  'use strict';
14
-
17
+
15
18
  //////////////
16
19
  // Constants
17
20
  /////////////
18
21
 
19
22
 
20
- var LIBVERSION = '2.0.0-alpha.2',
23
+ var LIBVERSION = '2.0.0-beta.1',
21
24
  EMPTY = '',
22
25
  UNKNOWN = '?',
23
26
  FUNC_TYPE = 'function',
@@ -40,6 +43,7 @@
40
43
  USER_AGENT = 'user-agent',
41
44
  UA_MAX_LENGTH = 350,
42
45
  BRANDS = 'brands',
46
+ FORMFACTOR = 'formFactor',
43
47
  FULLVERLIST = 'fullVersionList',
44
48
  PLATFORM = 'platform',
45
49
  PLATFORMVER = 'platformVersion',
@@ -47,12 +51,13 @@
47
51
  CH_HEADER = 'sec-ch-ua',
48
52
  CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list',
49
53
  CH_HEADER_ARCH = CH_HEADER + '-arch',
50
- CH_HEADER_BITNESS = CH_HEADER + '-bitness',
51
- CH_HEADER_MOBILE = CH_HEADER + '-mobile',
52
- CH_HEADER_MODEL = CH_HEADER + '-model',
53
- CH_HEADER_PLATFORM = CH_HEADER + '-platform',
54
+ CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS,
55
+ CH_HEADER_FORM_FACTOR = CH_HEADER + '-form-factor',
56
+ CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE,
57
+ CH_HEADER_MODEL = CH_HEADER + '-' + MODEL,
58
+ CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM,
54
59
  CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version',
55
- CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'],
60
+ CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS],
56
61
  UA_BROWSER = 'browser',
57
62
  UA_CPU = 'cpu',
58
63
  UA_DEVICE = 'device',
@@ -93,18 +98,7 @@
93
98
  // Helper
94
99
  //////////
95
100
 
96
- var assignFromEntries = function (arr) {
97
- for (var i in arr) {
98
- var propName = arr[i];
99
- if (typeof propName == OBJ_TYPE && propName.length == 2) {
100
- this[propName[0]] = propName[1];
101
- } else {
102
- this[propName] = undefined;
103
- }
104
- }
105
- return this;
106
- },
107
- extend = function (regexes, extensions) {
101
+ var extend = function (regexes, extensions) {
108
102
  var mergedRegexes = {};
109
103
  for (var i in regexes) {
110
104
  mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i];
@@ -135,10 +129,14 @@
135
129
  itemListToArray = function (header) {
136
130
  if (!header) return undefined;
137
131
  var arr = [];
138
- var tokens = strip(/\\?\"/g, header).split(', ');
132
+ var tokens = strip(/\\?\"/g, header).split(',');
139
133
  for (var i = 0; i < tokens.length; i++) {
140
- var token = tokens[i].split(';v=');
141
- arr[i] = { brand : token[0], version : token[1] };
134
+ if (tokens[i].indexOf(';') > -1) {
135
+ var token = trim(tokens[i]).split(';v=');
136
+ arr[i] = { brand : token[0], version : token[1] };
137
+ } else {
138
+ arr[i] = tokens[i];
139
+ }
142
140
  }
143
141
  return arr;
144
142
  },
@@ -148,11 +146,22 @@
148
146
  majorize = function (version) {
149
147
  return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined;
150
148
  },
149
+ setProps = function (arr) {
150
+ for (var i in arr) {
151
+ var propName = arr[i];
152
+ if (typeof propName == OBJ_TYPE && propName.length == 2) {
153
+ this[propName[0]] = propName[1];
154
+ } else {
155
+ this[propName] = undefined;
156
+ }
157
+ }
158
+ return this;
159
+ },
151
160
  strip = function (pattern, str) {
152
161
  return str.replace(pattern, EMPTY);
153
162
  },
154
163
  stripQuotes = function (val) {
155
- return typeof val === STR_TYPE ? strip(/\"/g, val) : val;
164
+ return typeof val === STR_TYPE ? strip(/\\?\"/g, val) : val;
156
165
  },
157
166
  trim = function (str, len) {
158
167
  if (typeof(str) === STR_TYPE) {
@@ -234,25 +243,14 @@
234
243
  return (i === UNKNOWN) ? undefined : i;
235
244
  }
236
245
  }
237
- return str;
246
+ return map.hasOwnProperty('*') ? map['*'] : str;
238
247
  };
239
248
 
240
249
  ///////////////
241
250
  // String map
242
251
  //////////////
243
252
 
244
- // Safari < 3.0
245
- var oldSafariMap = {
246
- '1.0' : '/8',
247
- '1.2' : '/1',
248
- '1.3' : '/3',
249
- '2.0' : '/412',
250
- '2.0.2' : '/416',
251
- '2.0.3' : '/417',
252
- '2.0.4' : '/419',
253
- '?' : '/'
254
- },
255
- windowsVersionMap = {
253
+ var windowsVersionMap = {
256
254
  'ME' : '4.90',
257
255
  'NT 3.11' : 'NT3.51',
258
256
  'NT 4.0' : 'NT4.0',
@@ -264,6 +262,16 @@
264
262
  '8.1' : 'NT 6.3',
265
263
  '10' : ['NT 6.4', 'NT 10.0'],
266
264
  'RT' : 'ARM'
265
+ },
266
+
267
+ formFactorMap = {
268
+ 'embedded' : 'Automotive',
269
+ 'mobile' : 'Mobile',
270
+ 'tablet' : ['Tablet', 'EInk'],
271
+ 'smarttv' : 'TV',
272
+ 'wearable' : ['VR', 'XR', 'Watch'],
273
+ '?' : ['Desktop', 'Unknown'],
274
+ '*' : undefined
267
275
  };
268
276
 
269
277
  //////////////
@@ -355,7 +363,7 @@
355
363
  /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp
356
364
  /safari (line)\/([\w\.]+)/i, // Line App for iOS
357
365
  /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
358
- /(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram
366
+ /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat
359
367
  ], [NAME, VERSION], [
360
368
  /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
361
369
  ], [VERSION, [NAME, 'GSA']], [
@@ -384,7 +392,7 @@
384
392
  /version\/([\w\.\,]+) .*(safari)/i // Safari
385
393
  ], [VERSION, NAME], [
386
394
  /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
387
- ], [NAME, [VERSION, strMapper, oldSafariMap]], [
395
+ ], [NAME, [VERSION, '1']], [
388
396
 
389
397
  /(webkit|khtml)\/([\w\.]+)/i
390
398
  ], [NAME, VERSION], [
@@ -425,7 +433,7 @@
425
433
  /((?:i[346]|x)86)[;\)]/i // IA32 (x86)
426
434
  ], [[ARCHITECTURE, 'ia32']], [
427
435
 
428
- /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
436
+ /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
429
437
  ], [[ARCHITECTURE, 'arm64']], [
430
438
 
431
439
  /\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF
@@ -482,12 +490,14 @@
482
490
  ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
483
491
 
484
492
  // Xiaomi
485
- /\b(poco[\w ]+)(?: bui|\))/i, // Xiaomi POCO
493
+ /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
486
494
  /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
487
495
  /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
488
496
  /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
497
+ /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|\))/i, // Xiaomi Redmi 'numeric' models
489
498
  /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi
490
499
  ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
500
+ /oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, // Redmi Pad
491
501
  /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets
492
502
  ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
493
503
 
@@ -502,7 +512,7 @@
502
512
  ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [
503
513
 
504
514
  // Realme
505
- /\b(rmx[12]\d{3})(?: bui|;|\))/i
515
+ /\b(rmx[1-3]\d{3})(?: bui|;|\))/i
506
516
  ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [
507
517
 
508
518
  // Motorola
@@ -588,9 +598,13 @@
588
598
  /droid.+; (m[1-5] note) bui/i,
589
599
  /\bmz-([-\w]{2,})/i
590
600
  ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
601
+
602
+ // Ulefone
603
+ /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i
604
+ ], [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]], [
591
605
 
592
606
  // MIXED
593
- /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i,
607
+ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i,
594
608
  // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
595
609
  /(hp) ([\w ]+\w)/i, // HP iPAQ
596
610
  /(asus)-?(\w+)/i, // Asus
@@ -635,7 +649,7 @@
635
649
  ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
636
650
  /crkey/i // Google Chromecast
637
651
  ], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
638
- /droid.+aft(\w)( bui|\))/i // Fire TV
652
+ /droid.+aft(\w+)( bui|\))/i // Fire TV
639
653
  ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
640
654
  /\(dtv[\);].+(aquos)/i,
641
655
  /(aquos-tv[\w ]+)\)/i // Sharp
@@ -740,7 +754,7 @@
740
754
 
741
755
  // iOS/macOS
742
756
  /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
743
- /ios;fbsv\/([\d\.]+)/i,
757
+ /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
744
758
  /cfnetwork\/.+darwin/i
745
759
  ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
746
760
  /(mac os x) ?([\w\. ]*)/i,
@@ -809,23 +823,23 @@
809
823
 
810
824
  var defaultProps = (function () {
811
825
  var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
812
- assignFromEntries.call(props.init, [
826
+ setProps.call(props.init, [
813
827
  [UA_BROWSER, [NAME, VERSION, MAJOR]],
814
828
  [UA_CPU, [ARCHITECTURE]],
815
829
  [UA_DEVICE, [TYPE, MODEL, VENDOR]],
816
830
  [UA_ENGINE, [NAME, VERSION]],
817
831
  [UA_OS, [NAME, VERSION]]
818
832
  ]);
819
- assignFromEntries.call(props.isIgnore, [
833
+ setProps.call(props.isIgnore, [
820
834
  [UA_BROWSER, [VERSION, MAJOR]],
821
835
  [UA_ENGINE, [VERSION]],
822
836
  [UA_OS, [VERSION]]
823
837
  ]);
824
- assignFromEntries.call(props.isIgnoreRgx, [
838
+ setProps.call(props.isIgnoreRgx, [
825
839
  [UA_BROWSER, / ?browser$/i],
826
840
  [UA_OS, / ?os$/i]
827
841
  ]);
828
- assignFromEntries.call(props.toString, [
842
+ setProps.call(props.toString, [
829
843
  [UA_BROWSER, [NAME, VERSION]],
830
844
  [UA_CPU, [ARCHITECTURE]],
831
845
  [UA_DEVICE, [VENDOR, MODEL]],
@@ -835,34 +849,47 @@
835
849
  return props;
836
850
  })();
837
851
 
838
- var createUAParserData = function (itemType, ua, rgxMap, uaCH) {
852
+ var createIData = function (item, itemType) {
839
853
 
840
854
  var init_props = defaultProps.init[itemType],
841
855
  is_ignoreProps = defaultProps.isIgnore[itemType] || 0,
842
856
  is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0,
843
857
  toString_props = defaultProps.toString[itemType] || 0;
844
858
 
845
- function UAParserData () {
846
- assignFromEntries.call(this, init_props);
859
+ function IData () {
860
+ setProps.call(this, init_props);
847
861
  }
848
- UAParserData.prototype.withClientHints = function () {
849
-
862
+
863
+ IData.prototype.getItem = function () {
864
+ return item;
865
+ };
866
+
867
+ IData.prototype.withClientHints = function () {
868
+
850
869
  // nodejs / non-client-hints browsers
851
870
  if (!NAVIGATOR_UADATA) {
852
- return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get();
871
+ return item
872
+ .parseCH()
873
+ .get();
853
874
  }
854
875
 
855
876
  // browsers based on chromium 85+
856
877
  return NAVIGATOR_UADATA
857
878
  .getHighEntropyValues(CH_ALL_VALUES)
858
879
  .then(function (res) {
859
- var JS_UACH = new UAParserDataCH(res, false);
860
- return new UAParserItem(itemType, ua, rgxMap, JS_UACH).parseCH().get();
880
+ return item
881
+ .setCH(new UACHData(res, false))
882
+ .parseCH()
883
+ .get();
861
884
  });
862
885
  };
863
886
 
887
+ IData.prototype.withFeatureCheck = function () {
888
+ return item.detectFeature().get();
889
+ };
890
+
864
891
  if (itemType != UA_RESULT) {
865
- UAParserData.prototype.is = function (strToCheck) {
892
+ IData.prototype.is = function (strToCheck) {
866
893
  var is = false;
867
894
  for (var i in this) {
868
895
  if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) {
@@ -875,7 +902,7 @@
875
902
  }
876
903
  return is;
877
904
  };
878
- UAParserData.prototype.toString = function () {
905
+ IData.prototype.toString = function () {
879
906
  var str = EMPTY;
880
907
  for (var i in toString_props) {
881
908
  if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
@@ -887,45 +914,45 @@
887
914
  }
888
915
 
889
916
  if (!NAVIGATOR_UADATA) {
890
- UAParserData.prototype.then = function (cb) {
917
+ IData.prototype.then = function (cb) {
891
918
  var that = this;
892
- var UAParserDataResolve = function () {
919
+ var IDataResolve = function () {
893
920
  for (var prop in that) {
894
921
  if (that.hasOwnProperty(prop)) {
895
922
  this[prop] = that[prop];
896
923
  }
897
924
  }
898
925
  };
899
- UAParserDataResolve.prototype = {
900
- is : UAParserData.prototype.is,
901
- toString : UAParserData.prototype.toString
926
+ IDataResolve.prototype = {
927
+ is : IData.prototype.is,
928
+ toString : IData.prototype.toString
902
929
  };
903
- var resolveData = new UAParserDataResolve();
930
+ var resolveData = new IDataResolve();
904
931
  cb(resolveData);
905
932
  return resolveData;
906
933
  };
907
934
  }
908
935
 
909
- return new UAParserData();
936
+ return new IData();
910
937
  };
911
938
 
912
939
  /////////////////
913
940
  // Constructor
914
941
  ////////////////
915
942
 
916
- function UAParserDataCH (uach, isHTTP_UACH) {
943
+ function UACHData (uach, isHttpUACH) {
917
944
  uach = uach || {};
918
- assignFromEntries.call(this, CH_ALL_VALUES);
919
- if (isHTTP_UACH) {
920
- assignFromEntries.call(this, [
945
+ setProps.call(this, CH_ALL_VALUES);
946
+ if (isHttpUACH) {
947
+ setProps.call(this, [
921
948
  [BRANDS, itemListToArray(uach[CH_HEADER])],
922
949
  [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
923
- [BRANDS, itemListToArray(uach[CH_HEADER])],
924
950
  [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
925
951
  [MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
926
952
  [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
927
953
  [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
928
954
  [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
955
+ [FORMFACTOR, itemListToArray(uach[CH_HEADER_FORM_FACTOR])],
929
956
  [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
930
957
  ]);
931
958
  } else {
@@ -933,127 +960,162 @@
933
960
  if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop];
934
961
  }
935
962
  }
936
- return this;
937
963
  }
938
964
 
939
- function UAParserItem (itemType, ua, rgxMap, uaCH) {
940
- assignFromEntries.call(this, [
965
+ function UAItem (itemType, ua, rgxMap, uaCH) {
966
+
967
+ this.get = function (prop) {
968
+ if (!prop) return this.data;
969
+ return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
970
+ };
971
+
972
+ this.set = function (prop, val) {
973
+ this.data[prop] = val;
974
+ return this;
975
+ };
976
+
977
+ this.setCH = function (ch) {
978
+ this.uaCH = ch;
979
+ return this;
980
+ };
981
+
982
+ this.detectFeature = function () {
983
+ if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
984
+ switch (this.itemType) {
985
+ case UA_BROWSER:
986
+ // Brave-specific detection
987
+ if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
988
+ this.set(NAME, 'Brave');
989
+ }
990
+ break;
991
+ case UA_DEVICE:
992
+ // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
993
+ if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
994
+ this.set(TYPE, MOBILE);
995
+ }
996
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
997
+ if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
998
+ this.set(MODEL, 'iPad')
999
+ .set(TYPE, TABLET);
1000
+ }
1001
+ break;
1002
+ case UA_OS:
1003
+ // Chrome-specific detection: check for 'platform' value of navigator.userAgentData
1004
+ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
1005
+ this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
1006
+ }
1007
+ break;
1008
+ case UA_RESULT:
1009
+ var data = this.data;
1010
+ var detect = function (itemType) {
1011
+ return data[itemType]
1012
+ .getItem()
1013
+ .detectFeature()
1014
+ .get();
1015
+ };
1016
+ this.set(UA_BROWSER, detect(UA_BROWSER))
1017
+ .set(UA_CPU, detect(UA_CPU))
1018
+ .set(UA_DEVICE, detect(UA_DEVICE))
1019
+ .set(UA_ENGINE, detect(UA_ENGINE))
1020
+ .set(UA_OS, detect(UA_OS));
1021
+ }
1022
+ }
1023
+ return this;
1024
+ };
1025
+
1026
+ this.parseUA = function () {
1027
+ if (this.itemType != UA_RESULT) {
1028
+ rgxMapper.call(this.data, this.ua, this.rgxMap);
1029
+ }
1030
+ if (this.itemType == UA_BROWSER) {
1031
+ this.set(MAJOR, majorize(this.get(VERSION)));
1032
+ }
1033
+ return this;
1034
+ };
1035
+
1036
+ this.parseCH = function () {
1037
+ var uaCH = this.uaCH,
1038
+ rgxMap = this.rgxMap;
1039
+
1040
+ switch (this.itemType) {
1041
+ case UA_BROWSER:
1042
+ var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
1043
+ if (brands) {
1044
+ for (var i in brands) {
1045
+ var brandName = brands[i].brand,
1046
+ brandVersion = brands[i].version;
1047
+ if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
1048
+ this.set(NAME, strip(GOOGLE+' ', brandName))
1049
+ .set(VERSION, brandVersion)
1050
+ .set(MAJOR, majorize(brandVersion));
1051
+ }
1052
+ }
1053
+ }
1054
+ break;
1055
+ case UA_CPU:
1056
+ var archName = uaCH[ARCHITECTURE];
1057
+ if (archName) {
1058
+ if (archName && uaCH[BITNESS] == '64') archName += '64';
1059
+ rgxMapper.call(this.data, archName + ';', rgxMap);
1060
+ }
1061
+ break;
1062
+ case UA_DEVICE:
1063
+ if (uaCH[MOBILE]) {
1064
+ this.set(TYPE, MOBILE);
1065
+ }
1066
+ if (uaCH[MODEL]) {
1067
+ this.set(MODEL, uaCH[MODEL]);
1068
+ }
1069
+ if (uaCH[FORMFACTOR]) {
1070
+ var ff;
1071
+ if (typeof uaCH[FORMFACTOR] !== 'string') {
1072
+ var idx = 0;
1073
+ while (!ff && idx < uaCH[FORMFACTOR].length) {
1074
+ ff = strMapper(uaCH[FORMFACTOR][idx++], formFactorMap);
1075
+ }
1076
+ } else {
1077
+ ff = strMapper(uaCH[FORMFACTOR], formFactorMap);
1078
+ }
1079
+ this.set(TYPE, ff);
1080
+ }
1081
+ break;
1082
+ case UA_OS:
1083
+ var osName = uaCH[PLATFORM];
1084
+ if(osName) {
1085
+ var osVersion = uaCH[PLATFORMVER];
1086
+ if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
1087
+ this.set(NAME, osName)
1088
+ .set(VERSION, osVersion);
1089
+ }
1090
+ break;
1091
+ case UA_RESULT:
1092
+ var data = this.data;
1093
+ var parse = function (itemType) {
1094
+ return data[itemType]
1095
+ .getItem()
1096
+ .setCH(uaCH)
1097
+ .parseCH()
1098
+ .get();
1099
+ };
1100
+ this.set(UA_BROWSER, parse(UA_BROWSER))
1101
+ .set(UA_CPU, parse(UA_CPU))
1102
+ .set(UA_DEVICE, parse(UA_DEVICE))
1103
+ .set(UA_ENGINE, parse(UA_ENGINE))
1104
+ .set(UA_OS, parse(UA_OS));
1105
+ }
1106
+ return this;
1107
+ };
1108
+
1109
+ setProps.call(this, [
941
1110
  ['itemType', itemType],
942
1111
  ['ua', ua],
943
1112
  ['uaCH', uaCH],
944
1113
  ['rgxMap', rgxMap],
945
- ['data', createUAParserData(itemType, ua, rgxMap, uaCH)]
1114
+ ['data', createIData(this, itemType)]
946
1115
  ]);
947
- this.parse();
948
- var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == ua;
949
- switch(this.itemType) {
950
- case UA_BROWSER:
951
- // Brave-specific detection
952
- if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
953
- this.set(NAME, 'Brave');
954
- }
955
- this.set(MAJOR, majorize(this.get(VERSION)));
956
- break;
957
- case UA_DEVICE:
958
- if (isSelfNav && !this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
959
- this.set(TYPE, MOBILE);
960
- }
961
- // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
962
- if (isSelfNav && this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
963
- this.set(MODEL, 'iPad')
964
- .set(TYPE, TABLET);
965
- }
966
- break;
967
- case UA_OS:
968
- if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
969
- this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
970
- }
971
- break;
972
- case UA_RESULT:
973
- var createUAParserItem = function (itemType) {
974
- return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get();
975
- };
976
- this.set('ua', ua)
977
- .set(UA_BROWSER, createUAParserItem(UA_BROWSER))
978
- .set(UA_CPU, createUAParserItem(UA_CPU))
979
- .set(UA_DEVICE, createUAParserItem(UA_DEVICE))
980
- .set(UA_ENGINE, createUAParserItem(UA_ENGINE))
981
- .set(UA_OS, createUAParserItem(UA_OS))
982
- .get();
983
- }
1116
+
984
1117
  return this;
985
1118
  }
986
- UAParserItem.prototype.get = function (prop) {
987
- if (!prop) return this.data;
988
- return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
989
- };
990
- UAParserItem.prototype.parse = function () {
991
- if (this.itemType != UA_RESULT) {
992
- rgxMapper.call(this.data, this.ua, this.rgxMap);
993
- }
994
- return this;
995
- };
996
- UAParserItem.prototype.parseCH = function () {
997
- var ua = this.ua,
998
- uaCH = this.uaCH,
999
- rgxMap = this.rgxMap;
1000
-
1001
- switch (this.itemType) {
1002
- case UA_BROWSER:
1003
- var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
1004
- if (brands) {
1005
- for (var i in brands) {
1006
- var brandName = brands[i].brand,
1007
- brandVersion = brands[i].version;
1008
- if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
1009
- this.set(NAME, strip(GOOGLE+' ', brandName))
1010
- .set(VERSION, brandVersion)
1011
- .set(MAJOR, majorize(brandVersion));
1012
- }
1013
- }
1014
- }
1015
- break;
1016
- case UA_CPU:
1017
- var archName = uaCH[ARCHITECTURE];
1018
- if (archName) {
1019
- if (archName && uaCH[BITNESS] == '64') archName += '64';
1020
- rgxMapper.call(this.data, archName + ';', rgxMap);
1021
- }
1022
- break;
1023
- case UA_DEVICE:
1024
- if (uaCH[MOBILE]) {
1025
- this.set(TYPE, MOBILE);
1026
- }
1027
- if (uaCH[MODEL]) {
1028
- this.set(MODEL, uaCH[MODEL]);
1029
- }
1030
- break;
1031
- case UA_OS:
1032
- var osName = uaCH[PLATFORM];
1033
- if(osName) {
1034
- var osVersion = uaCH[PLATFORMVER];
1035
- if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
1036
- this.set(NAME, osName)
1037
- .set(VERSION, osVersion);
1038
- }
1039
- break;
1040
- case UA_RESULT:
1041
- var createUAParserItemWithCH = function (itemType) {
1042
- return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get();
1043
- };
1044
- this.set('ua', ua)
1045
- .set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER))
1046
- .set(UA_CPU, createUAParserItemWithCH(UA_CPU))
1047
- .set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE))
1048
- .set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE))
1049
- .set(UA_OS, createUAParserItemWithCH(UA_OS));
1050
- }
1051
- return this;
1052
- };
1053
- UAParserItem.prototype.set = function (prop, val) {
1054
- this.data[prop] = val;
1055
- return this;
1056
- };
1057
1119
 
1058
1120
  function UAParser (ua, extensions, headers) {
1059
1121
 
@@ -1077,39 +1139,54 @@
1077
1139
  return new UAParser(ua, extensions, headers).getResult();
1078
1140
  }
1079
1141
 
1080
- var userAgent = ua ||
1081
- ((NAVIGATOR && NAVIGATOR.userAgent) ?
1082
- NAVIGATOR.userAgent :
1083
- (headers && headers[USER_AGENT] ?
1084
- headers[USER_AGENT] :
1085
- EMPTY)),
1086
-
1087
- HTTP_UACH = new UAParserDataCH(headers, true),
1142
+ var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string
1143
+ ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent
1144
+ (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers
1145
+ EMPTY)), // empty string
1088
1146
 
1147
+ httpUACH = new UACHData(headers, true),
1089
1148
  regexMap = extensions ?
1090
1149
  extend(defaultRegexes, extensions) :
1091
1150
  defaultRegexes,
1092
1151
 
1093
- createUAParserItemFunc = function (itemType) {
1094
- return function () {
1095
- return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).get();
1096
- };
1152
+ createItemFunc = function (itemType) {
1153
+ if (itemType == UA_RESULT) {
1154
+ return function () {
1155
+ return new UAItem(itemType, userAgent, regexMap, httpUACH)
1156
+ .set('ua', userAgent)
1157
+ .set(UA_BROWSER, this.getBrowser())
1158
+ .set(UA_CPU, this.getCPU())
1159
+ .set(UA_DEVICE, this.getDevice())
1160
+ .set(UA_ENGINE, this.getEngine())
1161
+ .set(UA_OS, this.getOS())
1162
+ .get();
1163
+ };
1164
+ } else {
1165
+ return function () {
1166
+ return new UAItem(itemType, userAgent, regexMap[itemType], httpUACH)
1167
+ .parseUA()
1168
+ .get();
1169
+ };
1170
+ }
1097
1171
  };
1098
-
1172
+
1099
1173
  // public methods
1100
- assignFromEntries.call(this, [
1101
- ['getBrowser', createUAParserItemFunc(UA_BROWSER)],
1102
- ['getCPU', createUAParserItemFunc(UA_CPU)],
1103
- ['getDevice', createUAParserItemFunc(UA_DEVICE)],
1104
- ['getEngine', createUAParserItemFunc(UA_ENGINE)],
1105
- ['getOS', createUAParserItemFunc(UA_OS)],
1106
- ['getResult', createUAParserItemFunc(UA_RESULT)],
1174
+ setProps.call(this, [
1175
+ ['getBrowser', createItemFunc(UA_BROWSER)],
1176
+ ['getCPU', createItemFunc(UA_CPU)],
1177
+ ['getDevice', createItemFunc(UA_DEVICE)],
1178
+ ['getEngine', createItemFunc(UA_ENGINE)],
1179
+ ['getOS', createItemFunc(UA_OS)],
1180
+ ['getResult', createItemFunc(UA_RESULT)],
1107
1181
  ['getUA', function () { return userAgent; }],
1108
1182
  ['setUA', function (ua) {
1109
- userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
1183
+ if (typeof ua === STR_TYPE)
1184
+ userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
1110
1185
  return this;
1111
1186
  }]
1112
- ]).setUA(userAgent);
1187
+ ])
1188
+ .setUA(userAgent);
1189
+
1113
1190
  return this;
1114
1191
  }
1115
1192