ua-parser-js 2.0.0-alpha.2 → 2.0.0-beta.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.
@@ -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