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

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,106 @@
1
+ // Type definitions for UAParser.js v2.0.0-beta.2
2
+ // Project: https://github.com/faisalman/ua-parser-js
3
+ // Definitions by: Faisal Salman <https://github.com/faisalman>
4
+
5
+ declare namespace UAParser {
6
+
7
+ interface IData<T> {
8
+ is(val: string): boolean;
9
+ toString(): string;
10
+ withClientHints(): PromiseLike<T> | T;
11
+ withFeatureCheck(): T;
12
+ }
13
+
14
+ interface IBrowser extends IData<IBrowser> {
15
+ name?: string;
16
+ version?: string;
17
+ major?: string;
18
+ }
19
+
20
+ interface ICPU extends IData<ICPU> {
21
+ architecture?: 'ia32' | 'ia64' | 'amd64' | 'arm' | 'arm64' | 'armhf' | 'avr' | 'irix' | 'irix64' | 'mips' | 'mips64' | '68k' | 'ppc' | 'sparc' | 'sparc64';
22
+ }
23
+
24
+ interface IDevice extends IData<IDevice> {
25
+ type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable';
26
+ vendor?: string;
27
+ model?: string;
28
+ }
29
+
30
+ interface IEngine extends IData<IEngine> {
31
+ name?: 'Amaya' | 'Blink' | 'EdgeHTML' | 'Flow' | 'Gecko' | 'Goanna' | 'iCab' | 'KHTML' | 'LibWeb' | 'Links' | 'Lynx' | 'NetFront' | 'NetSurf' | 'Presto' | 'Tasman' | 'Trident' | 'w3m' | 'WebKit';
32
+ version?: string;
33
+ }
34
+
35
+ interface IOS extends IData<IOS> {
36
+ name?: string;
37
+ version?: string;
38
+ }
39
+
40
+ interface IResult extends IData<IResult> {
41
+ ua: string;
42
+ browser: IBrowser;
43
+ cpu: ICPU;
44
+ device: IDevice;
45
+ engine: IEngine;
46
+ os: IOS;
47
+ }
48
+
49
+ type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[];
50
+ type UAParserProps = 'browser' | 'cpu' | 'device' | 'engine' | 'os';
51
+ type UAParserExt = Partial<Record<UAParserProps, RegexMap>>;
52
+
53
+ export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: Record<string, string>): IResult;
54
+ export function UAParser(uastring?: string, headers?: Record<string, string>): IResult;
55
+ export function UAParser(extensions?: UAParserExt, headers?: Record<string, string>): IResult;
56
+ export function UAParser(headers?: Record<string, string>): IResult;
57
+
58
+ export class UAParser {
59
+
60
+ static readonly BROWSER: {
61
+ NAME: 'name';
62
+ VERSION: 'version';
63
+ MAJOR: 'major';
64
+ };
65
+ static readonly CPU: {
66
+ ARCHITECTURE: 'architecture';
67
+ };
68
+ static readonly DEVICE: {
69
+ TYPE: 'type';
70
+ VENDOR: 'vendor';
71
+ MODEL: 'model';
72
+ CONSOLE: 'console';
73
+ MOBILE: 'mobile';
74
+ SMARTTV: 'smarttv';
75
+ TABLET: 'tablet';
76
+ WEARABLE: 'wearable';
77
+ EMBEDDED: 'embedded';
78
+ };
79
+ static readonly ENGINE: {
80
+ NAME: 'name';
81
+ VERSION: 'version';
82
+ };
83
+ static readonly OS: {
84
+ NAME: 'name';
85
+ VERSION: 'version';
86
+ };
87
+ static readonly VERSION: string;
88
+
89
+ constructor(uastring?: string, extensions?: UAParserExt, headers?: Record<string, string>);
90
+ constructor(uastring?: string, headers?: Record<string, string>);
91
+ constructor(extensions?: UAParserExt, headers?: Record<string, string>);
92
+ constructor(headers?: Record<string, string>);
93
+
94
+ getUA(): string;
95
+ getBrowser(): IBrowser;
96
+ getCPU(): ICPU;
97
+ getDevice(): IDevice;
98
+ getEngine(): IEngine;
99
+ getOS(): IOS;
100
+ getResult(): IResult;
101
+ setUA(uastring: string): UAParser;
102
+ }
103
+ }
104
+
105
+ export as namespace UAParser;
106
+ export = UAParser;
@@ -1,7 +1,7 @@
1
1
  /////////////////////////////////////////////////////////////////////////////////
