ua-parser-js 2.0.3 → 2.0.6

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.
@@ -3,7 +3,7 @@
3
3
  // Source: /src/main/ua-parser.js
4
4
 
5
5
  /////////////////////////////////////////////////////////////////////////////////
6
- /* UAParser.js v2.0.3
6
+ /* UAParser.js v2.0.6
7
7
  Copyright © 2012-2025 Faisal Salman <f@faisalman.com>
8
8
  AGPLv3 License *//*
9
9
  Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
@@ -21,26 +21,26 @@
21
21
  // Constants
22
22
  /////////////
23
23
 
24
- var LIBVERSION = '2.0.3',
24
+ var LIBVERSION = '2.0.6',
25
25
  UA_MAX_LENGTH = 500,
26
26
  USER_AGENT = 'user-agent',
27
27
  EMPTY = '',
28
28
  UNKNOWN = '?',
29
-
30
- // typeof
31
- FUNC_TYPE = 'function',
32
- UNDEF_TYPE = 'undefined',
33
- OBJ_TYPE = 'object',
34
- STR_TYPE = 'string',
29
+ TYPEOF = {
30
+ FUNCTION : 'function',
31
+ OBJECT : 'object',
32
+ STRING : 'string',
33
+ UNDEFINED : 'undefined'
34
+ },
35
35
 
36
36
  // properties
37
- UA_BROWSER = 'browser',
38
- UA_CPU = 'cpu',
39
- UA_DEVICE = 'device',
40
- UA_ENGINE = 'engine',
41
- UA_OS = 'os',
42
- UA_RESULT = 'result',
43
-
37
+ BROWSER = 'browser',
38
+ CPU = 'cpu',
39
+ DEVICE = 'device',
40
+ ENGINE = 'engine',
41
+ OS = 'os',
42
+ RESULT = 'result',
43
+
44
44
  NAME = 'name',
45
45
  TYPE = 'type',
46
46
  VENDOR = 'vendor',
@@ -68,16 +68,16 @@
68
68
  PLATFORM = 'platform',
69
69
  PLATFORMVER = 'platformVersion',
70
70
  BITNESS = 'bitness',
71
- CH_HEADER = 'sec-ch-ua',
72
- CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list',
73
- CH_HEADER_ARCH = CH_HEADER + '-arch',
74
- CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS,
75
- CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors',
76
- CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE,
77
- CH_HEADER_MODEL = CH_HEADER + '-' + MODEL,
78
- CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM,
79
- CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version',
80
- CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS],
71
+ CH = 'sec-ch-ua',
72
+ CH_FULL_VER_LIST= CH + '-full-version-list',
73
+ CH_ARCH = CH + '-arch',
74
+ CH_BITNESS = CH + '-' + BITNESS,
75
+ CH_FORM_FACTORS = CH + '-form-factors',
76
+ CH_MOBILE = CH + '-' + MOBILE,
77
+ CH_MODEL = CH + '-' + MODEL,
78
+ CH_PLATFORM = CH + '-' + PLATFORM,
79
+ CH_PLATFORM_VER = CH_PLATFORM + '-version',
80
+ CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS],
81
81
 
82
82
  // device vendors
83
83
  AMAZON = 'Amazon',
@@ -116,7 +116,7 @@
116
116
  // os
117
117
  WINDOWS = 'Windows';
118
118
 
119
- var isWindow = typeof window !== UNDEF_TYPE,
119
+ var isWindow = typeof window !== TYPEOF.UNDEFINED,
120
120
  NAVIGATOR = (isWindow && window.navigator) ?
121
121
  window.navigator :
122
122
  undefined,
@@ -152,13 +152,13 @@
152
152
  return enums;
153
153
  },
154
154
  has = function (str1, str2) {
155
- if (typeof str1 === OBJ_TYPE && str1.length > 0) {
155
+ if (typeof str1 === TYPEOF.OBJECT && str1.length > 0) {
156
156
  for (var i in str1) {
157
- if (lowerize(str1[i]) == lowerize(str2)) return true;
157
+ if (lowerize(str2) == lowerize(str1[i])) return true;
158
158
  }
159
159
  return false;
160
160
  }
161
- return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
161
+ return isString(str1) ? lowerize(str2) == lowerize(str1) : false;
162
162
  },
163
163
  isExtensions = function (obj, deep) {
164
164
  for (var prop in obj) {
@@ -166,7 +166,7 @@
166
166
  }
167
167
  },
168
168
  isString = function (val) {
169
- return typeof val === STR_TYPE;
169
+ return typeof val === TYPEOF.STRING;
170
170
  },
171
171
  itemListToArray = function (header) {
172
172
  if (!header) return undefined;
@@ -190,8 +190,10 @@
190
190
  },
191
191
  setProps = function (arr) {
192
192
  for (var i in arr) {
193
+ if (!arr.hasOwnProperty(i)) continue;
194
+
193
195
  var propName = arr[i];
194
- if (typeof propName == OBJ_TYPE && propName.length == 2) {
196
+ if (typeof propName == TYPEOF.OBJECT && propName.length == 2) {
195
197
  this[propName[0]] = propName[1];
196
198
  } else {
197
199
  this[propName] = undefined;
@@ -206,10 +208,8 @@
206
208
  return strip(/\\?\"/g, str);
207
209
  },
208
210
  trim = function (str, len) {
209
- if (isString(str)) {
210
- str = strip(/^\s\s*/, str);
211
- return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
212
- }
211
+ str = strip(/^\s\s*/, String(str));
212
+ return typeof len === TYPEOF.UNDEFINED ? str : str.substring(0, len);
213
213
  };
214
214
 
215
215
  ///////////////
@@ -240,26 +240,34 @@
240
240
  match = matches[++k];
241
241
  q = props[p];
242
242
  // check if given property is actually array
