ua-parser-js 2.0.9 → 2.0.10

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/extensions/ua-parser-extensions.js
4
4
 
5
5
  ///////////////////////////////////////////////
6
- /* Extensions for UAParser.js v2.0.9
6
+ /* Extensions for UAParser.js v2.0.10
7
7
  https://github.com/faisalman/ua-parser-js
8
8
  Author: Faisal Salman <f@faisalman.com>
9
9
  AGPLv3 License */
@@ -59,6 +59,7 @@ const Crawlers = Object.freeze({
59
59
  [
60
60
  // AhrefsBot - https://ahrefs.com/robot
61
61
  // Amazonbot - https://developer.amazon.com/amazonbot
62
+ // Awario - https://awario.com/bots.html
62
63
  // Bingbot / AdIdxBot - https://www.bing.com/webmasters/help/which-crawlers-does-bing-use-8c184ec0
63
64
  // Bravebot - https://search.brave.com/help/brave-search-crawler
64
65
  // CCBot - https://commoncrawl.org/faq
@@ -83,7 +84,7 @@ const Crawlers = Object.freeze({
83
84
  // SurdotlyBot - http://sur.ly/bot.html
84
85
  // Swiftbot - https://swiftype.com/swiftbot
85
86
  // YepBot - https://yep.com/yepbot/
86
- /((?:adidx|ahrefs|amazon|bing|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|sbintuitions|semrush|seznam|surdotly|swift|yep)bot)\/([\w\.-]+)/i,
87
+ /((?:adidx|ahrefs|amazon|(?:amzn|oai)-search|awario(?:smart|rss)?|bing|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|onespot-scraper|perplexity|sbintuitions|semrush|seznam|surdotly|swift|yep)bot)\/([\w\.-]+)/i,
87
88
 
88
89
  // Algolia Crawler
89
90
  /(algolia crawler(?: renderscript)?)\/?([\w\.]*)/i,
@@ -100,12 +101,12 @@ const Crawlers = Object.freeze({
100
101
  // Coc Coc Bot - https://help.coccoc.com/en/search-engine
101
102
  /(coccocbot-(?:image|web))\/([\w\.]+)/i,
102
103
 
103
- // Daum
104
- /(daum(?:oa)?(?:-image)?)[ \/]([\w\.]+)/i,
104
+ // Daum / HubSpot Crawler
105
+ /(daum(?:oa)?(?:-image)?|hubspot crawler)[ \/]([\w\.]+)/i,
105
106
 
106
107
  // Facebook / Meta
107
108
  // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers
108
- /(facebook(?:externalhit|catalog)|meta-externalagent)\/([\w\.]+)/i,
109
+ /(facebook(?:externalhit|catalog)|meta-(?:externalagent|externalads|webindexer))\/([\w\.]+)/i,
109
110
 
110
111
  // Googlebot - http://www.google.com/bot.html
111
112
  /(google(?:bot|other|-inspectiontool)(?:-image|-video|-news)?|storebot-google)\/?([\w\.]*)/i,
@@ -134,13 +135,19 @@ const Crawlers = Object.freeze({
134
135
  // Yeti (Naver)
135
136
  /(yeti)\/([\w\.]+)/i,
136
137
 
137
- // aiHitBot / Algolia Crawler / BLEXBot / Cloudflare AutoRAG / Diffbot / FirecrawlAgent / HuggingFace-Bot / Linespider / MSNBot / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / PanguBot / Replicate-Bot / RunPod-Bot / Webzio-Extended / Screaming Frog SEO Spider / Startpage / Timpibot / Together-Bot / VelenPublicWebCrawler / xAI-Bot / YisouSpider / YouBot / ZumBot
138
+ // aiHitBot / Algolia Crawler / Audisto Crawler / BLEXBot / BrightEdge Crawler / Cloudflare AutoRAG / Diffbot / FirecrawlAgent / HuggingFace-Bot / Linespider / MSNBot / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / PanguBot / Replicate-Bot / RunPod-Bot / Webzio-Extended / Screaming Frog SEO Spider / Startpage / Timpibot / Together-Bot / VelenPublicWebCrawler / xAI-Bot / YisouSpider / YouBot / ZumBot
138
139
  // Cotoyogi - https://ds.rois.ac.jp/en_center8/en_crawler/
139
140
  // Freespoke - https://docs.freespoke.com/search/bot/
140
- /((?:aihit|blex|diff|huggingface-|msn|pangu|replicate-|runpod-|timpi|together-|xai-|you|zum)bot|(?:magpie-|velenpublicweb)crawler|(?:chatglm-|line|screaming frog seo |yisou)spider|cloudflare-autorag|cotoyogi|(?:firecrawl|twin)agent|freespoke|omgili(?:bot)?|openai image downloader|startpageprivateimageproxy|webzio-extended)\/?([\w\.]*)/i
141
+ /((?:aihit|blex|diff|huggingface-|msn|pangu|replicate-|runpod-|timpi|together-|xai-|you|zum)bot|(?:audisto |brightedge |magpie-|velenpublicweb)crawler|(?:chatglm-|line|screaming frog seo |yisou)spider|cloudflare-autorag|cotoyogi|(?:firecrawl|twin)agent|freespoke|omgili(?:bot)?|openai image downloader|startpageprivateimageproxy|webzio-extended)\/?([\w\.]*)/i
141
142
  ],
142
143
  [NAME, VERSION, [TYPE, CRAWLER]],
143
144
 
145
+ [
146
+ // Headline - https://headline.com/legal/crawler
147
+ /(ev-crawler)\/([\w\.]+)/i
148
+ ],
149
+ [[NAME, 'Headline'], VERSION, [TYPE, CRAWLER]],
150
+
144
151
  [
145
152
  // YandexBot MirrorDetector
146
153
  /(yandexbot\/([\w\.]+); mirrordetector)/i
@@ -160,7 +167,7 @@ const Crawlers = Object.freeze({
160
167
  // v0bot - https://vercel.com/docs/bot-management
161
168
  // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp
162
169
  // Botify / Bytespider / DeepSeekBot / Qihoo 360Spider / SeekportBot / TikTokSpider
163
- /\b((ai2|aspiegel|atlassian-|dataforseo|deepseek|imagesift|petal|seekport|turnitin|v0)bot|360spider-?(image|video)?|baidu-ads|botify|(byte|tiktok)spider|cohere-training-data-crawler|elastic(?=\/s)|marginalia|siteimprove(?=bot|\.com)|teoma|webzio|yahoo! slurp)/i
170
+ /\b((ai2|aspiegel|atlassian-|dataforseo|deepseek|imagesift|petal|seekport|turnitin|v0|yacy)bot|360spider-?(image|video)?|baidu-ads|botify|(byte|tiktok)spider|cohere-training-data-crawler|elastic(?=\/s)|marginalia|proximic|siteimprove(?=bot|\.com)|teoma|webzio|yahoo! slurp)/i
164
171
  ],
165
172
  [NAME, [TYPE, CRAWLER]]
166
173
  ]
@@ -310,17 +317,21 @@ const Fetchers = Object.freeze({
310
317
  // Buffer Link Preview Bot - https://scraper.buffer.com/about/bots/link-preview-bot
311
318
  // ChatGPT-User - https://platform.openai.com/docs/plugins/bot
312
319
  // DuckAssistBot - https://duckduckgo.com/duckassistbot/
320
+ // Feedly - https://feedly.com/fetcher.html
313
321
  // FlipboardProxy - https://about.flipboard.com/proxy-service/
314
322
  // Google Site Verifier / Meta / Yahoo! Japan
315
323
  // Iframely - https://iframely.com/docs/about
316
324
  // Perplexity-User - https://docs.perplexity.ai/guides/bots
317
325
  // MistralAI-User - https://docs.mistral.ai/robots/
318
326
  // Yandex Bots - https://yandex.com/bots
319
- /(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|cohere-ai|flipboardproxy|hubspot page fetcher|mastodon|(?:bitly|bufferlinkpreview|discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero|zoom)bot|google-site-verification|iframely|kakaotalk-scrap|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|fordomain|pagechecker|searchshop)|yadirectfetcher|whatsapp)\/([\w\.]+)/i,
327
+ /(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:amzn|chatgpt|claude|mistralai|perplexity)-user|cohere-ai|flipboardproxy|hubspot page fetcher|mastodon|(?:bitly|bufferlinkpreview|discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptime(?:ro)?|zoom)bot|google-site-verification|iframely|kakaotalk-scrap|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|fordomain|pagechecker|searchshop)|yadirectfetcher|whatsapp)\/([\w\.]+)/i,
320
328
 
321
329
  // Bluesky
322
330
  /(bluesky) cardyb\/([\w\.]+)/i,
323
331
 
332
+ // Feedly
333
+ /(feedly)(?:bot)?\/([\w\.]+)/i,
334
+
324
335
  // Nova Act - https://github.com/aws/nova-act
325
336
  /agent-(novaact)\/([\w\.]+)/i,
326
337
 
@@ -334,7 +345,7 @@ const Fetchers = Object.freeze({
334
345
 
335
346
  [
336
347
  // Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / KeybaseBot / Snapchat / Vercelbot / Yandex Bots
337
- /((?:better uptime |keybase|telegram|vercel)bot|lighthouse$|feedfetcher-google|gemini-deep-research|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|vercel(flags|tracing|-(favicon|screenshot)-bot)|yandex(?:sitelinks|userproxy))/i
348
+ /((?:better uptime |keybase|telegram|vercel)bot|lighthouse$|feedfetcher-google|gemini-deep-research|google(?:docs|imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|vercel(flags|tracing|-(favicon|screenshot)-bot)|virustotal(?=cloud)|yandex(?:sitelinks|userproxy))/i
338
349
  ],
339
350
  [NAME, [TYPE, FETCHER]],
340
351
  ],
@@ -444,7 +455,7 @@ const Libraries = Object.freeze({
444
455
  /(nutch)-([\w\.-]+)(\(|$)/i,
445
456
  /\((java)\/([\w\.]+)/i
446
457
  ], [NAME, VERSION, [TYPE, LIBRARY]], [
447
- /(node-fetch|undici)/i
458
+ /(node-fetch|phpcrawl|undici)/i
448
459
  ], [NAME, [TYPE, LIBRARY]]
449
460
  ]
450
461
  });
@@ -1,4 +1,4 @@
1
- // Type definitions for Helpers submodule of UAParser.js v2.0.9
1
+ // Type definitions for Helpers submodule of UAParser.js v2.0.10
2
2
  // Project: https://github.com/faisalman/ua-parser-js
3
3
  // Definitions by: Faisal Salman <https://github.com/faisalman>
4
4
 
@@ -1,5 +1,5 @@
1
1
  ///////////////////////////////////////////////
2
- /* Helpers for UAParser.js v2.0.9
2
+ /* Helpers for UAParser.js v2.0.10
3
3
  https://github.com/faisalman/ua-parser-js
4
4
  Author: Faisal Salman <f@faisalman.com>
5
5
  AGPLv3 License */
@@ -41,7 +41,7 @@ const isChromeFamily = _isChromeFamily;
41
41
  /**
42
42
  * @deprecated Moved to `browser-detection` submodule
43
43
  */
44
- const isElectron = () => _isElectron;
44
+ const isElectron = _isElectron;
45
45
 
46
46
  /**
47
47
  * @deprecated Moved to `browser-detection` submodule
@@ -3,7 +3,7 @@
3
3
  // Source: /src/helpers/ua-parser-helpers.js
4
4
 
5
5
  ///////////////////////////////////////////////
6
- /* Helpers for UAParser.js v2.0.9
6
+ /* Helpers for UAParser.js v2.0.10
7
7
  https://github.com/faisalman/ua-parser-js
8
8
  Author: Faisal Salman <f@faisalman.com>
9
9
  AGPLv3 License */
@@ -45,7 +45,7 @@ const isChromeFamily = _isChromeFamily;
45
45
  /**
46
46
  * @deprecated Moved to `browser-detection` submodule
47
47
  */
48
- const isElectron = () => _isElectron;
48
+ const isElectron = _isElectron;
49
49
 
50
50
  /**
51
51
  * @deprecated Moved to `browser-detection` submodule
@@ -1,10 +1,15 @@
1
- // Type definitions for UAParser.js v2.0.9
1
+ // Type definitions for UAParser.js v2.0.10
2
2
  // Project: https://github.com/faisalman/ua-parser-js
3
3
  // Definitions by: Faisal Salman <https://github.com/faisalman>
4
4
 
5
5
  import { BrowserType, CPUArch, DeviceType, EngineName } from "../enums/ua-parser-enums";
6
6
 
7
7
  declare namespace UAParser {
8
+
9
+ type BrowserTypes = typeof BrowserType[keyof typeof BrowserType];
10
+ type CPUArchs = typeof CPUArch[keyof typeof CPUArch];
11
+ type DeviceTypes = typeof DeviceType[keyof typeof DeviceType];
12
+ type EngineNames = typeof EngineName[keyof typeof EngineName];
8
13
 
9
14
  interface IData<T> {
10
15
  is(val: string): boolean;
@@ -17,21 +22,21 @@ declare namespace UAParser {
17
22
  name?: string;
18
23
  version?: string;
19
24
  major?: string;
20
- type?: typeof BrowserType[keyof typeof BrowserType];
25
+ type?: BrowserTypes;
21
26
  }
22
27
 
23
28
  interface ICPU extends IData<ICPU> {
24
- architecture?: typeof CPUArch[keyof typeof CPUArch];
29
+ architecture?: CPUArchs;
25
30
  }
26
31
 
27
32
  interface IDevice extends IData<IDevice> {
28
- type?: typeof DeviceType[keyof typeof DeviceType];
33
+ type?: DeviceTypes;
29
34
  vendor?: string;
30
35
  model?: string;
31
36
  }
32
37
 
33
38
  interface IEngine extends IData<IEngine> {
34
- name?: typeof EngineName[keyof typeof EngineName];
39
+ name?: EngineNames;
35
40
  version?: string;
36
41
  }
37
42
 
@@ -105,6 +110,7 @@ declare namespace UAParser {
105
110
  getOS(): IOS;
106
111
  getResult(): IResult;
107
112
  setUA(uastring: string): UAParser;
113
+ useExtension(extensions: UAParserExt): UAParser;
108
114
  }
109
115
  }
110
116
 
@@ -1,5 +1,5 @@
1
1
  /////////////////////////////////////////////////////////////////////////////////
2
- /* UAParser.js v2.0.9
2
+ /* UAParser.js v2.0.10
3
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.
@@ -19,7 +19,7 @@
19
19
  // Constants
20
20
  /////////////
21
21
 
22
- var LIBVERSION = '2.0.9',
22
+ var LIBVERSION = '2.0.10',
23
23
  UA_MAX_LENGTH = 500,
24
24
  USER_AGENT = 'user-agent',
25
25
  EMPTY = '',
@@ -170,7 +170,7 @@
170
170
  itemListToArray = function (header) {
171
171
  if (!header) return undefined;
172
172
  var arr = [];
173
- var tokens = strip(/\\?\"/g, header).split(',');
173
+ var tokens = normalizeHeaderValue(header).split(',');
174
174
  for (var i = 0; i < tokens.length; i++) {
175
175
  if (tokens[i].indexOf(';') > -1) {
176
176
  var token = trim(tokens[i]).split(';v=');
@@ -187,6 +187,9 @@
187
187
  majorize = function (version) {
188
188
  return isString(version) ? strip(/[^\d\.]/g, version).split('.')[0] : undefined;
189
189
  },
190
+ normalizeHeaderValue = function (str) {
191
+ return isString(str) ? trim(strip(/\\?\"/g, str), UA_MAX_LENGTH) : undefined;
192
+ },
190
193
  setProps = function (arr) {
191
194
  for (var i in arr) {
192
195
  if (!arr.hasOwnProperty(i)) continue;
@@ -203,9 +206,6 @@
203
206
  strip = function (pattern, str) {
204
207
  return isString(str) ? str.replace(pattern, EMPTY) : str;
205
208
  },
206
- stripQuotes = function (str) {
207
- return strip(/\\?\"/g, str);
208
- },
209
209
  trim = function (str, len) {
210
210
  str = strip(/^\s\s*/, String(str));
211
211
  return typeof len === TYPEOF.UNDEFINED ? str : str.substring(0, len);
@@ -278,6 +278,10 @@
278
278
  }
279
279
  },
280
280
 
281
+ strTest = function (str, map) {
282
+ return map.test.test(str) ? map.ifTrue : map.ifFalse;
283
+ },
284
+
281
285
  strMapper = function (str, map) {
282
286
 
283
287
  for (var i in map) {
@@ -348,7 +352,7 @@
348
352
  /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
349
353
  ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [
350
354
  /webview.+edge\/([\w\.]+)/i // Microsoft Edge
351
- ], [VERSION, [NAME, EDGE+' WebView']], [
355
+ ], [VERSION, [NAME, EDGE+' WebView'], [TYPE, INAPP]], [
352
356
  /edg(?:e|ios|a)?\/([\w\.]+)/i
353
357
  ], [VERSION, [NAME, 'Edge']], [
354
358
 
@@ -388,7 +392,7 @@
388
392
  ], [VERSION, [NAME, 'Quark']], [
389
393
  /\bddg\/([\w\.]+)/i // DuckDuckGo
390
394
  ], [VERSION, [NAME, 'DuckDuckGo']], [
391
- /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser
395
+ /(?:\buc? ?browser|(?:juc.+)ucweb| ucpc)[\/ ]?([\w\.]+)/i // UCBrowser
392
396
  ], [VERSION, [NAME, 'UCBrowser']], [
393
397
  /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser
394
398
  /\bqbcore\/([\w\.]+).+microm/i,
@@ -427,7 +431,9 @@
427
431
  /\b(qq)\/([\w\.]+)/i // QQ
428
432
  ], [[NAME, /(.+)/, '$1Browser'], VERSION], [
429
433
  /(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i
430
- ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser
434
+ ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/VivoBrowser/PicoBrowser
435
+ / HBPC\/([\w\.]+)/ // Huawei Browser
436
+ ], [VERSION, [NAME, HUAWEI + SUFFIX_BROWSER]], [
431
437
  /samsungbrowser\/([\w\.]+)/i // Samsung Internet
432
438
  ], [VERSION, [NAME, SAMSUNG + ' Internet']], [
433
439
  /metasr[\/ ]?([\d\.]+)/i // Sogou Explorer
@@ -475,10 +481,10 @@
475
481
  ], [VERSION, [NAME, CHROME+' Headless']], [
476
482
 
477
483
  /wv\).+chrome\/([\w\.]+).+edgw\//i // Edge WebView2
478
- ], [VERSION, [NAME, EDGE+' WebView2']], [
484
+ ], [VERSION, [NAME, EDGE+' WebView2'], [TYPE, INAPP]], [
479
485
 
480
- / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
481
- ], [[NAME, CHROME+' WebView'], VERSION], [
486
+ /; wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
487
+ ], [[NAME, CHROME+' WebView'], VERSION, [TYPE, INAPP]], [
482
488
 
483
489
  /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser
484
490
  ], [VERSION, [NAME, 'Android' + SUFFIX_BROWSER]], [
@@ -612,7 +618,7 @@
612
618
  /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
619
  // Xiaomi Mi
614
620
  /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i,
615
- / ([\w ]+) miui\/v?\d/i
621
+ /; ([\w ]+) miui\/v?\d/i
616
622
  ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
617
623
 
618
624
  // OnePlus
@@ -767,10 +773,19 @@
767
773
  /; (ac[3-6]\d\w{2,8})( b|\))/i
768
774
  ], [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]], [
769
775
 
776
+ // Blackview
777
+ /blackview ([-\w ]+)( b|\))/i,
778
+ /; (bv\d{4}[-\w ]*)( b|\))/i
779
+ ], [MODEL, [VENDOR, 'Blackview'], [TYPE, MOBILE]], [
780
+
770
781
  // HMD
771
782
  /; (n159v)/i
772
783
  ], [MODEL, [VENDOR, 'HMD'], [TYPE, MOBILE]], [
773
784
 
785
+ // T-Mobile
786
+ /((revvl[ \w\+]+|tm(?:rv|af)\w*[45]g(?:tb)?))( b|\))/i
787
+ ], [MODEL, [TYPE, strTest, { 'test': /ta?b/i, 'ifTrue': TABLET, 'ifFalse': MOBILE }], [VENDOR, 'T-Mobile']], [
788
+
774
789
  // MIXED
775
790
  /(imo) (tab \w+)/i, // IMO
776
791
  /(infinix|tecno) (x1101b?|p904|dp(7c|8d|10a)( pro)?|p70[1-3]a?|p904|t1101)/i // Infinix XPad / Tecno
@@ -778,8 +793,8 @@
778
793
 
779
794
  /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i,
780
795
  // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan
781
- // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL/Wiko
782
- /; (blu|hmd|imo|infinix|lava|oneplus|tcl|wiko)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i,
796
+ // BLU/Coolpad/CUBOT/HMD/IMO/Infinix/Lava/OnePlus/TCL/Wiko
797
+ /; (blu|coolpad|cubot|hmd|imo|infinix|lava|oneplus|tcl|wiko)[_ ]([-\w\+ ]+?)(?: bui|\)|; r)/i,
783
798
  /(hp) ([\w ]+\w)/i, // HP iPAQ
784
799
  /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia
785
800
  /(oppo) ?([\w ]+) bui/i, // OPPO
@@ -1187,12 +1202,12 @@
1187
1202
  [BRANDS, itemListToArray(uach[CH])],
1188
1203
  [FULLVERLIST, itemListToArray(uach[CH_FULL_VER_LIST])],
1189
1204
  [MOBILE, /\?1/.test(uach[CH_MOBILE])],
1190
- [MODEL, stripQuotes(uach[CH_MODEL])],
1191
- [PLATFORM, stripQuotes(uach[CH_PLATFORM])],
1192
- [PLATFORMVER, stripQuotes(uach[CH_PLATFORM_VER])],
1193
- [ARCHITECTURE, stripQuotes(uach[CH_ARCH])],
1205
+ [MODEL, normalizeHeaderValue(uach[CH_MODEL])],
1206
+ [PLATFORM, normalizeHeaderValue(uach[CH_PLATFORM])],
1207
+ [PLATFORMVER, normalizeHeaderValue(uach[CH_PLATFORM_VER])],
1208
+ [ARCHITECTURE, normalizeHeaderValue(uach[CH_ARCH])],
1194
1209
  [FORMFACTORS, itemListToArray(uach[CH_FORM_FACTORS])],
1195
- [BITNESS, stripQuotes(uach[CH_BITNESS])]
1210
+ [BITNESS, normalizeHeaderValue(uach[CH_BITNESS])]
1196
1211
  ]);
1197
1212
  } else {
1198
1213
  for (var prop in uach) {
@@ -1280,11 +1295,17 @@
1280
1295
  this.set(MAJOR, majorize(this.get(VERSION)));
1281
1296
  break;
1282
1297
  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
1298
+ // Since iOS 26, Safari's UA reports the OS version as frozen at 18:
1299
+ // https://webkit.org/blog/17333/webkit-features-in-safari-26-0/#update-to-ua-string
1300
+ if (this.get(NAME) == 'iOS' && this.get(VERSION)) {
1301
+ // Only perform this if iOS version is 18/19
1302
+ if (/^1[89][^\d]/.exec(this.get(VERSION))) {
1303
+ // Based on the assumption that "iOS" version is tightly coupled with "Safari" version
1304
+ var realVersion = /\) Version\/((\d+)[\d\.]*)/.exec(this.ua);
1305
+ if (realVersion && parseInt(realVersion[2], 10) >= 26) {
1306
+ // iOS version = Safari version
1307
+ this.set(VERSION, realVersion[1]);
1308
+ }
1288
1309
  }
1289
1310
  }
1290
1311
  break;
@@ -1440,9 +1461,7 @@
1440
1461
  EMPTY)), // empty string