2
- /* UAParser.js v2.0.0-alpha.3
2
+ /* UAParser.js v2.0.0-beta.2
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
@@ -19,8 +19,7 @@
19
19
  // Constants
20
20
  /////////////
21
21
 
22
-
23
- var LIBVERSION = '2.0.0-alpha.3',
22
+ var LIBVERSION = '2.0.0-beta.2',
24
23
  EMPTY = '',
25
24
  UNKNOWN = '?',
26
25
  FUNC_TYPE = 'function',
@@ -41,8 +40,9 @@
41
40
  WEARABLE = 'wearable',
42
41
  EMBEDDED = 'embedded',
43
42
  USER_AGENT = 'user-agent',
44
- UA_MAX_LENGTH = 350,
43
+ UA_MAX_LENGTH = 500,
45
44
  BRANDS = 'brands',
45
+ FORMFACTOR = 'formFactor',
46
46
  FULLVERLIST = 'fullVersionList',
47
47
  PLATFORM = 'platform',
48
48
  PLATFORMVER = 'platformVersion',
@@ -51,11 +51,12 @@
51
51
  CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list',
52
52
  CH_HEADER_ARCH = CH_HEADER + '-arch',
53
53
  CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS,
54
+ CH_HEADER_FORM_FACTOR = CH_HEADER + '-form-factor',
54
55
  CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE,
55
56
  CH_HEADER_MODEL = CH_HEADER + '-' + MODEL,
56
57
  CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM,
57
58
  CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version',
58
- CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, BITNESS],
59
+ CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS],
59
60
  UA_BROWSER = 'browser',
60
61
  UA_CPU = 'cpu',
61
62
  UA_DEVICE = 'device',
@@ -68,6 +69,7 @@
68
69
  BLACKBERRY = 'BlackBerry',
69
70
  GOOGLE = 'Google',
70
71
  HUAWEI = 'Huawei',
72
+ LENOVO = 'Lenovo',
71
73
  LG = 'LG',
72
74
  MICROSOFT = 'Microsoft',
73
75
  MOTOROLA = 'Motorola',
@@ -83,9 +85,11 @@
83
85
  FIREFOX = 'Firefox',
84
86
  OPERA = 'Opera',
85
87
  FACEBOOK = 'Facebook',
88
+ SOGOU = 'Sogou',
86
89
  WINDOWS = 'Windows';
87
90
 
88
- var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ?
91
+ var isWindow = typeof window !== UNDEF_TYPE,
92
+ NAVIGATOR = (isWindow && window.navigator) ?
89
93
  window.navigator :
90
94
  undefined,
91
95
  NAVIGATOR_UADATA = (NAVIGATOR && NAVIGATOR.userAgentData) ?
@@ -117,28 +121,35 @@
117
121
  }
118
122
  return false;
119
123
  }
120
- return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
124
+ return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
121
125
  },
122
126
  isExtensions = function (obj) {
123
127
  for (var prop in obj) {
124
128
  return /^(browser|cpu|device|engine|os)$/.test(prop);
125
129
  }
126
130
  },
131
+ isString = function (val) {
132
+ return typeof val === STR_TYPE;
133
+ },
127
134
  itemListToArray = function (header) {
128
135
  if (!header) return undefined;
129
136
  var arr = [];
130
- var tokens = strip(/\\?\"/g, header).split(', ');
137
+ var tokens = strip(/\\?\"/g, header).split(',');
131
138
  for (var i = 0; i < tokens.length; i++) {
132
- var token = tokens[i].split(';v=');
133
- arr[i] = { brand : token[0], version : token[1] };
139
+ if (tokens[i].indexOf(';') > -1) {
140
+ var token = trim(tokens[i]).split(';v=');
141
+ arr[i] = { brand : token[0], version : token[1] };
142
+ } else {
143
+ arr[i] = trim(tokens[i]);
144
+ }
134
145
  }
135
146
  return arr;
136
147
  },
137
148
  lowerize = function (str) {
138
- return typeof(str) === STR_TYPE ? str.toLowerCase() : str;
149
+ return isString(str) ? str.toLowerCase() : str;
139
150
  },
140
151
  majorize = function (version) {
141
- return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined;
152
+ return isString(version) ? strip(/[^\d\.]/g, version).split('.')[0] : undefined;
142
153
  },
143
154
  setProps = function (arr) {
144
155
  for (var i in arr) {
@@ -152,15 +163,15 @@
152
163
  return this;
153
164
  },
154
165
  strip = function (pattern, str) {
155
- return str.replace(pattern, EMPTY);
166
+ return isString(str) ? str.replace(pattern, EMPTY) : str;
156
167
  },
157
- stripQuotes = function (val) {
158
- return typeof val === STR_TYPE ? strip(/\"/g, val) : val;
168
+ stripQuotes = function (str) {
169
+ return strip(/\\?\"/g, str);
159
170
  },
160
171
  trim = function (str, len) {
161
- if (typeof(str) === STR_TYPE) {
172
+ if (isString(str)) {
162
173
  str = strip(/^\s\s*/, str);
