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