1441
1462
 
1442
1463
  httpUACH = new UACHData(headers, true),
1443
- regexMap = extensions ?
1444
- extend(defaultRegexes, extensions) :
1445
- defaultRegexes,
1464
+ regexMap = defaultRegexes,
1446
1465
 
1447
1466
  createItemFunc = function (itemType) {
1448
1467
  if (itemType == RESULT) {
@@ -1477,9 +1496,14 @@
1477
1496
  ['setUA', function (ua) {
1478
1497
  if (isString(ua)) userAgent = trim(ua, UA_MAX_LENGTH);
1479
1498
  return this;
1499
+ }],
1500
+ ['useExtension', function (exts) {
1501
+ if (exts) regexMap = extend(regexMap, exts);
1502
+ return this;
1480
1503
  }]
1481
1504
  ])
1482
- .setUA(userAgent);
1505
+ .setUA(userAgent)
1506
+ .useExtension(extensions);
1483
1507
 
1484
1508
  return this;
1485
1509
  }
@@ -3,7 +3,7 @@
3
3
  // Source: /src/main/ua-parser.js
4
4
 
5
5
  /////////////////////////////////////////////////////////////////////////////////
6
- /* UAParser.js v2.0.9
6
+ /* UAParser.js v2.0.10
7
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.
@@ -21,7 +21,7 @@
21
21
  // Constants
22
22
  /////////////
23
23
 
24
- var LIBVERSION = '2.0.9',
24
+ var LIBVERSION = '2.0.10',
25
25
  UA_MAX_LENGTH = 500,
26
26
  USER_AGENT = 'user-agent',
27
27
  EMPTY = '',
@@ -172,7 +172,7 @@
172
172
  itemListToArray = function (header) {
173
173
  if (!header) return undefined;
174
174
  var arr = [];
175
- var tokens = strip(/\\?\"/g, header).split(',');
175
+ var tokens = normalizeHeaderValue(header).split(',');
176
176
  for (var i = 0; i < tokens.length; i++) {
177
177
  if (tokens[i].indexOf(';') > -1) {
178
178
  var token = trim(tokens[i]).split(';v=');
@@ -189,6 +189,9 @@
189
189
  majorize = function (version) {
190
190
  return isString(version) ? strip(/[^\d\.]/g, version).split('.')[0] : undefined;
191
191
  },
192
+ normalizeHeaderValue = function (str) {
193
+ return isString(str) ? trim(strip(/\\?\"/g, str), UA_MAX_LENGTH) : undefined;
194
+ },
192
195
  setProps = function (arr) {
193
196
  for (var i in arr) {
194
197
  if (!arr.hasOwnProperty(i)) continue;
@@ -205,9 +208,6 @@
205
208
  strip = function (pattern, str) {
206
209
  return isString(str) ? str.replace(pattern, EMPTY) : str;
207
210
  },
208
- stripQuotes = function (str) {
209
- return strip(/\\?\"/g, str);
210
- },
211
211
  trim = function (str, len) {
212
212
  str = strip(/^\s\s*/, String(str));
