ua-parser-js 2.0.6 → 2.0.9

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,6 +1,6 @@
1
1
  /////////////////////////////////////////////////////////////////////////////////
2
- /* UAParser.js v2.0.6
3
- Copyright © 2012-2025 Faisal Salman <f@faisalman.com>
2
+ /* UAParser.js v2.0.9
3
+ Copyright © 2012-2026 Faisal Salman <f@faisalman.com>
4
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.
@@ -19,7 +19,7 @@
19
19
  // Constants
20
20
  /////////////
21
21
 
22
- var LIBVERSION = '2.0.6',
22
+ var LIBVERSION = '2.0.9',
23
23
  UA_MAX_LENGTH = 500,
24
24
  USER_AGENT = 'user-agent',
25
25
  EMPTY = '',
@@ -57,6 +57,7 @@
57
57
  EMBEDDED = 'embedded',
58
58
 
59
59
  // browser types
60
+ FETCHER = 'fetcher',
60
61
  INAPP = 'inapp',
61
62
 
62
63
  // client hints
@@ -375,10 +376,11 @@
375
376
  /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet
376
377
  /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer
377
378
 
378
- // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble/Palemoon
379
- /(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,
380
- // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
381
- /(heytap|ovi|115|surf|qwant)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf
379
+ // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble/Palemoon/HiBrowser
380
+ /(atlas|flock|rockmelt|midori|epiphany|silk|skyfire|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:hi|lg |ovi|qute)browser|palemoon)\/v?([-\w\.]+)/i,
381
+ // Atlas/Rekonq/Puffin/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
382
+ /(brave)(?: chrome)?\/([\d\.]+)/i, // Brave
383
+ /(aloha|heytap|ovi|115|surf|qwant)browser\/([\d\.]+)/i, // Aloha/HeyTap/Ovi/115/Surf
382
384
  /(qwant)(?:ios|mobile)\/([\d\.]+)/i, // Qwant
383
385
  /(ecosia|weibo)(?:__| \w+@)([\d\.]+)/i // Ecosia/Weibo
384
386
  ], [NAME, VERSION], [
@@ -400,11 +402,15 @@
400
402
  ], [VERSION, [NAME, 'Yandex']], [
401
403
  /slbrowser\/([\w\.]+)/i // Smart Lenovo Browser
402
404
  ], [VERSION, [NAME, 'Smart ' + LENOVO + SUFFIX_BROWSER]], [
403
- /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser
405
+ /(av(?:ast|g|ira))\/([\w\.]+)/i // Avast/AVG/Avira Secure Browser
404
406
  ], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [
407
+ /norton\/([\w\.]+)/i // Norton Private Browser
408
+ ], [VERSION, [NAME, 'Norton Private' + SUFFIX_BROWSER]], [
405
409
  /\bfocus\/([\w\.]+)/i // Firefox Focus
406
410
  ], [VERSION, [NAME, FIREFOX+' Focus']], [
407
- /\bopt\/([\w\.]+)/i // Opera Touch
411
+ / mms\/([\w\.]+)$/i // Opera Neon
412
+ ], [VERSION, [NAME, OPERA+' Neon']], [
413
+ / opt\/([\w\.]+)$/i // Opera Touch
408
414
  ], [VERSION, [NAME, OPERA+' Touch']], [
409
415
  /coc_coc\w+\/([\w\.]+)/i // Coc Coc Browser
410
416
  ], [VERSION, [NAME, 'Coc Coc']], [
@@ -432,10 +438,9 @@
432
438
  /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla
433
439
  /m?(qqbrowser|2345(?=browser|chrome|explorer))\w*[\/ ]?v?([\w\.]+)/i // QQ/2345
434
440
  ], [NAME, VERSION], [
435
- /(lbbrowser|rekonq)/i // LieBao Browser/Rekonq
441
+ /(lbbrowser|luakit|rekonq|steam(?= (clie|tenf|gameo)))/i // LieBao Browser/Luakit/Rekonq/Steam
436
442
  ], [NAME], [
437
- /ome\/([\w\.]+) \w* ?(iron) saf/i, // Iron
438
- /ome\/([\w\.]+).+qihu (360)[es]e/i // 360
443
+ /ome\/([\w\.]+).+(iron(?= saf)|360(?=[es]e$))/i // Iron / 360
439
444
  ], [VERSION, NAME], [
440
445
 
441
446
  // WebView
@@ -453,7 +458,7 @@
453
458
  ], [NAME, VERSION, [TYPE, INAPP]], [
454
459
  /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
455
460
  ], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [
456
- /musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok
461
+ /(?:musical_ly|trill)(?:.+app_?version\/|_)([\w\.]+)/i // TikTok
457
462
  ], [VERSION, [NAME, 'TikTok'], [TYPE, INAPP]], [
458
463
  /\[(linkedin)app\]/i // LinkedIn App for iOS & Android
459
464
  ], [NAME, [TYPE, INAPP]], [
@@ -463,6 +468,9 @@
463
468
  /(chromium)[\/ ]([-\w\.]+)/i // Chromium
464
469
  ], [NAME, VERSION], [
465
470
 
471
+ /ome-(lighthouse)$/i // Chrome Lighthouse
472
+ ], [NAME, [TYPE, FETCHER]], [
473
+
466
474
  /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless
467
475
  ], [VERSION, [NAME, CHROME+' Headless']], [
468
476
 
@@ -598,17 +606,17 @@
598
606
  /\b(?:xiao)?((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
599
607
  ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
600
608
 
601
- /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
602
609
  /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
603
610
  /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
604
- /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
605
- /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models
606
- /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i, // Xiaomi Mi
611
+ // Xiaomi Redmi / POCO / Black Shark / Qin
612
+ /oid[^\)]+; (redmi[\-_ ]?(?:note|k)?[\w_ ]+|m?[12]\d[01]\d\w{3,6}|poco[\w ]+|(shark )?\w{3}-[ah]0|qin ?[1-3](s\+|ultra| pro)?)( bui|; wv|\))/i,
613
+ // Xiaomi Mi
614
+ /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i,
607
615
  / ([\w ]+) miui\/v?\d/i
608
616
  ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
609
617
 
610
618
  // OnePlus
611
- /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,
619
+ /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-l]\w[1m]10)\b/i,
612
620
  /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i
613
621
  ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [
614
622
 
@@ -648,7 +656,7 @@
648
656
  ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [
649
657
 
650
658
  // LG
651
- /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
659
+ /\b(?:lg)?([vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
652
660
  ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [
653
661
  /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i,
654
662
  /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch|webos))(\w+)/i,
@@ -852,10 +860,10 @@
852
860
  ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [
853
861
  /(ouya)/i, // Ouya
854
862
  /(nintendo) (\w+)/i, // Nintendo
855
- /(retroid) (pocket ([^\)]+))/i // Retroid Pocket
856
- ], [VENDOR, MODEL, [TYPE, CONSOLE]], [
857
- /droid.+; (shield)( bui|\))/i // Nvidia Portable
858
- ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [
863
+ /(retroid) (pocket ([^\)]+))/i, // Retroid Pocket
864
+ /(valve).+(steam deck)/i,
865
+ /droid.+; ((shield|rgcube|gr0006))( bui|\))/i // Nvidia Portable/Anbernic/Logitech
866
+ ], [[VENDOR, strMapper, { 'Nvidia': 'Shield', 'Anbernic': 'RGCUBE', 'Logitech': 'GR0006' }], MODEL, [TYPE, CONSOLE]], [
859
867
 
860
868
  ///////////////////
861
869
  // WEARABLES
@@ -864,7 +872,7 @@
864
872
  /\b(sm-[lr]\d\d[0156][fnuw]?s?|gear live)\b/i // Samsung Galaxy Watch
865
873
  ], [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]], [
866
874
  /((pebble))app/i, // Pebble
867
- /(asus|google|lg|oppo) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch
875
+ /(asus|google|lg|oppo|xiaomi) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch / Xiaomi Watch
868
876
  ], [VENDOR, MODEL, [TYPE, WEARABLE]], [
869
877
  /(ow(?:19|20)?we?[1-3]{1,3})/i // Oppo Watch
870
878
  ], [MODEL, [VENDOR, OPPO], [TYPE, WEARABLE]], [
@@ -939,7 +947,7 @@
939
947
  /(presto)\/([\w\.]+)/i, // Presto
940
948
  /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna|servo)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna/Servo
941
949
  /ekioh(flow)\/([\w\.]+)/i, // Flow
942
- /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links
950
+ /(khtml|tasman|links|dillo)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links/Dillo
943
951
  /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab
944
952
 
945
953
  /\b(libweb)/i // LibWeb
@@ -968,7 +976,8 @@
968
976
 
969
977
  // iOS/macOS
970
978
  /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
971
- /(?:ios;fbsv|ios(?=.+ip(?:ad|hone))|ip(?:ad|hone)(?: |.+i(?:pad)?)os)[\/ ]([\w\.]+)/i,
979
+ /(?:ios;fbsv|ios(?=.+ip(?:ad|hone)|.+apple ?tv)|ip(?:ad|hone)(?: |.+i(?:pad)?)os|apple ?tv.+ios)[\/ ]([\w\.]+)/i,
980
+ /\btvos ?([\w\.]+)/i,
972
981
  /cfnetwork\/.+darwin/i
973
982
  ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
974
983
  /(mac os x) ?([\w\. ]*)/i,
@@ -1000,7 +1009,7 @@
1000
1009
  ], [VERSION, [NAME, BLACKBERRY]], [
1001
1010
  /(?:symbian ?os|symbos|s60(?=;)|series ?60)[-\/ ]?([\w\.]*)/i // Symbian
1002
1011
  ], [VERSION, [NAME, 'Symbian']], [
1003
- /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS
1012
+ /mozilla\/[\d\.]+ \((?:mobile[;\w ]*|tablet|tv|[^\)]*(?:viera|lg(?:l25|-d300)|alcatel ?o.+|y300-f1)); rv:([\w\.]+)\).+gecko\//i // Firefox OS
1004
1013
  ], [VERSION, [NAME, FIREFOX+' OS']], [
1005
1014
  /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS
1006
1015
  /webos(?:[ \/]?|\.tv-20(?=2[2-9]))(\d[\d\.]*)/i
@@ -1008,15 +1017,16 @@
1008
1017
  /web0s;.+?(?:chr[o0]me|safari)\/(\d+)/i
1009
1018
  // https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine
1010
1019
  ], [[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']], [
1011
- /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS
1020
+ /watch(?: ?os[,\/ ]|\d,\d\/)([\d\.]+)/i // watchOS
1012
1021
  ], [VERSION, [NAME, 'watchOS']], [
1013
1022
 
1014
1023
  // Google ChromeOS
1015
- /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS
1016
- ], [[NAME, "Chrome OS"], VERSION],[
1024
+ /cros [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS
1025
+ ], [VERSION, [NAME, 'Chrome OS']],[
1017
1026
 
1018
1027
  // Smart TVs
1019
- /panasonic;(viera)/i, // Panasonic Viera
1028
+ /kepler ([\w\.]+); (aft|aeo)/i // Vega OS
1029
+ ], [VERSION, [NAME, 'Vega OS']],[
1020
1030
  /(netrange)mmh/i, // Netrange
1021
1031
  /(nettv)\/(\d+\.[\w\.]+)/i, // NetTV
1022
1032
 
@@ -1142,25 +1152,25 @@
1142
1152
  };
1143
1153
  }
1144
1154
 
1145
- if (!NAVIGATOR_UADATA) {
1146
- IData.prototype.then = function (cb) {
1147
- var that = this;
1148
- var IDataResolve = function () {
1149
- for (var prop in that) {
1150
- if (that.hasOwnProperty(prop)) {
1151
- this[prop] = that[prop];
1152
- }
1155
+ IData.prototype.then = function (cb) {
1156
+ var that = this;
1157
+ var IDataResolve = function () {
1158
+ for (var prop in that) {
1159
+ if (that.hasOwnProperty(prop)) {
1160
+ this[prop] = that[prop];
1153
1161
  }
1154
- };
1155
- IDataResolve.prototype = {
1156
- is : IData.prototype.is,
1157
- toString : IData.prototype.toString
1158
- };
1159
- var resolveData = new IDataResolve();
1160
- cb(resolveData);
1161
- return resolveData;
1162
+ }
1162
1163
  };
1163
- }
1164
+ IDataResolve.prototype = {
1165
+ is : IData.prototype.is,
1166
+ toString : IData.prototype.toString,
1167
+ withClientHints : IData.prototype.withClientHints,
1168
+ withFeatureCheck : IData.prototype.withFeatureCheck
1169
+ };
1170
+ var resolveData = new IDataResolve();
1171
+ cb(resolveData);
1172
+ return resolveData;
1173
+ };
1164
1174
 
1165
1175
  return new IData();
1166
1176
  };
@@ -1192,199 +1202,197 @@
1192
1202
  }
1193
1203
 
1194
1204
  function UAItem (itemType, ua, rgxMap, uaCH) {
1205
+ setProps.call(this, [
1206
+ ['itemType', itemType],
1207
+ ['ua', ua],
1208
+ ['uaCH', uaCH],
1209
+ ['rgxMap', rgxMap],
1210
+ ['data', createIData(this, itemType)]
1211
+ ]);
1212
+ return this;
1213
+ }
1195
1214
 
1196
- this.get = function (prop) {
1197
- if (!prop) return this.data;
1198
- return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
1199
- };
1200
-
1201
- this.set = function (prop, val) {
1202
- this.data[prop] = val;
1203
- return this;
1204
- };
1215
+ UAItem.prototype.get = function (prop) {
1216
+ if (!prop) return this.data;
1217
+ return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
1218
+ };
1205
1219
 
1206
- this.setCH = function (ch) {
1207
- this.uaCH = ch;
1208
- return this;
1209
- };
1220
+ UAItem.prototype.set = function (prop, val) {
1221
+ this.data[prop] = val;
1222
+ return this;
1223
+ };
1210
1224
 
1211
- this.detectFeature = function () {
1212
- if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
1213
- switch (this.itemType) {
1214
- case BROWSER:
1215
- // Brave-specific detection
1216
- if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
1217
- this.set(NAME, 'Brave');
1218
- }
1219
- break;
1220
- case DEVICE:
1221
- // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
1222
- if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
1223
- this.set(TYPE, MOBILE);
1224
- }
1225
- // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
1226
- if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
1227
- this.set(MODEL, 'iPad')
1228
- .set(TYPE, TABLET);
1229
- }
1230
- break;
1231
- case OS:
1232
- // Chrome-specific detection: check for 'platform' value of navigator.userAgentData
1233
- if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
1234
- this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
1235
- }
1236
- break;
1237
- case RESULT:
1238
- var data = this.data;
1239
- var detect = function (itemType) {
1240
- return data[itemType]
1241
- .getItem()
1242
- .detectFeature()
1243
- .get();
1244
- };
1245
- this.set(BROWSER, detect(BROWSER))
1246
- .set(CPU, detect(CPU))
1247
- .set(DEVICE, detect(DEVICE))
1248
- .set(ENGINE, detect(ENGINE))
1249
- .set(OS, detect(OS));
1250
- }
1251
- }
1252
- return this;
1253
- };
1225
+ UAItem.prototype.setCH = function (ch) {
1226
+ this.uaCH = ch;
1227
+ return this;
1228
+ };
1254
1229
 
1255
- this.parseUA = function () {
1256
- if (this.itemType != RESULT) {
1257
- rgxMapper.call(this.data, this.ua, this.rgxMap);
1258
- }
1230
+ UAItem.prototype.detectFeature = function () {
1231
+ if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
1259
1232
  switch (this.itemType) {
1260
1233
  case BROWSER:
1261
- this.set(MAJOR, majorize(this.get(VERSION)));
1262
- break;
1263
- case OS:
1264
- if (this.get(NAME) == 'iOS' && this.get(VERSION) == '18.6') {
1265
- // Based on the assumption that iOS version is tightly coupled with Safari version
1266
- var realVersion = /\) Version\/([\d\.]+)/.exec(this.ua); // Get Safari version
1267
- if (realVersion && parseInt(realVersion[1].substring(0,2), 10) >= 26) {
1268
- this.set(VERSION, realVersion[1]); // Set as iOS version
1269
- }
1270
- }
1271
- break;
1272
- }
1273
- return this;
1274
- };
1275
-
1276
- this.parseCH = function () {
1277
- var uaCH = this.uaCH,
1278
- rgxMap = this.rgxMap;
1279
-
1280
- switch (this.itemType) {
1281
- case BROWSER:
1282
- case ENGINE:
1283
- var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
1284
- if (brands) {
1285
- for (var i=0; i<brands.length; i++) {
1286
- var brandName = brands[i].brand || brands[i],
1287
- brandVersion = brands[i].version;
1288
- if (this.itemType == BROWSER &&
1289
- !/not.a.brand/i.test(brandName) &&
1290
- (!prevName ||
1291
- (/Chrom/.test(prevName) && brandName != CHROMIUM) ||
1292
- (prevName == EDGE && /WebView2/.test(brandName))
1293
- )) {
1294
- brandName = strMapper(brandName, browserHintsMap);
1295
- prevName = this.get(NAME);
1296
- if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) {
1297
- this.set(NAME, brandName)
1298
- .set(VERSION, brandVersion)
1299
- .set(MAJOR, majorize(brandVersion));
1300
- }
1301
- prevName = brandName;
1302
- }
1303
- if (this.itemType == ENGINE && brandName == CHROMIUM) {
1304
- this.set(VERSION, brandVersion);
1305
- }
1306
- }
1307
- }
1308
- break;
1309
- case CPU:
1310
- var archName = uaCH[ARCHITECTURE];
1311
- if (archName) {
1312
- if (archName && uaCH[BITNESS] == '64') archName += '64';
1313
- rgxMapper.call(this.data, archName + ';', rgxMap);
1234
+ // Brave-specific detection
1235
+ if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
1236
+ this.set(NAME, 'Brave');
1314
1237
  }
1315
1238
  break;
1316
1239
  case DEVICE:
1317
- if (uaCH[MOBILE]) {
1240
+ // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
1241
+ if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
1318
1242
  this.set(TYPE, MOBILE);
1319
1243
  }
1320
- if (uaCH[MODEL]) {
1321
- this.set(MODEL, uaCH[MODEL]);
1322
- if (!this.get(TYPE) || !this.get(VENDOR)) {
1323
- var reParse = {};
1324
- rgxMapper.call(reParse, 'droid 9; ' + uaCH[MODEL] + ')', rgxMap);
1325
- if (!this.get(TYPE) && !!reParse.type) {
1326
- this.set(TYPE, reParse.type);
1327
- }
1328
- if (!this.get(VENDOR) && !!reParse.vendor) {
1329
- this.set(VENDOR, reParse.vendor);
1330
- }
1331
- }
1332
- }
1333
- if (uaCH[FORMFACTORS]) {
1334
- var ff;
1335
- if (typeof uaCH[FORMFACTORS] !== 'string') {
1336
- var idx = 0;
1337
- while (!ff && idx < uaCH[FORMFACTORS].length) {
1338
- ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap);
1339
- }
1340
- } else {
1341
- ff = strMapper(uaCH[FORMFACTORS], formFactorsMap);
1342
- }
1343
- this.set(TYPE, ff);
1244
+ // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
1245
+ if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
1246
+ this.set(MODEL, 'iPad')
1247
+ .set(TYPE, TABLET);
1344
1248
  }
1345
1249
  break;
1346
1250
  case OS:
1347
- var osName = uaCH[PLATFORM];
1348
- if(osName) {
1349
- var osVersion = uaCH[PLATFORMVER];
1350
- if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
1351
- this.set(NAME, osName)
1352
- .set(VERSION, osVersion);
1251
+ // Chrome-specific detection: check for 'platform' value of navigator.userAgentData
1252
+ if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
1253
+ this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
1353
1254
  }
1354
- // Xbox-Specific Detection
1355
- if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') {
1356
- this.set(NAME, 'Xbox')
1357
- .set(VERSION, undefined);
1358
- }
1359
1255
  break;
1360
1256
  case RESULT:
1361
1257
  var data = this.data;
1362
- var parse = function (itemType) {
1258
+ var detect = function (itemType) {
1363
1259
  return data[itemType]
1364
1260
  .getItem()
1365
- .setCH(uaCH)
1366
- .parseCH()
1261
+ .detectFeature()
1367
1262
  .get();
1368
1263
  };
1369
- this.set(BROWSER, parse(BROWSER))
1370
- .set(CPU, parse(CPU))
1371
- .set(DEVICE, parse(DEVICE))
1372
- .set(ENGINE, parse(ENGINE))
1373
- .set(OS, parse(OS));
1264
+ this.set(BROWSER, detect(BROWSER))
1265
+ .set(CPU, detect(CPU))
1266
+ .set(DEVICE, detect(DEVICE))
1267
+ .set(ENGINE, detect(ENGINE))
1268
+ .set(OS, detect(OS));
1374
1269
  }
1375
- return this;
1376
- };
1270
+ }
1271
+ return this;
1272
+ };
1377
1273
 
1378
- setProps.call(this, [
1379
- ['itemType', itemType],
1380
- ['ua', ua],
1381
- ['uaCH', uaCH],
1382
- ['rgxMap', rgxMap],
1383
- ['data', createIData(this, itemType)]
1384
- ]);
1274
+ UAItem.prototype.parseUA = function () {
1275
+ if (this.itemType != RESULT) {
1276
+ rgxMapper.call(this.data, this.ua, this.rgxMap);
1277
+ }
1278
+ switch (this.itemType) {
1279
+ case BROWSER:
1280
+ this.set(MAJOR, majorize(this.get(VERSION)));
1281
+ break;
1282
+ case OS:
1283
+ if (this.get(NAME) == 'iOS' && this.get(VERSION) == '18.6') {
1284
+ // Based on the assumption that iOS version is tightly coupled with Safari version
1285
+ var realVersion = /\) Version\/([\d\.]+)/.exec(this.ua); // Get Safari version
1286
+ if (realVersion && parseInt(realVersion[1].substring(0,2), 10) >= 26) {
1287
+ this.set(VERSION, realVersion[1]); // Set as iOS version
1288
+ }
1289
+ }
1290
+ break;
1291
+ }
1292
+ return this;
1293
+ };
1385
1294
 
1295
+ UAItem.prototype.parseCH = function () {
1296
+ var uaCH = this.uaCH,
1297
+ rgxMap = this.rgxMap;
1298
+
1299
+ switch (this.itemType) {
1300
+ case BROWSER:
1301
+ case ENGINE:
1302
+ var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
1303
+ if (brands) {
1304
+ for (var i=0; i<brands.length; i++) {
1305
+ var brandName = brands[i].brand || brands[i],
1306
+ brandVersion = brands[i].version;
1307
+ if (this.itemType == BROWSER &&
1308
+ !/not.a.brand/i.test(brandName) &&
1309
+ (!prevName ||
1310
+ (/Chrom/.test(prevName) && brandName != CHROMIUM) ||
1311
+ (prevName == EDGE && /WebView2/.test(brandName))
1312
+ )) {
1313
+ brandName = strMapper(brandName, browserHintsMap);
1314
+ prevName = this.get(NAME);
1315
+ if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) {
1316
+ this.set(NAME, brandName)
1317
+ .set(VERSION, brandVersion)
1318
+ .set(MAJOR, majorize(brandVersion));
1319
+ }
1320
+ prevName = brandName;
1321
+ }
1322
+ if (this.itemType == ENGINE && brandName == CHROMIUM) {
1323
+ this.set(VERSION, brandVersion);
1324
+ }
1325
+ }
1326
+ }
1327
+ break;
1328
+ case CPU:
1329
+ var archName = uaCH[ARCHITECTURE];
1330
+ if (archName) {
1331
+ if (archName && uaCH[BITNESS] == '64') archName += '64';
1332
+ rgxMapper.call(this.data, archName + ';', rgxMap);
1333
+ }
1334
+ break;
1335
+ case DEVICE:
1336
+ if (uaCH[MOBILE]) {
1337
+ this.set(TYPE, MOBILE);
1338
+ }
1339
+ if (uaCH[MODEL]) {
1340
+ this.set(MODEL, uaCH[MODEL]);
1341
+ if (!this.get(TYPE) || !this.get(VENDOR)) {
1342
+ var reParse = {};
1343
+ rgxMapper.call(reParse, 'droid 9; ' + uaCH[MODEL] + ')', rgxMap);
1344
+ if (!this.get(TYPE) && !!reParse.type) {
1345
+ this.set(TYPE, reParse.type);
1346
+ }
1347
+ if (!this.get(VENDOR) && !!reParse.vendor) {
1348
+ this.set(VENDOR, reParse.vendor);
1349
+ }
1350
+ }
1351
+ }
1352
+ if (uaCH[FORMFACTORS]) {
1353
+ var ff;
1354
+ if (typeof uaCH[FORMFACTORS] !== 'string') {
1355
+ var idx = 0;
1356
+ while (!ff && idx < uaCH[FORMFACTORS].length) {
1357
+ ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap);
1358
+ }
1359
+ } else {
1360
+ ff = strMapper(uaCH[FORMFACTORS], formFactorsMap);
1361
+ }
1362
+ this.set(TYPE, ff);
1363
+ }
1364
+ break;
1365
+ case OS:
1366
+ var osName = uaCH[PLATFORM];
1367
+ if(osName) {
1368
+ var osVersion = uaCH[PLATFORMVER];
1369
+ if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
1370
+ this.set(NAME, osName)
1371
+ .set(VERSION, osVersion);
1372
+ }
1373
+ // Xbox-Specific Detection
1374
+ if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') {
1375
+ this.set(NAME, 'Xbox')
1376
+ .set(VERSION, undefined);
1377
+ }
1378
+ break;
1379
+ case RESULT:
1380
+ var data = this.data;
1381
+ var parse = function (itemType) {
1382
+ return data[itemType]
1383
+ .getItem()
1384
+ .setCH(uaCH)
1385
+ .parseCH()
1386
+ .get();
1387
+ };
1388
+ this.set(BROWSER, parse(BROWSER))
1389
+ .set(CPU, parse(CPU))
1390
+ .set(DEVICE, parse(DEVICE))
1391
+ .set(ENGINE, parse(ENGINE))
1392
+ .set(OS, parse(OS));
1393
+ }
1386
1394
  return this;
1387
- }
1395
+ };
1388
1396
 
1389
1397
  function UAParser (ua, extensions, headers) {
1390
1398