243
- if (typeof q === OBJ_TYPE && q.length > 0) {
243
+ if (typeof q === TYPEOF.OBJECT && q.length > 0) {
244
244
  if (q.length === 2) {
245
- if (typeof q[1] == FUNC_TYPE) {
245
+ if (typeof q[1] == TYPEOF.FUNCTION) {
246
246
  // assign modified match
247
247
  this[q[0]] = q[1].call(this, match);
248
248
  } else {
249
249
  // assign given value, ignore regex match
250
250
  this[q[0]] = q[1];
251
251
  }
252
- } else if (q.length === 3) {
253
- // check whether function or regex
254
- if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {
255
- // call function (usually string mapper)
256
- this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;
252
+ } else if (q.length >= 3) {
253
+ // Check whether q[1] FUNCTION or REGEX
254
+ if (typeof q[1] === TYPEOF.FUNCTION && !(q[1].exec && q[1].test)) {
255
+ if (q.length > 3) {
256
+ this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined;
257
+ } else {
258
+ // call function (usually string mapper)
259
+ this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;
260
+ }
257
261
  } else {
258
- // sanitize match using given regex
259
- this[q[0]] = match ? match.replace(q[1], q[2]) : undefined;
262
+ if (q.length == 3) {
263
+ // sanitize match using given regex
264
+ this[q[0]] = match ? match.replace(q[1], q[2]) : undefined;
265
+ } else if (q.length == 4) {
266
+ this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;
267
+ } else if (q.length > 4) {
268
+ this[q[0]] = match ? q[3].apply(this, [match.replace(q[1], q[2])].concat(q.slice(4))) : undefined;
269
+ }
260
270
  }
261
- } else if (q.length === 4) {
262
- this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;
263
271
  }
264
272
  } else {
265
273
  this[q] = match ? match : undefined;
@@ -275,7 +283,7 @@
275
283
 
276
284
  for (var i in map) {
277
285
  // check if current value is array
278
- if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {
286
+ if (typeof map[i] === TYPEOF.OBJECT && map[i].length > 0) {
279
287
  for (var j = 0; j < map[i].length; j++) {
280
288
  if (has(map[i][j], str)) {
281
289
  return (i === UNKNOWN) ? undefined : i;
@@ -293,17 +301,17 @@
293
301
  //////////////
294
302
 
295
303
  var windowsVersionMap = {
296
- 'ME' : '4.90',
297
- 'NT 3.11' : 'NT3.51',
298
- 'NT 4.0' : 'NT4.0',
299
- '2000' : 'NT 5.0',
300
- 'XP' : ['NT 5.1', 'NT 5.2'],
301
- 'Vista' : 'NT 6.0',
302
- '7' : 'NT 6.1',
303
- '8' : 'NT 6.2',
304
- '8.1' : 'NT 6.3',
305
- '10' : ['NT 6.4', 'NT 10.0'],
306
- 'RT' : 'ARM'
304
+ 'ME' : '4.90',
305
+ 'NT 3.51': '3.51',
306
+ 'NT 4.0': '4.0',
307
+ '2000' : ['5.0', '5.01'],
308
+ 'XP' : ['5.1', '5.2'],
309
+ 'Vista' : '6.0',
310
+ '7' : '6.1',
311
+ '8' : '6.2',
312
+ '8.1' : '6.3',
313
+ '10' : ['6.4', '10.0'],
314
+ 'NT' : ''
307
315
  },
308
316
 
309
317
  formFactorsMap = {
@@ -315,6 +323,18 @@
315
323
  'xr' : ['VR', 'XR'],
316
324
  '?' : ['Desktop', 'Unknown'],
317
325
  '*' : undefined
326
+ },
327
+
328
+ browserHintsMap = {
329
+ 'Chrome' : 'Google Chrome',
330
+ 'Edge' : 'Microsoft Edge',
331
+ 'Edge WebView2' : 'Microsoft Edge WebView2',
332
+ 'Chrome WebView': 'Android WebView',
333
+ 'Chrome Headless':'HeadlessChrome',
334
+ 'Huawei Browser': 'HuaweiBrowser',
335
+ 'MIUI Browser' : 'Miui Browser',
336
+ 'Opera Mobi' : 'OperaMobile',
337
+ 'Yandex' : 'YaBrowser'
318
338
  };
319
339
 
320
340
  //////////////
@@ -328,7 +348,9 @@
328
348
  // Most common regardless engine
329
349
  /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
330
350
  ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [
331
- /edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge
351
+ /webview.+edge\/([\w\.]+)/i // Microsoft Edge
352
+ ], [VERSION, [NAME, EDGE+' WebView']], [
353
+ /edg(?:e|ios|a)?\/([\w\.]+)/i
332
354
  ], [VERSION, [NAME, 'Edge']], [
333
355
 
334
356
  // Presto based
@@ -355,10 +377,11 @@
355
377
  /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet
356
378
  /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer
357
379
 
358
- // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble
359
- /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:lg |qute)browser)\/([-\w\.]+)/i,
380
+ // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble/Palemoon
381
+ /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:lg |qute)browser|palemoon)\/([-\w\.]+)/i,
360
382
  // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
361
- /(heytap|ovi|115|surf)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf
383
+ /(heytap|ovi|115|surf|qwant)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf
384
+ /(qwant)(?:ios|mobile)\/([\d\.]+)/i, // Qwant
362
385
  /(ecosia|weibo)(?:__| \w+@)([\d\.]+)/i // Ecosia/Weibo
363
386
  ], [NAME, VERSION], [
364
387
  /quark(?:pc)?\/([-\w\.]+)/i // Quark
@@ -420,7 +443,6 @@
420
443
  // WebView
421
444
  /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android
422
445
  ], [[NAME, FACEBOOK], VERSION, [TYPE, INAPP]], [
423
- /(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android
424
446
  /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App
425
447
  /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp
426
448
  /(daum)apps[\/ ]([\w\.]+)/i, // Daum App
@@ -428,7 +450,8 @@
428
450
  /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
429
451
  /(alipay)client\/([\w\.]+)/i, // Alipay
430
452
  /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter
431
- /(instagram|snapchat)[\/ ]([-\w\.]+)/i // Instagram/Snapchat
453
+ /(bing)(?:web|sapphire)\/([\w\.]+)/i, // Bing
454
+ /(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna
432
455
  ], [NAME, VERSION, [TYPE, INAPP]], [
433
456
  /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
434
457
  ], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [
@@ -436,6 +459,8 @@
436
459
  ], [VERSION, [NAME, 'TikTok'], [TYPE, INAPP]], [
437
460
  /\[(linkedin)app\]/i // LinkedIn App for iOS & Android
438
461
  ], [NAME, [TYPE, INAPP]], [
462
+ /(zalo(?:app)?)[\/\sa-z]*([\w\.-]+)/i // Zalo
463
+ ], [[NAME, /(.+)/, 'Zalo'], VERSION, [TYPE, INAPP]], [
439
464
 
440
465
  /(chromium)[\/ ]([-\w\.]+)/i // Chromium
441
466
  ], [NAME, VERSION], [
@@ -443,6 +468,9 @@
443
468
  /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless
444
469
  ], [VERSION, [NAME, CHROME+' Headless']], [
445
470
 
471
+ /wv\).+chrome\/([\w\.]+).+edgw\//i // Edge WebView2
472
+ ], [VERSION, [NAME, EDGE+' WebView2']], [
473
+
446
474
  / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
447
475
  ], [[NAME, CHROME+' WebView'], VERSION], [
448
476
 
@@ -480,10 +508,10 @@
480
508
  /(swiftfox)/i, // Swiftfox
481
509
  /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i,
482
510
  // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
483
- /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i,
511
+ /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|basilisk|waterfox)\/([-\w\.]+)$/i,
484
512
  // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
485
513
  /(firefox)\/([\w\.]+)/i, // Other Firefox-based
486
- /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla
514
+ /(mozilla)\/([\w\.]+(?= .+rv\:.+gecko\/\d+)|[0-4][\w\.]+(?!.+compatible))/i, // Mozilla
487
515
 
488
516
  // Other
489
517
  /(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
@@ -514,15 +542,17 @@
514
542
  /( (ce|mobile); ppc;|\/[\w\.]+arm\b)/i
515
543
  ], [[ARCHITECTURE, 'arm']], [
516
544
 
517
- /((ppc|powerpc)(64)?)( mac|;|\))/i // PowerPC
518
- ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [
519
-
520
545
  / sun4\w[;\)]/i // SPARC
521
546
  ], [[ARCHITECTURE, 'sparc']], [
522
-
523
- /\b(avr32|ia64(?=;)|68k(?=\))|\barm(?=v([1-7]|[5-7]1)l?|;|eabi)|(irix|mips|sparc)(64)?\b|pa-risc)/i
524
547
  // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC
525
- ], [[ARCHITECTURE, lowerize]]
548
+ /\b(avr32|ia64(?=;)|68k(?=\))|\barm(?=v([1-7]|[5-7]1)l?|;|eabi)|(irix|mips|sparc)(64)?\b|pa-risc)/i,
549
+ /((ppc|powerpc)(64)?)( mac|;|\))/i, // PowerPC
550
+ /(?:osf1|[freopnt]{3,4}bsd) (alpha)/i // Alpha
551
+ ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [
552
+ /mc680.0/i
553
+ ], [[ARCHITECTURE, '68k']], [
554
+ /winnt.+\[axp/i
555
+ ], [[ARCHITECTURE, 'alpha']]
526
556
  ],
527
557
 
528
558
  device : [[
@@ -535,16 +565,15 @@
535
565
  /\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i
536
566
  ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [
537
567
  /\b((?:s[cgp]h|gt|sm)-(?![lr])\w+|sc[g-]?[\d]+a?|galaxy nexus)/i,
538
- /samsung[- ]((?!sm-[lr])[-\w]+)/i,
568
+ /samsung[- ]((?!sm-[lr]|browser)[-\w]+)/i,
539
569
  /sec-(sgh\w+)/i
540
570
  ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [
541
571
 
542
572
  // Apple
543
- /(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone
573
+ /(?:\/|\()(ip(?:hone|od)[\w, ]*)[\/\);]/i // iPod/iPhone
544
574
  ], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [
545
- /\((ipad);[-\w\),; ]+apple/i, // iPad
546
- /applecoremedia\/[\w\.]+ \((ipad)/i,
547
- /\b(ipad)\d\d?,\d\d?[;\]].+ios/i
575
+ /\b(?:ios|apple\w+)\/.+[\(\/](ipad)/i, // iPad
576
+ /\b(ipad)[\d,]*[;\] ].+(mac |i(pad)?)os/i
548
577
  ], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [
549
578
  /(macintosh);/i
550
579
  ], [MODEL, [VENDOR, APPLE]], [
@@ -562,13 +591,13 @@
562
591
  // Huawei
563
592
  /\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i
564
593
  ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [
565
- /(?:huawei)([-\w ]+)[;\)]/i,
566
- /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i
594
+ /(?:huawei) ?([-\w ]+)[;\)]/i,
595
+ /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][\dc][adnt]?)\b(?!.+d\/s)/i
567
596
  ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
568
597
 
569
598
  // Xiaomi
570
599
  /oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i,
571
- /\b((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
600
+ /\b(?:xiao)?((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
572
601
  ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
573
602
 
574
603
  /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
@@ -576,20 +605,26 @@
576
605
  /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
577
606
  /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
578
607
  /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models
579
- /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i, // Xiaomi Mi
608
+ /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i, // Xiaomi Mi
580
609
  / ([\w ]+) miui\/v?\d/i
581
610
  ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
582
611
 
612
+ // OnePlus
613
+ /droid.+; (cph2[3-6]\d[13579]|((gm|hd)19|(ac|be|in|kb)20|(d[en]|eb|le|mt)21|ne22)[0-2]\d|p[g-k]\w[1m]10)\b/i,
614
+ /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i
615
+ ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [
616
+
583
617
  // OPPO
584
618
  /; (\w+) bui.+ oppo/i,
585
619
  /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i
586
620
  ], [MODEL, [VENDOR, OPPO], [TYPE, MOBILE]], [
587
621
  /\b(opd2(\d{3}a?))(?: bui|\))/i
588
- ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['304', '403', '203'], '*' : OPPO }], [TYPE, TABLET]], [
622
+ ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['203', '304', '403', '404', '413', '415'], '*' : OPPO }], [TYPE, TABLET]], [
623
+
624
+ // BLU
625
+ /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i // Vivo series
626
+ ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [
589
627
 
590
- // BLU Vivo Series
591
- /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i
592
- ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [
593
628
  // Vivo
594
629
  /; vivo (\w+)(?: bui|\))/i,
595
630
  /\b(v[12]\d{3}\w?[at])(?: bui|;)/i
@@ -599,10 +634,17 @@
599
634
  /\b(rmx[1-3]\d{3})(?: bui|;|\))/i
600
635
  ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [
601
636
 
637
+ // Lenovo
638
+ /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i,
639
+ /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i
640
+ ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [
641
+ /lenovo[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i
642
+ ], [MODEL, [VENDOR, LENOVO], [TYPE, MOBILE]], [
643
+
602
644
  // Motorola
603
645
  /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i,
604
- /\bmot(?:orola)?[- ](\w*)/i,
605
- /((?:moto(?! 360)[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i
646
+ /\bmot(?:orola)?[- ]([\w\s]+)(\)| bui)/i,
647
+ /((?:moto(?! 360)[-\w\(\) ]+|xt\d{3,4}[cgkosw\+]?[-\d]*|nexus 6)(?= bui|\)))/i
606
648
  ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [
607
649
  /\b(mz60\d|xoom[2 ]{0,2}) build\//i
608
650
  ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [
@@ -611,40 +653,33 @@
611
653
  /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
612
654
  ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [
613
655
  /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i,
614
- /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch))(\w+)/i,
656
+ /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch|webos))(\w+)/i,
615
657
  /\blg-?([\d\w]+) bui/i
616
658
  ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [
617
659
 
618
- // Lenovo
619
- /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i,
620
- /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i
621
- ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [
622
-
623
660
  // Nokia
624
661
  /(nokia) (t[12][01])/i
625
662
  ], [VENDOR, MODEL, [TYPE, TABLET]], [
626
663
  /(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i,
627
- /nokia[-_ ]?(([-\w\. ]*))/i
664
+ /nokia[-_ ]?(([-\w\. ]*?))( bui|\)|;|\/)/i
628
665
  ], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [
629
666
 
630
667
  // Google
631
668
  /(pixel (c|tablet))\b/i // Google Pixel C/Tablet
632
669
  ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [
633
- /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel
670
+ // Google Pixel
671
+ /droid.+;(?: google)? (g(01[13]a|020[aem]|025[jn]|1b60|1f8f|2ybb|4s1m|576d|5nz6|8hhn|8vou|a02099|c15s|d1yq|e2ae|ec77|gh2x|kv4x|p4bc|pj41|r83y|tt9q|ur25|wvk6)|pixel[\d ]*a?( pro)?( xl)?( fold)?( \(5g\))?)( bui|\))/i
634
672
  ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [
673
+ /(google) (pixelbook( go)?)/i
674
+ ], [VENDOR, MODEL], [
635
675
 
636
676
  // Sony
637
- /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i
677
+ /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-\w\w\d\d)(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i
638
678
  ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [
639
679
  /sony tablet [ps]/i,
640
680
  /\b(?:sony)?sgp\w+(?: bui|\))/i
641
681
  ], [[MODEL, 'Xperia Tablet'], [VENDOR, SONY], [TYPE, TABLET]], [
642
682
 
643
- // OnePlus
644
- / (kb2005|in20[12]5|be20[12][59])\b/i,
645
- /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i
646
- ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [
647
-
648
683
  // Amazon
649
684
  /(alexa)webm/i,
650
685
  /(kf[a-z]{2}wi|aeo(?!bc)\w\w)( bui|\))/i, // Kindle Fire without Silk / Echo Show
@@ -657,7 +692,7 @@
657
692
  /(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook
658
693
  ], [MODEL, VENDOR, [TYPE, TABLET]], [
659
694
  /\b((?:bb[a-f]|st[hv])100-\d)/i,
660
- /\(bb10; (\w+)/i // BlackBerry 10
695
+ /(?:blackberry|\(bb10;) (\w+)/i
661
696
  ], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [
662
697
 
663
698
  // Asus
@@ -715,7 +750,7 @@
715
750
  ], [MODEL, [VENDOR, 'Smartfren'], [TYPE, MOBILE]], [
716
751
 
717
752
  // Nothing
718
- /droid.+; (a(?:015|06[35]|142p?))/i
753
+ /droid.+; (a(in)?(0(15|59|6[35])|142)p?)/i
719
754
  ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [
720
755
 
721
756
  // Archos
@@ -726,18 +761,24 @@
726
761
  /; (ac[3-6]\d\w{2,8})( b|\))/i
727
762
  ], [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]], [
728
763
 
764
+ // HMD
765
+ /; (n159v)/i
766
+ ], [MODEL, [VENDOR, 'HMD'], [TYPE, MOBILE]], [
767
+
729
768
  // MIXED
730
769
  /(imo) (tab \w+)/i, // IMO
731
- /(infinix) (x1101b?)/i // Infinix XPad
770
+ /(infinix|tecno) (x1101b?|p904|dp(7c|8d|10a)( pro)?|p70[1-3]a?|p904|t1101)/i // Infinix XPad / Tecno
732
771
  ], [VENDOR, MODEL, [TYPE, TABLET]], [
733
772
 
734
- /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|infinix|tecno|micromax|advan)[-_ ]?([-\w]*)/i,
735
- // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Infinix/Tecno/Micromax/Advan
736
- /; (blu|hmd|imo|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/TCL
773
+ /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i,
774
+ // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan
775
+ // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL/Wiko
776
+ /; (blu|hmd|imo|infinix|lava|oneplus|tcl|wiko)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i,
737
777
  /(hp) ([\w ]+\w)/i, // HP iPAQ
738
778
  /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia
739
- /(lenovo)[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i, // Lenovo
740
- /(oppo) ?([\w ]+) bui/i // OPPO
779
+ /(oppo) ?([\w ]+) bui/i, // OPPO
780
+ /(hisense) ([ehv][\w ]+)\)/i, // Hisense
781
+ /droid[^;]+; (philips)[_ ]([sv-x][\d]{3,4}[xz]?)/i // Philips
741
782
  ], [VENDOR, MODEL, [TYPE, MOBILE]], [
742
783
 
743
784
  /(kobo)\s(ereader|touch)/i, // Kobo
@@ -764,10 +805,12 @@
764
805
  // SMARTTVS
765
806
  ///////////////////
766
807
 
808
+ /(philips)[\w ]+tv/i, // Philips
767
809
  /smart-tv.+(samsung)/i // Samsung
768
810
  ], [VENDOR, [TYPE, SMARTTV]], [
769
811
  /hbbtv.+maple;(\d+)/i
770
812
  ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [
813
+ /(vizio)(?: |.+model\/)(\w+-\w+)/i, // Vizio
771
814
  /tcast.+(lg)e?. ([-\w]+)/i // LG SmartTV
772
815
  ], [VENDOR, MODEL, [TYPE, SMARTTV]], [
773
816
  /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i
@@ -799,26 +842,22 @@
799
842
  ], [VENDOR, MODEL, [TYPE, SMARTTV]], [
800
843
  /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku
801
844
  /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices
802
- ], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [
803
- // SmartTV from Unidentified Vendors
804
- /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i
805
- ], [MODEL, [TYPE, SMARTTV]], [
806
- /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i
807
- ], [[TYPE, SMARTTV]], [
845
+ ], [[VENDOR, /.+\/(\w+)/, '$1', strMapper, {'LG':'lge'}], [MODEL, trim], [TYPE, SMARTTV]], [
808
846
 
809
847
  ///////////////////
810
848
  // CONSOLES
811
849
  ///////////////////
812
850
 
813
- /(ouya)/i, // Ouya
814
- /(nintendo) (\w+)/i // Nintendo
815
- ], [VENDOR, MODEL, [TYPE, CONSOLE]], [
816
- /droid.+; (shield)( bui|\))/i // Nvidia Portable
817
- ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [
818
851
  /(playstation \w+)/i // Playstation
819
852
  ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [
820
853
  /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox
821
854
  ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [
855
+ /(ouya)/i, // Ouya
856
+ /(nintendo) (\w+)/i, // Nintendo
857
+ /(retroid) (pocket ([^\)]+))/i // Retroid Pocket
858
+ ], [VENDOR, MODEL, [TYPE, CONSOLE]], [
859
+ /droid.+; (shield)( bui|\))/i // Nvidia Portable
860
+ ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [
822
861
 
823
862
  ///////////////////
824
863
  // WEARABLES
@@ -850,10 +889,12 @@
850
889
 
851
890
  /droid.+; (glass) \d/i // Google Glass
852
891
  ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [
853
- /(pico) (4|neo3(?: link|pro)?)/i // Pico
892
+ /(pico) ([\w ]+) os\d/i // Pico
854
893
  ], [VENDOR, MODEL, [TYPE, XR]], [
855
894
  /(quest( \d| pro)?s?).+vr/i // Meta Quest
856
895
  ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [
896
+ /mobile vr; rv.+firefox/i // Unidentifiable VR device using Firefox Reality / Wolvic
897
+ ], [[TYPE, XR]], [
857
898
 
858
899
  ///////////////////
859
900
  // EMBEDDED
@@ -865,14 +906,18 @@
865
906
  ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [
866
907
  /(homepod).+mac os/i // Apple HomePod
867
908
  ], [MODEL, [VENDOR, APPLE], [TYPE, EMBEDDED]], [
868
- /windows iot/i
909
+ /windows iot/i // Unidentifiable embedded device using Windows IoT
869
910
  ], [[TYPE, EMBEDDED]], [
870
911
 
871
912
  ////////////////////
872
913
  // MIXED (GENERIC)
873
914
  ///////////////////
874
915
 
875
- /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+?(mobile|vr|\d) safari/i
916
+ /droid.+; ([\w- ]+) (4k|android|smart|google)[- ]?tv/i // Unidentifiable SmartTV
917
+ ], [MODEL, [TYPE, SMARTTV]], [
918
+ /\b((4k|android|smart|opera)[- ]?tv|tv; rv:|large screen[\w ]+safari)\b/i
919
+ ], [[TYPE, SMARTTV]], [
920
+ /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew|; hmsc).+?(mobile|vr|\d) safari/i
876
921
  ], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [
877
922
  /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet
878
923
  ], [[TYPE, TABLET]], [
@@ -911,22 +956,25 @@
911
956
  os : [[
912
957
 
913
958
  // Windows
914
- /microsoft (windows) (vista|xp)/i // Windows (iTunes)
959
+ /(windows nt) (6\.[23]); arm/i // Windows RT
960
+ ], [[NAME, /N/, 'R'], [VERSION, strMapper, windowsVersionMap]], [
961
+ /(windows (?:phone|mobile|iot))(?: os)?[\/ ]?([\d\.]*( se)?)/i, // Windows IoT/Mobile/Phone
962
+ // Windows NT/3.1/95/98/ME/2000/XP/Vista/7/8/8.1/10/11
963
+ /(windows)[\/ ](1[01]|2000|3\.1|7|8(\.1)?|9[58]|me|server 20\d\d( r2)?|vista|xp)/i
964
+ ], [NAME, VERSION], [
965
+ /windows nt ?([\d\.\)]*)(?!.+xbox)/i,
966
+ /\bwin(?=3| ?9|n)(?:nt| 9x )?([\d\.;]*)/i
967
+ ], [[VERSION, /(;|\))/g, '', strMapper, windowsVersionMap], [NAME, WINDOWS]], [
968
+ /(windows ce)\/?([\d\.]*)/i // Windows CE
915
969
  ], [NAME, VERSION], [
916
- /(windows (?:phone(?: os)?|mobile|iot))[\/ ]?([\d\.\w ]*)/i // Windows Phone
917
- ], [NAME, [VERSION, strMapper, windowsVersionMap]], [
918
- /windows nt 6\.2; (arm)/i, // Windows RT
919
- /windows[\/ ]([ntce\d\. ]+\w)(?!.+xbox)/i,
920
- /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i
921
- ], [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]], [
922
970
 
923
971
  // iOS/macOS
924
972
  /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
925
- /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
973
+ /(?:ios;fbsv|ios(?=.+ip(?:ad|hone))|ip(?:ad|hone)(?: |.+i(?:pad)?)os)[\/ ]([\w\.]+)/i,
926
974
  /cfnetwork\/.+darwin/i
927
975
  ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
928
976
  /(mac os x) ?([\w\. ]*)/i,
929
- /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS
977
+ /(macintosh|mac_powerpc\b)(?!.+(haiku|morphos))/i // Mac OS
930
978
  ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [
931
979
 
932
980
  // Google Chromecast
@@ -942,12 +990,13 @@
942
990
  ], [VERSION, [NAME, CHROMECAST]], [
943
991
 
944
992
  // Mobile OSes
945
- /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS
993
+ /droid ([\w\.]+)\b.+(android[- ]x86)/i // Android-x86
946
994
  ], [VERSION, NAME], [
947
995
  /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch
948
996
  ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [
997
+ /(harmonyos)[\/ ]?([\d\.]*)/i, // HarmonyOS
949
998
  // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen
950
- /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen|webos)\w*[-\/\.; ]?([\d\.]*)/i
999
+ /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen)\w*[-\/\.; ]?([\d\.]*)/i
951
1000
  ], [NAME, VERSION], [
952
1001
  /\(bb(10);/i // BlackBerry 10
953
1002
  ], [VERSION, [NAME, BLACKBERRY]], [
@@ -955,9 +1004,12 @@
955
1004
  ], [VERSION, [NAME, 'Symbian']], [
956
1005
  /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS
957
1006
  ], [VERSION, [NAME, FIREFOX+' OS']], [
958
- /web0s;.+rt(tv)/i,
959
- /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i // WebOS
1007
+ /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS
1008
+ /webos(?:[ \/]?|\.tv-20(?=2[2-9]))(\d[\d\.]*)/i
960
1009
  ], [VERSION, [NAME, 'webOS']], [
1010
+ /web0s;.+?(?:chr[o0]me|safari)\/(\d+)/i
1011
+ // https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine
1012
+ ], [[VERSION, strMapper, {'25':'120','24':'108','23':'94','22':'87','6':'79','5':'68','4':'53','3':'38','2':'538','1':'537','*':'TV'}], [NAME, 'webOS']], [
961
1013
  /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS
962
1014
  ], [VERSION, [NAME, 'watchOS']], [
963
1015
 
@@ -977,20 +1029,20 @@
977
1029
 
978
1030
  // Other
979
1031
  /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm
980
- /(mint)[\/\(\) ]?(\w*)/i, // Mint
981
- /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux
982
- /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i,
983
- // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire
984
- /(hurd|linux)(?: arm\w*| x86\w*| ?)([\w\.]*)/i, // Hurd/Linux
1032
+ /linux.+(mint)[\/\(\) ]?([\w\.]*)/i, // Mint
1033
+ /(mageia|vectorlinux|fuchsia|arcaos|arch(?= ?linux))[;l ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS/Arch
1034
+ /([kxln]?ubuntu|debian|suse|opensuse|gentoo|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire|knoppix)(?: gnu[\/ ]linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i,
1035
+ // Ubuntu/Debian/SUSE/Gentoo/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire/Knoppix
1036
+ /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris
1037
+ /\b(aix)[; ]([1-9\.]{0,4})/i, // AIX
1038
+ /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS
985
1039
  /(gnu) ?([\w\.]*)/i, // GNU
986
1040
  /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly
987
- /(haiku) (\w+)/i // Haiku
1041
+ /(haiku) ?(r\d)?/i // Haiku
988
1042
  ], [NAME, VERSION], [
989
- /(sunos) ?([\w\.\d]*)/i // Solaris
1043
+ /(sunos) ?([\d\.]*)/i // Solaris
990
1044
  ], [[NAME, 'Solaris'], VERSION], [
991
- /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris
992
- /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX
993
- /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS
1045
+ /\b(beos|os\/2|amigaos|openvms|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/OpenVMS/HP-UX/SerenityOS
994
1046
  /(unix) ?([\w\.]*)/i // UNIX
995
1047
  ], [NAME, VERSION]
996
1048
  ]
@@ -1003,27 +1055,27 @@
1003
1055
  var defaultProps = (function () {
1004
1056
  var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
1005
1057
  setProps.call(props.init, [
1006
- [UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]],
1007
- [UA_CPU, [ARCHITECTURE]],
1008
- [UA_DEVICE, [TYPE, MODEL, VENDOR]],
1009
- [UA_ENGINE, [NAME, VERSION]],
1010
- [UA_OS, [NAME, VERSION]]
1058
+ [BROWSER, [NAME, VERSION, MAJOR, TYPE]],
1059
+ [CPU, [ARCHITECTURE]],
1060
+ [DEVICE, [TYPE, MODEL, VENDOR]],
1061
+ [ENGINE, [NAME, VERSION]],
1062
+ [OS, [NAME, VERSION]]
1011
1063
  ]);
1012
1064
  setProps.call(props.isIgnore, [
1013
- [UA_BROWSER, [VERSION, MAJOR]],
1014
- [UA_ENGINE, [VERSION]],
1015
- [UA_OS, [VERSION]]
1065
+ [BROWSER, [VERSION, MAJOR]],
1066
+ [ENGINE, [VERSION]],
1067
+ [OS, [VERSION]]
1016
1068
  ]);
1017
1069
  setProps.call(props.isIgnoreRgx, [
1018
- [UA_BROWSER, / ?browser$/i],
1019
- [UA_OS, / ?os$/i]
1070
+ [BROWSER, / ?browser$/i],
1071
+ [OS, / ?os$/i]
1020
1072
  ]);
1021
1073
  setProps.call(props.toString, [
1022
- [UA_BROWSER, [NAME, VERSION]],
1023
- [UA_CPU, [ARCHITECTURE]],
1024
- [UA_DEVICE, [VENDOR, MODEL]],
1025
- [UA_ENGINE, [NAME, VERSION]],
1026
- [UA_OS, [NAME, VERSION]]
1074
+ [BROWSER, [NAME, VERSION]],
1075
+ [CPU, [ARCHITECTURE]],
1076
+ [DEVICE, [VENDOR, MODEL]],
1077
+ [ENGINE, [NAME, VERSION]],
1078
+ [OS, [NAME, VERSION]]
1027
1079
  ]);
1028
1080
  return props;
1029
1081
  })();
@@ -1067,14 +1119,14 @@
1067
1119
  return item.detectFeature().get();
1068
1120
  };
1069
1121
 
1070
- if (itemType != UA_RESULT) {
1122
+ if (itemType != RESULT) {
1071
1123
  IData.prototype.is = function (strToCheck) {
1072
1124
  var is = false;
1073
1125
  for (var i in this) {
1074
1126
  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)) {
1075
1127
  is = true;
1076
- if (strToCheck != UNDEF_TYPE) break;
1077
- } else if (strToCheck == UNDEF_TYPE && is) {
1128
+ if (strToCheck != TYPEOF.UNDEFINED) break;
1129
+ } else if (strToCheck == TYPEOF.UNDEFINED && is) {
1078
1130
  is = !is;
1079
1131
  break;
1080
1132
  }
@@ -1084,11 +1136,11 @@
1084
1136
  IData.prototype.toString = function () {
1085
1137
  var str = EMPTY;
1086
1138
  for (var i in toString_props) {
1087
- if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
1139
+ if (typeof(this[toString_props[i]]) !== TYPEOF.UNDEFINED) {
1088
1140
  str += (str ? ' ' : EMPTY) + this[toString_props[i]];
1089
1141
  }
1090
1142
  }
1091
- return str || UNDEF_TYPE;
1143
+ return str || TYPEOF.UNDEFINED;
1092
1144
  };
1093
1145
  }
1094
1146
 
@@ -1124,19 +1176,19 @@
1124
1176
  setProps.call(this, CH_ALL_VALUES);
1125
1177
  if (isHttpUACH) {
1126
1178
  setProps.call(this, [
1127
- [BRANDS, itemListToArray(uach[CH_HEADER])],
1128
- [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
1129
- [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
1130
- [MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
1131
- [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
1132
- [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
1133
- [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
1134
- [FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])],
1135
- [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
1179
+ [BRANDS, itemListToArray(uach[CH])],
1180
+ [FULLVERLIST, itemListToArray(uach[CH_FULL_VER_LIST])],
1181
+ [MOBILE, /\?1/.test(uach[CH_MOBILE])],
1182
+ [MODEL, stripQuotes(uach[CH_MODEL])],
1183
+ [PLATFORM, stripQuotes(uach[CH_PLATFORM])],
1184
+ [PLATFORMVER, stripQuotes(uach[CH_PLATFORM_VER])],
1185
+ [ARCHITECTURE, stripQuotes(uach[CH_ARCH])],
1186
+ [FORMFACTORS, itemListToArray(uach[CH_FORM_FACTORS])],
1187
+ [BITNESS, stripQuotes(uach[CH_BITNESS])]
1136
1188
  ]);
1137
1189
  } else {
1138
1190
  for (var prop in uach) {
1139
- if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop];
1191
+ if(this.hasOwnProperty(prop) && typeof uach[prop] !== TYPEOF.UNDEFINED) this[prop] = uach[prop];
1140
1192
  }
1141
1193
  }
1142
1194
  }
@@ -1161,30 +1213,30 @@
1161
1213
  this.detectFeature = function () {
1162
1214
  if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
1163
1215
  switch (this.itemType) {
1164
- case UA_BROWSER:
1216
+ case BROWSER:
1165
1217
  // Brave-specific detection
1166
- if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
1218
+ if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
1167
1219
  this.set(NAME, 'Brave');
1168
1220
  }
1169
1221
  break;
1170
- case UA_DEVICE:
1222
+ case DEVICE:
1171
1223
  // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
1172
1224
  if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
1173
1225
  this.set(TYPE, MOBILE);
1174
1226
  }
1175
1227
  // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
1176
- if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
1228
+ if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
1177
1229
  this.set(MODEL, 'iPad')
1178
1230
  .set(TYPE, TABLET);
1179
1231
  }
1180
1232
  break;
1181
- case UA_OS:
1233
+ case OS:
1182
1234
  // Chrome-specific detection: check for 'platform' value of navigator.userAgentData
1183
1235
  if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
1184
1236
  this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
1185
1237
  }
1186
1238
  break;
1187
- case UA_RESULT:
1239
+ case RESULT:
1188
1240
  var data = this.data;
1189
1241
  var detect = function (itemType) {
1190
1242
  return data[itemType]
@@ -1192,22 +1244,33 @@
1192
1244
  .detectFeature()
1193
1245
  .get();
1194
1246
  };
1195
- this.set(UA_BROWSER, detect(UA_BROWSER))
1196
- .set(UA_CPU, detect(UA_CPU))
1197
- .set(UA_DEVICE, detect(UA_DEVICE))
1198
- .set(UA_ENGINE, detect(UA_ENGINE))
1199
- .set(UA_OS, detect(UA_OS));
1247
+ this.set(BROWSER, detect(BROWSER))
1248
+ .set(CPU, detect(CPU))
1249
+ .set(DEVICE, detect(DEVICE))
1250
+ .set(ENGINE, detect(ENGINE))
1251
+ .set(OS, detect(OS));
1200
1252
  }
1201
1253
  }
1202
1254
  return this;
1203
1255
  };
1204
1256
 
1205
1257
  this.parseUA = function () {
1206
- if (this.itemType != UA_RESULT) {
1258
+ if (this.itemType != RESULT) {
1207
1259
  rgxMapper.call(this.data, this.ua, this.rgxMap);
1208
1260
  }
1209
- if (this.itemType == UA_BROWSER) {
1210
- this.set(MAJOR, majorize(this.get(VERSION)));
1261
+ switch (this.itemType) {
1262
+ case BROWSER:
1263
+ this.set(MAJOR, majorize(this.get(VERSION)));
1264
+ break;
1265
+ case OS:
1266
+ if (this.get(NAME) == 'iOS' && this.get(VERSION) == '18.6') {
1267
+ // Based on the assumption that iOS version is tightly coupled with Safari version
1268
+ var realVersion = /\) Version\/([\d\.]+)/.exec(this.ua); // Get Safari version
1269
+ if (realVersion && parseInt(realVersion[1].substring(0,2), 10) >= 26) {
1270
+ this.set(VERSION, realVersion[1]); // Set as iOS version
1271
+ }
1272
+ }
1273
+ break;
1211
1274
  }
1212
1275
  return this;
1213
1276
  };
@@ -1217,43 +1280,42 @@
1217
1280
  rgxMap = this.rgxMap;
1218
1281
 
1219
1282
  switch (this.itemType) {
1220
- case UA_BROWSER:
1221
- case UA_ENGINE:
1283
+ case BROWSER:
1284
+ case ENGINE:
1222
1285
  var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
1223
1286
  if (brands) {
1224
- for (var i in brands) {
1287
+ for (var i=0; i<brands.length; i++) {
1225
1288
  var brandName = brands[i].brand || brands[i],
1226
1289
  brandVersion = brands[i].version;
1227
- if (this.itemType == UA_BROWSER && !/not.a.brand/i.test(brandName) && (!prevName || (/chrom/i.test(prevName) && brandName != CHROMIUM))) {
1228
- brandName = strMapper(brandName, {
1229
- 'Chrome' : 'Google Chrome',
1230
- 'Edge' : 'Microsoft Edge',
1231
- 'Chrome WebView' : 'Android WebView',
1232
- 'Chrome Headless' : 'HeadlessChrome',
1233
- 'Huawei Browser' : 'HuaweiBrowser',
1234
- 'MIUI Browser' : 'Miui Browser',
1235
- 'Opera Mobi' : 'OperaMobile',
1236
- 'Yandex' : 'YaBrowser'
1237
- });
1238
- this.set(NAME, brandName)
1239
- .set(VERSION, brandVersion)
1240
- .set(MAJOR, majorize(brandVersion));
1241
- prevName = brandName;
1290
+ if (this.itemType == BROWSER &&
1291
+ !/not.a.brand/i.test(brandName) &&
1292
+ (!prevName ||
1293
+ (/Chrom/.test(prevName) && brandName != CHROMIUM) ||
1294
+ (prevName == EDGE && /WebView2/.test(brandName))
1295
+ )) {
1296
+ brandName = strMapper(brandName, browserHintsMap);
1297
+ prevName = this.get(NAME);
1298
+ if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) {
1299
+ this.set(NAME, brandName)
1300
+ .set(VERSION, brandVersion)
1301
+ .set(MAJOR, majorize(brandVersion));
1302
+ }
1303
+ prevName = brandName;
1242
1304
  }
1243
- if (this.itemType == UA_ENGINE && brandName == CHROMIUM) {
1305
+ if (this.itemType == ENGINE && brandName == CHROMIUM) {
1244
1306
  this.set(VERSION, brandVersion);
1245
1307
  }
1246
1308
  }
1247
1309
  }
1248
1310
  break;
1249
- case UA_CPU:
1311
+ case CPU:
1250
1312
  var archName = uaCH[ARCHITECTURE];
1251
1313
  if (archName) {
1252
1314
  if (archName && uaCH[BITNESS] == '64') archName += '64';
1253
1315
  rgxMapper.call(this.data, archName + ';', rgxMap);
1254
1316
  }
1255
1317
  break;
1256
- case UA_DEVICE:
1318
+ case DEVICE:
1257
1319
  if (uaCH[MOBILE]) {
1258
1320
  this.set(TYPE, MOBILE);
1259
1321
  }
@@ -1283,7 +1345,7 @@
1283
1345
  this.set(TYPE, ff);
1284
1346
  }
1285
1347
  break;
1286
- case UA_OS:
1348
+ case OS:
1287
1349
  var osName = uaCH[PLATFORM];
1288
1350
  if(osName) {
1289
1351
  var osVersion = uaCH[PLATFORMVER];
@@ -1297,7 +1359,7 @@
1297
1359
  .set(VERSION, undefined);
1298
1360
  }
1299
1361
  break;
1300
- case UA_RESULT:
1362
+ case RESULT:
1301
1363
  var data = this.data;
1302
1364
  var parse = function (itemType) {
1303
1365
  return data[itemType]
@@ -1306,11 +1368,11 @@
1306
1368
  .parseCH()
1307
1369
  .get();
1308
1370
  };
1309
- this.set(UA_BROWSER, parse(UA_BROWSER))
1310
- .set(UA_CPU, parse(UA_CPU))
1311
- .set(UA_DEVICE, parse(UA_DEVICE))
1312
- .set(UA_ENGINE, parse(UA_ENGINE))
1313
- .set(UA_OS, parse(UA_OS));
1371
+ this.set(BROWSER, parse(BROWSER))
1372
+ .set(CPU, parse(CPU))
1373
+ .set(DEVICE, parse(DEVICE))
1374
+ .set(ENGINE, parse(ENGINE))
1375
+ .set(OS, parse(OS));
1314
1376
  }
1315
1377
  return this;
1316
1378
  };
@@ -1328,9 +1390,9 @@
1328
1390
 
1329
1391
  function UAParser (ua, extensions, headers) {
1330
1392
 
1331
- if (typeof ua === OBJ_TYPE) {
1393
+ if (typeof ua === TYPEOF.OBJECT) {
1332
1394
  if (isExtensions(ua, true)) {
1333
- if (typeof extensions === OBJ_TYPE) {
1395
+ if (typeof extensions === TYPEOF.OBJECT) {
1334
1396
  headers = extensions; // case UAParser(extensions, headers)
1335
1397
  }
1336
1398
  extensions = ua; // case UAParser(extensions)
@@ -1339,23 +1401,34 @@
1339
1401
  extensions = undefined;
1340
1402
  }
1341
1403
  ua = undefined;
1342
- } else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) {
1404
+ } else if (typeof ua === TYPEOF.STRING && !isExtensions(extensions, true)) {
1343
1405
  headers = extensions; // case UAParser(ua, headers)
1344
1406
  extensions = undefined;
1345
1407
  }
1346
1408
 
1347
- // Convert Headers object into a plain object
1348
- if (headers && typeof headers.append === FUNC_TYPE) {
1349
- var kv = {};
1350
- headers.forEach(function (v, k) { kv[k] = v; });
1351
- headers = kv;
1409
+ if (headers) {
1410
+ if (typeof headers.append === TYPEOF.FUNCTION) {
1411
+ // Convert Headers object into a plain object
1412
+ var kv = {};
1413
+ headers.forEach(function (v, k) { kv[String(k).toLowerCase()] = v; });
1414
+ headers = kv;
1415
+ } else {
1416
+ // Normalize headers field name into lowercase
1417
+ var normalized = {};
1418
+ for (var header in headers) {
1419
+ if (headers.hasOwnProperty(header)) {
1420
+ normalized[String(header).toLowerCase()] = headers[header];
1421
+ }
1422
+ }
1423
+ headers = normalized;
1424
+ }
1352
1425
  }
1353
1426
 
1354
1427
  if (!(this instanceof UAParser)) {
1355
1428
  return new UAParser(ua, extensions, headers).getResult();
1356
1429
  }
1357
1430
 
1358
- var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string
1431
+ var userAgent = typeof ua === TYPEOF.STRING ? ua : // Passed user-agent string
1359
1432
  (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers
1360
1433
  ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent
1361
1434
  EMPTY)), // empty string
@@ -1366,15 +1439,15 @@
1366
1439
  defaultRegexes,
1367
1440
 
1368
1441
  createItemFunc = function (itemType) {
1369
- if (itemType == UA_RESULT) {
1442
+ if (itemType == RESULT) {
1370
1443
  return function () {
1371
1444
  return new UAItem(itemType, userAgent, regexMap, httpUACH)
1372
1445
  .set('ua', userAgent)
1373
- .set(UA_BROWSER, this.getBrowser())
1374
- .set(UA_CPU, this.getCPU())
1375
- .set(UA_DEVICE, this.getDevice())
1376
- .set(UA_ENGINE, this.getEngine())
1377
- .set(UA_OS, this.getOS())
1446
+ .set(BROWSER, this.getBrowser())
1447
+ .set(CPU, this.getCPU())
1448
+ .set(DEVICE, this.getDevice())
1449
+ .set(ENGINE, this.getEngine())
1450
+ .set(OS, this.getOS())
1378
1451
  .get();
1379
1452
  };
1380
1453
  } else {
@@ -1388,16 +1461,15 @@
1388
1461
 
1389
1462
  // public methods
1390
1463
  setProps.call(this, [
1391
- ['getBrowser', createItemFunc(UA_BROWSER)],
1392
- ['getCPU', createItemFunc(UA_CPU)],
1393
- ['getDevice', createItemFunc(UA_DEVICE)],
1394
- ['getEngine', createItemFunc(UA_ENGINE)],
1395
- ['getOS', createItemFunc(UA_OS)],
1396
- ['getResult', createItemFunc(UA_RESULT)],
1464
+ ['getBrowser', createItemFunc(BROWSER)],
1465
+ ['getCPU', createItemFunc(CPU)],
1466
+ ['getDevice', createItemFunc(DEVICE)],
1467
+ ['getEngine', createItemFunc(ENGINE)],
1468
+ ['getOS', createItemFunc(OS)],
1469
+ ['getResult', createItemFunc(RESULT)],
1397
1470
  ['getUA', function () { return userAgent; }],
1398
1471
  ['setUA', function (ua) {
1399
- if (isString(ua))
1400
- userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
1472
+ if (isString(ua)) userAgent = trim(ua, UA_MAX_LENGTH);
1401
1473
  return this;
1402
1474
  }]
1403
1475
  ])