213
213
  return typeof len === TYPEOF.UNDEFINED ? str : str.substring(0, len);
@@ -280,6 +280,10 @@
280
280
  }
281
281
  },
282
282
 
283
+ strTest = function (str, map) {
284
+ return map.test.test(str) ? map.ifTrue : map.ifFalse;
285
+ },
286
+
283
287
  strMapper = function (str, map) {
284
288
 
285
289
  for (var i in map) {
@@ -350,7 +354,7 @@
350
354
  /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
351
355
  ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [
352
356
  /webview.+edge\/([\w\.]+)/i // Microsoft Edge
353
- ], [VERSION, [NAME, EDGE+' WebView']], [
357
+ ], [VERSION, [NAME, EDGE+' WebView'], [TYPE, INAPP]], [
354
358
  /edg(?:e|ios|a)?\/([\w\.]+)/i
355
359
  ], [VERSION, [NAME, 'Edge']], [
356
360
 
@@ -390,7 +394,7 @@
390
394
  ], [VERSION, [NAME, 'Quark']], [
391
395
  /\bddg\/([\w\.]+)/i // DuckDuckGo
392
396
  ], [VERSION, [NAME, 'DuckDuckGo']], [
393
- /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser
397
+ /(?:\buc? ?browser|(?:juc.+)ucweb| ucpc)[\/ ]?([\w\.]+)/i // UCBrowser
394
398
  ], [VERSION, [NAME, 'UCBrowser']], [
395
399
  /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser
396
400
  /\bqbcore\/([\w\.]+).+microm/i,
@@ -429,7 +433,9 @@
429
433
  /\b(qq)\/([\w\.]+)/i // QQ
430
434
  ], [[NAME, /(.+)/, '$1Browser'], VERSION], [
431
435
  /(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i
432
- ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser
436
+ ], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/VivoBrowser/PicoBrowser
437
+ / HBPC\/([\w\.]+)/ // Huawei Browser
438
+ ], [VERSION, [NAME, HUAWEI + SUFFIX_BROWSER]], [
433
439
  /samsungbrowser\/([\w\.]+)/i // Samsung Internet
434
440
  ], [VERSION, [NAME, SAMSUNG + ' Internet']], [
435
441
  /metasr[\/ ]?([\d\.]+)/i // Sogou Explorer
@@ -477,10 +483,10 @@
477
483
  ], [VERSION, [NAME, CHROME+' Headless']], [
478
484
 
479
485
  /wv\).+chrome\/([\w\.]+).+edgw\//i // Edge WebView2
480
- ], [VERSION, [NAME, EDGE+' WebView2']], [
486
+ ], [VERSION, [NAME, EDGE+' WebView2'], [TYPE, INAPP]], [
481
487
 
482
- / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
483
- ], [[NAME, CHROME+' WebView'], VERSION], [
488
+ /; wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
489
+ ], [[NAME, CHROME+' WebView'], VERSION, [TYPE, INAPP]], [
484
490
 
485
491
  /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser
486
492
  ], [VERSION, [NAME, 'Android' + SUFFIX_BROWSER]], [
@@ -614,7 +620,7 @@
614
620
  /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
621
  // Xiaomi Mi
616
622
  /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i,
617
- / ([\w ]+) miui\/v?\d/i
623
+ /; ([\w ]+) miui\/v?\d/i
618
624
  ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
619
625
 
620
626
  // OnePlus
@@ -769,10 +775,19 @@
769
775
  /; (ac[3-6]\d\w{2,8})( b|\))/i
770
776
  ], [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]], [
771
777
 
778
+ // Blackview
779
+ /blackview ([-\w ]+)( b|\))/i,
780
+ /; (bv\d{4}[-\w ]*)( b|\))/i
781
+ ], [MODEL, [VENDOR, 'Blackview'], [TYPE, MOBILE]], [
782
+
772
783
  // HMD
773
784
  /; (n159v)/i
774
785
  ], [MODEL, [VENDOR, 'HMD'], [TYPE, MOBILE]], [
775
786
 
787
+ // T-Mobile
788
+ /((revvl[ \w\+]+|tm(?:rv|af)\w*[45]g(?:tb)?))( b|\))/i
789
+ ], [MODEL, [TYPE, strTest, { 'test': /ta?b/i, 'ifTrue': TABLET, 'ifFalse': MOBILE }], [VENDOR, 'T-Mobile']], [
790
+
776
791
  // MIXED
777
792
  /(imo) (tab \w+)/i, // IMO
778
793
  /(infinix|tecno) (x1101b?|p904|dp(7c|8d|10a)( pro)?|p70[1-3]a?|p904|t1101)/i // Infinix XPad / Tecno
@@ -780,8 +795,8 @@
780
795
 
781
796
  /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i,
782
797
  // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan
783
- // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL/Wiko
784
- /; (blu|hmd|imo|infinix|lava|oneplus|tcl|wiko)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i,
798
+ // BLU/Coolpad/CUBOT/HMD/IMO/Infinix/Lava/OnePlus/TCL/Wiko
799
+ /; (blu|coolpad|cubot|hmd|imo|infinix|lava|oneplus|tcl|wiko)[_ ]([-\w\+ ]+?)(?: bui|\)|; r)/i,
785
800
  /(hp) ([\w ]+\w)/i, // HP iPAQ