163
- return typeof(len) === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
174
+ return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
164
175
  }
165
176
  };
166
177
 
@@ -237,7 +248,7 @@
237
248
  return (i === UNKNOWN) ? undefined : i;
238
249
  }
239
250
  }
240
- return str;
251
+ return map.hasOwnProperty('*') ? map['*'] : str;
241
252
  };
242
253
 
243
254
  ///////////////
@@ -256,6 +267,16 @@
256
267
  '8.1' : 'NT 6.3',
257
268
  '10' : ['NT 6.4', 'NT 10.0'],
258
269
  'RT' : 'ARM'
270
+ },
271
+
272
+ formFactorMap = {
273
+ 'embedded' : 'Automotive',
274
+ 'mobile' : 'Mobile',
275
+ 'tablet' : ['Tablet', 'EInk'],
276
+ 'smarttv' : 'TV',
277
+ 'wearable' : ['VR', 'XR', 'Watch'],
278
+ '?' : ['Desktop', 'Unknown'],
279
+ '*' : undefined
259
280
  };
260
281
 
261
282
  //////////////
@@ -279,15 +300,18 @@
279
300
  ], [NAME, VERSION], [
280
301
  /opios[\/ ]+([\w\.]+)/i // Opera mini on iphone >= 8.0
281
302
  ], [VERSION, [NAME, OPERA+' Mini']], [
303
+ /\bop(?:rg)?x\/([\w\.]+)/i // Opera GX
304
+ ], [VERSION, [NAME, OPERA+' GX']], [
282
305
  /\bopr\/([\w\.]+)/i // Opera Webkit
283
306
  ], [VERSION, [NAME, OPERA]], [
284
307
 
285
308
  // Mixed
309
+ /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu
310
+ ], [VERSION, [NAME, 'Baidu']], [
286
311
  /(kindle)\/([\w\.]+)/i, // Kindle
287
312
  /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer
288
313
  // Trident based
289
- /(avant |iemobile|slim)(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser
290
- /(ba?idubrowser)[\/ ]?([\w\.]+)/i, // Baidu Browser
314
+ /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser
291
315
  /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer
292
316
 
293
317
  // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon
@@ -299,8 +323,7 @@
299
323
  /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser
300
324
  ], [VERSION, [NAME, 'UCBrowser']], [
301
325
  /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser
302
- /\bqbcore\/([\w\.]+).+microm/i
303
- ], [VERSION, [NAME, 'WeChat(Win) Desktop']], [
326
+ /\bqbcore\/([\w\.]+).+microm/i,
304
327
  /micromessenger\/([\w\.]+)/i // WeChat
305
328
  ], [VERSION, [NAME, 'WeChat']], [
306
329
  /konqueror\/([\w\.]+)/i // Konqueror
@@ -309,6 +332,8 @@
309
332
  ], [VERSION, [NAME, 'IE']], [
310
333
  /ya(?:search)?browser\/([\w\.]+)/i // Yandex
311
334
  ], [VERSION, [NAME, 'Yandex']], [
335
+ /slbrowser\/([\w\.]+)/i // Smart Lenovo Browser
336
+ ], [VERSION, [NAME, 'Smart ' + LENOVO + SUFFIX_BROWSER]], [
312
337
  /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser
313
338
  ], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [
314
339
  /\bfocus\/([\w\.]+)/i // Firefox Focus
@@ -327,15 +352,20 @@
327
352
  ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [
328
353
  /\bqihu|(qi?ho?o?|360)browser/i // 360
329
354
  ], [[NAME, '360' + SUFFIX_BROWSER]], [
330
- /(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i
331
- ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Samsung/Sailfish/Huawei Browser
355
+ /(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i
356
+ ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser
357
+ /samsungbrowser\/([\w\.]+)/i // Samsung Internet
358
+ ], [VERSION, [NAME, SAMSUNG + ' Internet']], [
332
359
  /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
333
360
  ], [[NAME, /_/g, ' '], VERSION], [
361
+ /metasr[\/ ]?([\d\.]+)/i // Sogou Explorer
362
+ ], [VERSION, [NAME, SOGOU + ' Explorer']], [
363
+ /(sogou)mo\w+\/([\d\.]+)/i // Sogou Mobile
364
+ ], [[NAME, SOGOU + ' Mobile'], VERSION], [
334
365
  /(electron)\/([\w\.]+) safari/i, // Electron-based App
335
366
  /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla
336
- /m?(qqbrowser|baiduboxapp|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/Baidu App/2345 Browser
367
+ /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser
337
368
  ], [NAME, VERSION], [
338
- /(metasr)[\/ ]?([\w\.]+)/i, // SouGouBrowser
339
369
  /(lbbrowser)/i, // LieBao Browser
340
370
  /\[(linkedin)app\]/i // LinkedIn App for iOS & Android
341
371
  ], [NAME], [
@@ -343,10 +373,12 @@
343
373
  // WebView
344
374
  /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android
345
375
  ], [[NAME, FACEBOOK], VERSION], [
376
+ /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android
346
377
  /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App
347
378
  /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp
348
379
  /safari (line)\/([\w\.]+)/i, // Line App for iOS
349
380
  /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
381
+ /(alipay)client\/([\w\.]+)/i, // Alipay
350
382
  /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat
351
383
  ], [NAME, VERSION], [
352
384
  /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
@@ -478,8 +510,10 @@
478
510
  /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
479
511
  /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
480
512
  /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
513
+ /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models
481
514
  /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi
482
515
  ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
516
+ /oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, // Redmi Pad
483
517
  /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets
484
518
  ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
485
519
 
@@ -494,7 +528,7 @@
494
528
  ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [
495
529
 
496
530
  // Realme
497
- /\b(rmx[12]\d{3})(?: bui|;|\))/i
531
+ /\b(rmx[1-3]\d{3})(?: bui|;|\))/i
498
532
  ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [
499
533
 
500
534
  // Motorola
@@ -516,7 +550,7 @@
516
550
  // Lenovo
517
551
  /(ideatab[-\w ]+)/i,
518
552
  /lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i
519
- ], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [
553
+ ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [
520
554
 
521
555
  // Nokia
522
556
  /(?:maemo|nokia).*(n900|lumia \d+)/i,
@@ -580,6 +614,10 @@
580
614
  /droid.+; (m[1-5] note) bui/i,
581
615
  /\bmz-([-\w]{2,})/i
582
616
  ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
617
+
618
+ // Ulefone
619
+ /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i
620
+ ], [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]], [
583
621
 
584
622
  // MIXED
585
623
  /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i,
@@ -686,7 +724,7 @@
686
724
  // MIXED (GENERIC)
687
725
  ///////////////////
688
726
 
689
- /droid .+?; ([^;]+?)(?: bui|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors
727
+ /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors
690
728
  ], [MODEL, [TYPE, MOBILE]], [
691
729
  /droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors
692
730
  ], [MODEL, [TYPE, TABLET]], [
@@ -723,12 +761,12 @@
723
761
  // Windows
724
762
  /microsoft (windows) (vista|xp)/i // Windows (iTunes)
725
763
  ], [NAME, VERSION], [
726
- /(windows) nt 6\.2; (arm)/i, // Windows RT
727
- /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i, // Windows Phone
728
- /(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i
764
+ /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i // Windows Phone
729
765
  ], [NAME, [VERSION, strMapper, windowsVersionMap]], [
730
- /(win(?=3|9|n)|win 9x )([nt\d\.]+)/i
731
- ], [[NAME, WINDOWS], [VERSION, strMapper, windowsVersionMap]], [
766
+ /windows nt 6\.2; (arm)/i, // Windows RT
767
+ /windows[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i,
768
+ /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i
769
+ ], [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]], [
732
770
 
733
771
  // iOS/macOS
734
772
  /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
@@ -930,6 +968,7 @@
930
968
  [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
931
969
  [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
932
970
  [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
971
+ [FORMFACTOR, itemListToArray(uach[CH_HEADER_FORM_FACTOR])],
933
972
  [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
934
973
  ]);
935
974
  } else {
@@ -1011,21 +1050,21 @@
1011
1050
  };
1012
1051
 
1013
1052
  this.parseCH = function () {
1014
- var ua = this.ua,
1015
- uaCH = this.uaCH,
1053
+ var uaCH = this.uaCH,
1016
1054
  rgxMap = this.rgxMap;
1017
1055
 
1018
1056
  switch (this.itemType) {
1019
1057
  case UA_BROWSER:
1020
- var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
1058
+ var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
1021
1059
  if (brands) {
1022
1060
  for (var i in brands) {
1023
- var brandName = brands[i].brand,
1061
+ var brandName = strip(/(Google|Microsoft) /, brands[i].brand || brands[i]),
1024
1062
  brandVersion = brands[i].version;
1025
- if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
1026
- this.set(NAME, strip(GOOGLE+' ', brandName))
1063
+ if (!/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && !/chromi/i.test(brandName)))) {
1064
+ this.set(NAME, brandName)
1027
1065
  .set(VERSION, brandVersion)
1028
1066
  .set(MAJOR, majorize(brandVersion));
1067
+ prevName = brandName;
1029
1068
  }
1030
1069
  }
1031
1070
  }
@@ -1044,6 +1083,23 @@
1044
1083
  if (uaCH[MODEL]) {
1045
1084
  this.set(MODEL, uaCH[MODEL]);
1046
1085
  }
1086
+ // Xbox-Specific Detection
1087
+ if (uaCH[MODEL] == 'Xbox') {
1088
+ this.set(TYPE, CONSOLE)
1089
+ .set(VENDOR, MICROSOFT);
1090
+ }
1091
+ if (uaCH[FORMFACTOR]) {
1092
+ var ff;
1093
+ if (typeof uaCH[FORMFACTOR] !== 'string') {
1094
+ var idx = 0;
1095
+ while (!ff && idx < uaCH[FORMFACTOR].length) {
1096
+ ff = strMapper(uaCH[FORMFACTOR][idx++], formFactorMap);
1097
+ }
1098
+ } else {
1099
+ ff = strMapper(uaCH[FORMFACTOR], formFactorMap);
1100
+ }
1101
+ this.set(TYPE, ff);
1102
+ }
1047
1103
  break;
1048
1104
  case UA_OS:
1049
1105
  var osName = uaCH[PLATFORM];
@@ -1053,6 +1109,11 @@
1053
1109
  this.set(NAME, osName)
1054
1110
  .set(VERSION, osVersion);
1055
1111
  }
1112
+ // Xbox-Specific Detection
1113
+ if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') {
1114
+ this.set(NAME, 'Xbox')
1115
+ .set(VERSION, undefined);
1116
+ }
1056
1117
  break;
1057
1118
  case UA_RESULT:
1058
1119
  var data = this.data;
@@ -1146,7 +1207,7 @@
1146
1207
  ['getResult', createItemFunc(UA_RESULT)],
1147
1208
  ['getUA', function () { return userAgent; }],
1148
1209
  ['setUA', function (ua) {
1149
- if (typeof ua === STR_TYPE)
1210
+ if (isString(ua))
1150
1211
  userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
1151
1212
  return this;
1152
1213
  }]
@@ -1167,7 +1228,7 @@
1167
1228
  //////////
1168
1229
 
1169
1230
  // check js environment
1170
- if (typeof(exports) !== UNDEF_TYPE) {
1231
+ if (typeof exports !== UNDEF_TYPE) {
1171
1232
  // nodejs env
1172
1233
  if (typeof module !== UNDEF_TYPE && module.exports) {
1173
1234
  exports = module.exports = UAParser;
@@ -1175,11 +1236,11 @@
1175
1236
  exports.UAParser = UAParser;
1176
1237
  } else {
1177
1238
  // requirejs env (optional)
1178
- if (typeof(define) === FUNC_TYPE && define.amd) {
1239
+ if (typeof define === FUNC_TYPE && define.amd) {
1179
1240
  define(function () {
1180
1241
  return UAParser;
1181
1242
  });
1182
- } else if (typeof window !== UNDEF_TYPE) {
1243
+ } else if (isWindow) {
1183
1244
  // browser env
1184
1245
  window.UAParser = UAParser;
1185
1246
  }
@@ -1190,7 +1251,7 @@
1190
1251
  // In AMD env the global scope should be kept clean, but jQuery is an exception.
1191
1252
  // jQuery always exports to global scope, unless jQuery.noConflict(true) is used,
1192
1253
  // and we should catch that.
1193
- var $ = typeof window !== UNDEF_TYPE && (window.jQuery || window.Zepto);
1254
+ var $ = isWindow && (window.jQuery || window.Zepto);
1194
1255
  if ($ && !$.ua) {
1195
1256
  var parser = new UAParser();
1196
1257
  $.ua = parser.getResult();