786
801
  /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia
787
802
  /(oppo) ?([\w ]+) bui/i, // OPPO
@@ -1189,12 +1204,12 @@
1189
1204
  [BRANDS, itemListToArray(uach[CH])],
1190
1205
  [FULLVERLIST, itemListToArray(uach[CH_FULL_VER_LIST])],
1191
1206
  [MOBILE, /\?1/.test(uach[CH_MOBILE])],
1192
- [MODEL, stripQuotes(uach[CH_MODEL])],
1193
- [PLATFORM, stripQuotes(uach[CH_PLATFORM])],
1194
- [PLATFORMVER, stripQuotes(uach[CH_PLATFORM_VER])],
1195
- [ARCHITECTURE, stripQuotes(uach[CH_ARCH])],
1207
+ [MODEL, normalizeHeaderValue(uach[CH_MODEL])],
1208
+ [PLATFORM, normalizeHeaderValue(uach[CH_PLATFORM])],
1209
+ [PLATFORMVER, normalizeHeaderValue(uach[CH_PLATFORM_VER])],
1210
+ [ARCHITECTURE, normalizeHeaderValue(uach[CH_ARCH])],
1196
1211
  [FORMFACTORS, itemListToArray(uach[CH_FORM_FACTORS])],
1197
- [BITNESS, stripQuotes(uach[CH_BITNESS])]
1212
+ [BITNESS, normalizeHeaderValue(uach[CH_BITNESS])]
1198
1213
  ]);
1199
1214
  } else {
1200
1215
  for (var prop in uach) {
@@ -1282,11 +1297,17 @@
1282
1297
  this.set(MAJOR, majorize(this.get(VERSION)));
1283
1298
  break;
1284
1299
  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
1300
+ // Since iOS 26, Safari's UA reports the OS version as frozen at 18:
1301
+ // https://webkit.org/blog/17333/webkit-features-in-safari-26-0/#update-to-ua-string
1302
+ if (this.get(NAME) == 'iOS' && this.get(VERSION)) {
1303
+ // Only perform this if iOS version is 18/19
1304
+ if (/^1[89][^\d]/.exec(this.get(VERSION))) {
1305
+ // Based on the assumption that "iOS" version is tightly coupled with "Safari" version
1306
+ var realVersion = /\) Version\/((\d+)[\d\.]*)/.exec(this.ua);
1307
+ if (realVersion && parseInt(realVersion[2], 10) >= 26) {
1308
+ // iOS version = Safari version
1309
+ this.set(VERSION, realVersion[1]);
1310
+ }
1290
1311
  }
1291
1312
  }
1292
1313
  break;
@@ -1442,9 +1463,7 @@
1442
1463
  EMPTY)), // empty string
1443
1464
 
1444
1465
  httpUACH = new UACHData(headers, true),
1445
- regexMap = extensions ?
1446
- extend(defaultRegexes, extensions) :
1447
- defaultRegexes,
1466
+ regexMap = defaultRegexes,
1448
1467
 
1449
1468
  createItemFunc = function (itemType) {
1450
1469
  if (itemType == RESULT) {
@@ -1479,9 +1498,14 @@
1479
1498
  ['setUA', function (ua) {
1480
1499
  if (isString(ua)) userAgent = trim(ua, UA_MAX_LENGTH);
1481
1500
  return this;
1501
+ }],
1502
+ ['useExtension', function (exts) {
1503
+ if (exts) regexMap = extend(regexMap, exts);
1504
+ return this;
1482
1505
  }]
1483
1506
  ])
1484
- .setUA(userAgent);
1507
+ .setUA(userAgent)
1508
+ .useExtension(extensions);
1485
1509
 
1486
1510
  return this;
1487
1511
  }