Bump version 2.0.7

This commit is contained in:
Faisal Salman
2025-12-09 13:15:29 +07:00
parent 82d50451a2
commit 7bc177de79
19 changed files with 365 additions and 282 deletions

View File

@@ -12,8 +12,8 @@
/*jshint esversion: 6 */
import { UAParser } from '../main/ua-parser.mjs';
import { Extension, BrowserType } from '../enums/ua-parser-enums.mjs';
import { Bots, Crawlers, Fetchers } from '../extensions/ua-parser-extensions.mjs';
import { BrowserType, Extension } from '../enums/ua-parser-enums.mjs';
const { Crawler, Fetcher } = Extension.BrowserName;
class BotList {
@@ -40,6 +40,9 @@ const BotTypes = new BotList(Bots, 'type', [
const AIAssistants = new BotList(Fetchers, 'name', [
// Amazon
Fetcher.AMAZON_NOVA_ACT,
// Anthropic
Fetcher.ANTHROPIC_CLAUDE_USER,

View File

@@ -12,7 +12,9 @@
/*jshint esversion: 6 */
import { UAParser } from '../main/ua-parser.mjs';
import { EngineName } from '../enums/ua-parser-enums.mjs';
import { isStandalonePWA } from 'is-standalone-pwa';
import { isFromEU } from 'detect-europe-js';
const isChromeFamily = val => !!(
(typeof val === 'string' ?
@@ -20,11 +22,13 @@ const isChromeFamily = val => !!(
val.engine
)?.is(EngineName.BLINK));
const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js
/ electron\//i.test(navigator?.userAgent)); // browser
const isElectron = () => !!(
process?.versions?.hasOwnProperty('electron') || // node.js
/ electron\//i.test(navigator?.userAgent)); // browser
export {
isChromeFamily,
isElectron,
isFromEU,
isStandalonePWA
}

View File

@@ -3,7 +3,7 @@
// Source: /src/enums/ua-parser-enums.js
///////////////////////////////////////////////
/* Enums for UAParser.js v2.0.6
/* Enums for UAParser.js v2.0.7
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com>
AGPLv3 License */
@@ -19,6 +19,7 @@ export const BrowserName: Readonly<{
AMAYA: 'Amaya',
ANDROID: 'Android Browser',
ARORA: 'Arora',
ATLAS: 'Atlas',
AVANT: 'Avant',
AVAST: 'Avast Secure Browser',
AVG: 'AVG Secure Browser',
@@ -147,6 +148,7 @@ export const BrowserName: Readonly<{
SNAPCHAT: 'Snapchat',
SOGOU_EXPLORER: 'Sogou Explorer',
SOGOU_MOBILE: 'Sogou Mobile',
STEAM: 'Steam',
SURF: 'Surf',
SWIFTFOX: 'Swiftfox',
TESLA: 'Tesla',
@@ -228,8 +230,9 @@ export const DeviceVendor: Readonly<{
ACER: 'Acer',
ADVAN: 'Advan',
ALCATEL: 'Alcatel',
APPLE: 'Apple',
AMAZON: 'Amazon',
ANBERNIC: 'Anbernic',
APPLE: 'Apple',
ARCHOS: 'Archos',
ASUS: 'ASUS',
ATT: 'AT&T',
@@ -258,6 +261,7 @@ export const DeviceVendor: Readonly<{
LAVA: 'Lava',
LENOVO: 'Lenovo',
LG: 'LG',
LOGITECH: 'Logitech',
MEIZU: 'Meizu',
MICROMAX: 'Micromax',
MICROSOFT: 'Microsoft',
@@ -291,6 +295,7 @@ export const DeviceVendor: Readonly<{
TECNO: 'TECNO',
TESLA: 'Tesla',
ULEFONE: 'Ulefone',
VALVE: 'Valve',
VIVO: 'Vivo',
VIZIO: 'Vizio',
VODAFONE: 'Vodafone',
@@ -631,6 +636,7 @@ export const Extension: Readonly<{
},
Fetcher: {
AHREFS_SITEAUDIT: 'AhrefsSiteAudit',
AMAZON_NOVA_ACT: 'NovaAct',
ANTHROPIC_CLAUDE_USER: 'Claude-User',
ASANA: 'Asana',
BETTER_UPTIME_BOT: 'Better Uptime Bot',
@@ -708,9 +714,13 @@ export const Extension: Readonly<{
AIOHTTP: 'aiohttp',
APACHE_HTTPCLIENT: 'Apache-HttpClient',
AXIOS: 'axios',
BUN: 'Bun',
DART: 'Dart',
DENO: 'Deno',
GO_HTTP_CLIENT: 'go-http-client',
GOT: 'got',
GUZZLEHTTP: 'GuzzleHttp',
HACKNEY: 'hackney',
JAVA: 'Java',
JAVA_HTTPCLIENT: 'Java-http-client',
JSDOM: 'jsdom',
@@ -718,16 +728,19 @@ export const Extension: Readonly<{
LUA_RESTY_HTTP: 'lua-resty-http',
NEEDLE: 'Needle',
NUTCH: 'Nutch',
OKHTTP: 'OkHttp',
NODE_FETCH: 'node-fetch',
NODE_JS: 'Node.js',
NODE_SUPERAGENT: 'node-superagent',
OKHTTP: 'OkHttp',
PHP_SOAP: 'PHP-SOAP',
POSTMAN_RUNTIME: 'PostmanRuntime',
PYTHON_HTTPX: 'python-httpx',
PYTHON_URLLIB: 'python-urllib',
PYTHON_URLLIB3: 'python-urllib3',
PYTHON_REQUESTS: 'python-requests',
SCRAPY: 'Scrapy'
REST_CLIENT: 'rest-client',
SCRAPY: 'Scrapy',
UNDICI: 'undici'
}
},
DeviceVendor: {

View File

@@ -1,5 +1,5 @@
///////////////////////////////////////////////
/* Enums for UAParser.js v2.0.6
/* Enums for UAParser.js v2.0.7
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com>
AGPLv3 License */

View File

@@ -3,7 +3,7 @@
// Source: /src/enums/ua-parser-enums.js
///////////////////////////////////////////////
/* Enums for UAParser.js v2.0.6
/* Enums for UAParser.js v2.0.7
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com>
AGPLv3 License */
@@ -19,6 +19,7 @@ const BrowserName = Object.freeze({
AMAYA: 'Amaya',
ANDROID: 'Android Browser',
ARORA: 'Arora',
ATLAS: 'Atlas',
AVANT: 'Avant',
AVAST: 'Avast Secure Browser',
AVG: 'AVG Secure Browser',
@@ -147,6 +148,7 @@ const BrowserName = Object.freeze({
SNAPCHAT: 'Snapchat',
SOGOU_EXPLORER: 'Sogou Explorer',
SOGOU_MOBILE: 'Sogou Mobile',
STEAM: 'Steam',
SURF: 'Surf',
SWIFTFOX: 'Swiftfox',
TESLA: 'Tesla',
@@ -228,8 +230,9 @@ const DeviceVendor = Object.freeze({
ACER: 'Acer',
ADVAN: 'Advan',
ALCATEL: 'Alcatel',
APPLE: 'Apple',
AMAZON: 'Amazon',
ANBERNIC: 'Anbernic',
APPLE: 'Apple',
ARCHOS: 'Archos',
ASUS: 'ASUS',
ATT: 'AT&T',
@@ -258,6 +261,7 @@ const DeviceVendor = Object.freeze({
LAVA: 'Lava',
LENOVO: 'Lenovo',
LG: 'LG',
LOGITECH: 'Logitech',
MEIZU: 'Meizu',
MICROMAX: 'Micromax',
MICROSOFT: 'Microsoft',
@@ -291,6 +295,7 @@ const DeviceVendor = Object.freeze({
TECNO: 'TECNO',
TESLA: 'Tesla',
ULEFONE: 'Ulefone',
VALVE: 'Valve',
VIVO: 'Vivo',
VIZIO: 'Vizio',
VODAFONE: 'Vodafone',
@@ -631,6 +636,7 @@ const Extension = Object.freeze({
},
Fetcher: {
AHREFS_SITEAUDIT: 'AhrefsSiteAudit',
AMAZON_NOVA_ACT: 'NovaAct',
ANTHROPIC_CLAUDE_USER: 'Claude-User',
ASANA: 'Asana',
BETTER_UPTIME_BOT: 'Better Uptime Bot',
@@ -708,9 +714,13 @@ const Extension = Object.freeze({
AIOHTTP: 'aiohttp',
APACHE_HTTPCLIENT: 'Apache-HttpClient',
AXIOS: 'axios',
BUN: 'Bun',
DART: 'Dart',
DENO: 'Deno',
GO_HTTP_CLIENT: 'go-http-client',
GOT: 'got',
GUZZLEHTTP: 'GuzzleHttp',
HACKNEY: 'hackney',
JAVA: 'Java',
JAVA_HTTPCLIENT: 'Java-http-client',
JSDOM: 'jsdom',
@@ -718,16 +728,19 @@ const Extension = Object.freeze({
LUA_RESTY_HTTP: 'lua-resty-http',
NEEDLE: 'Needle',
NUTCH: 'Nutch',
OKHTTP: 'OkHttp',
NODE_FETCH: 'node-fetch',
NODE_JS: 'Node.js',
NODE_SUPERAGENT: 'node-superagent',
OKHTTP: 'OkHttp',
PHP_SOAP: 'PHP-SOAP',
POSTMAN_RUNTIME: 'PostmanRuntime',
PYTHON_HTTPX: 'python-httpx',
PYTHON_URLLIB: 'python-urllib',
PYTHON_URLLIB3: 'python-urllib3',
PYTHON_REQUESTS: 'python-requests',
SCRAPY: 'Scrapy'
REST_CLIENT: 'rest-client',
SCRAPY: 'Scrapy',
UNDICI: 'undici'
}
},
DeviceVendor: {

View File

@@ -1,5 +1,5 @@
///////////////////////////////////////////////
/* Extensions for UAParser.js v2.0.6
/* Extensions for UAParser.js v2.0.7
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com>
AGPLv3 License */

View File

@@ -3,7 +3,7 @@
// Source: /src/extensions/ua-parser-extensions.js
///////////////////////////////////////////////
/* Extensions for UAParser.js v2.0.6
/* Extensions for UAParser.js v2.0.7
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com>
AGPLv3 License */
@@ -262,7 +262,7 @@ const Emails = Object.freeze({
const Fetchers = Object.freeze({
browser : [
[
// Asana / Bitlybot / Better Uptime / BingPreview / Blueno / Cohere-AI / HubSpot Page Fetcher / kakaotalk-scrap / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot
// Asana / Bitlybot / Better Uptime / BingPreview / Blueno / Cohere-AI / HubSpot Page Fetcher / kakaotalk-scrap / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot / WhatsApp
// AhrefsSiteAudit - https://ahrefs.com/robot/site-audit
// Buffer Link Preview Bot - https://scraper.buffer.com/about/bots/link-preview-bot
// ChatGPT-User - https://platform.openai.com/docs/plugins/bot
@@ -272,19 +272,19 @@ const Fetchers = Object.freeze({
// Perplexity-User - https://docs.perplexity.ai/guides/bots
// MistralAI-User - https://docs.mistral.ai/robots/
// Yandex Bots - https://yandex.com/bots
/(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|cohere-ai|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)\/([\w\.]+)/i,
/(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|cohere-ai|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,
// Bluesky
/(bluesky) cardyb\/([\w\.]+)/i,
// Nova Act - https://github.com/aws/nova-act
/agent-(novaact)\/([\w\.]+)/i,
// Skype
/(skypeuripreview) preview\/([\w\.]+)/i,
// Slackbot - https://api.slack.com/robots
/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i,
// WhatsApp
/(whatsapp)\/([\w\.]+)/i
/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i
],
[NAME, VERSION, [TYPE, FETCHER]],
@@ -393,13 +393,15 @@ const MediaPlayers = Object.freeze({
const Libraries = Object.freeze({
browser : [
// Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent
[
/^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|scrapy)\/([\w\.]+)/i,
// Apache-HttpClient/Axios/Bun/Dart/go-http-client/got/GuzzleHttp/hackney/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/Node.js/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/rest-client/Scrapy/superagent
/^(apache-httpclient|axios|bun|dart|deno|(?:go|java)-http-client|got|guzzlehttp|hackney|java|libwww-perl|lua-resty-http|needle|node(?:\.js|-fetch|-superagent)|okhttp|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|rest-client|scrapy)\/([\w\.]+)/i,
/(adobeair|aiohttp|jsdom)\/([\w\.]+)/i,
/(nutch)-([\w\.-]+)(\(|$)/i,
/\((java)\/([\w\.]+)/i
], [NAME, VERSION, [TYPE, LIBRARY]]
], [NAME, VERSION, [TYPE, LIBRARY]], [
/(node-fetch|undici)/i
], [NAME, [TYPE, LIBRARY]]
]
});

View File

@@ -1,5 +1,5 @@
///////////////////////////////////////////////
/* Helpers for UAParser.js v2.0.6
/* Helpers for UAParser.js v2.0.7
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com>
AGPLv3 License */
@@ -11,7 +11,7 @@ const { UAParser } = require('../main/ua-parser');
const { EngineName } = require('../enums/ua-parser-enums');
const { getDeviceVendor: _getDeviceVendor, isAppleSilicon: _isAppleSilicon } = require('../device-detection/device-detection');
const { isBot: _isBot, isAICrawler } = require('../bot-detection/bot-detection');
const { isStandalonePWA: _isStandalonePWA } = require('../browser-detection/browser-detection');
const { isChromeFamily: _isChromeFamily, isElectron: _isElectron, isStandalonePWA: _isStandalonePWA } = require('../browser-detection/browser-detection');
const { isFromEU: _isFromEU } = require('../browser-detection/browser-detection');
const { isFrozenUA } = require('ua-is-frozen');
@@ -38,13 +38,12 @@ const isBot = _isBot;
/**
* @deprecated Moved to `browser-detection` submodule
*/
const isChromeFamily = val => !!((typeof val === 'string' ? new UAParser(val).getEngine() : val.engine)?.is(EngineName.BLINK));
const isChromeFamily = _isChromeFamily;
/**
* @deprecated Moved to `browser-detection` submodule
*/
const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js
/ electron\//i.test(navigator?.userAgent)); // browser
const isElectron = () => _isElectron;
/**
* @deprecated Moved to `browser-detection` submodule

View File

@@ -3,7 +3,7 @@
// Source: /src/helpers/ua-parser-helpers.js
///////////////////////////////////////////////
/* Helpers for UAParser.js v2.0.6
/* Helpers for UAParser.js v2.0.7
https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com>
AGPLv3 License */
@@ -15,9 +15,9 @@ import { UAParser } from '../main/ua-parser.mjs';
import { EngineName } from '../enums/ua-parser-enums.mjs';
import { getDeviceVendor: _getDeviceVendor, isAppleSilicon: _isAppleSilicon } from '../device-detection/device-detection.mjs';
import { isBot: _isBot, isAICrawler } from '../bot-detection/bot-detection.mjs';
import { isFromEU } from 'detect-europe-js';
import { isChromeFamily: _isChromeFamily, isElectron: _isElectron, isStandalonePWA: _isStandalonePWA } from '../browser-detection/browser-detection.mjs';
import { isFromEU: _isFromEU } from '../browser-detection/browser-detection.mjs';
import { isFrozenUA } from 'ua-is-frozen';
import { isStandalonePWA } from 'is-standalone-pwa';
/**
* @deprecated Moved to `device-detection` submodule
@@ -39,10 +39,25 @@ const isAIBot = isAICrawler;
*/
const isBot = _isBot;
const isChromeFamily = val => !!((typeof val === 'string' ? new UAParser(val).getEngine() : val.engine)?.is(EngineName.BLINK));
/**
* @deprecated Moved to `browser-detection` submodule
*/
const isChromeFamily = _isChromeFamily;
const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js
/ electron\//i.test(navigator?.userAgent)); // browser
/**
* @deprecated Moved to `browser-detection` submodule
*/
const isElectron = () => _isElectron;
/**
* @deprecated Moved to `browser-detection` submodule
*/
const isFromEU = _isFromEU;
/**
* @deprecated Moved to `browser-detection` submodule
*/
const isStandalonePWA = _isStandalonePWA;
export {
getDeviceVendor,

View File

@@ -1,4 +1,4 @@
// Type definitions for UAParser.js v2.0.6
// Type definitions for UAParser.js v2.0.7
// Project: https://github.com/faisalman/ua-parser-js
// Definitions by: Faisal Salman <https://github.com/faisalman>

View File

@@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v2.0.6
/* UAParser.js v2.0.7
Copyright © 2012-2025 Faisal Salman <f@faisalman.com>
AGPLv3 License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
@@ -19,7 +19,7 @@
// Constants
/////////////
var LIBVERSION = '2.0.6',
var LIBVERSION = '2.0.7',
UA_MAX_LENGTH = 500,
USER_AGENT = 'user-agent',
EMPTY = '',

View File

@@ -3,7 +3,7 @@
// Source: /src/main/ua-parser.js
/////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v2.0.6
/* UAParser.js v2.0.7
Copyright © 2012-2025 Faisal Salman <f@faisalman.com>
AGPLv3 License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
@@ -21,7 +21,7 @@
// Constants
/////////////
var LIBVERSION = '2.0.6',
var LIBVERSION = '2.0.7',
UA_MAX_LENGTH = 500,
USER_AGENT = 'user-agent',
EMPTY = '',
@@ -378,8 +378,8 @@
/(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer
// Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble/Palemoon
/(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,
// Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
/(atlas|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,
// Atlas/Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
/(heytap|ovi|115|surf|qwant)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf
/(qwant)(?:ios|mobile)\/([\d\.]+)/i, // Qwant
/(ecosia|weibo)(?:__| \w+@)([\d\.]+)/i // Ecosia/Weibo
@@ -434,7 +434,7 @@
/(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla
/m?(qqbrowser|2345(?=browser|chrome|explorer))\w*[\/ ]?v?([\w\.]+)/i // QQ/2345
], [NAME, VERSION], [
/(lbbrowser|rekonq)/i // LieBao Browser/Rekonq
/(lbbrowser|rekonq|steam(?= (clie|tenf|gameo)))/i // LieBao Browser/Rekonq/Steam
], [NAME], [
/ome\/([\w\.]+) \w* ?(iron) saf/i, // Iron
/ome\/([\w\.]+).+qihu (360)[es]e/i // 360
@@ -600,12 +600,12 @@
/\b(?:xiao)?((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
/\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
/\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
/\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
/oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i, // Xiaomi Mi
// Xiaomi Redmi / POCO / Black Shark / Qin
/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,
// Xiaomi Mi
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i,
/ ([\w ]+) miui\/v?\d/i
], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
@@ -854,10 +854,10 @@
], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [
/(ouya)/i, // Ouya
/(nintendo) (\w+)/i, // Nintendo
/(retroid) (pocket ([^\)]+))/i // Retroid Pocket
], [VENDOR, MODEL, [TYPE, CONSOLE]], [
/droid.+; (shield)( bui|\))/i // Nvidia Portable
], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [
/(retroid) (pocket ([^\)]+))/i, // Retroid Pocket
/(valve).+(steam deck)/i,
/droid.+; ((shield|rgcube|gr0006))( bui|\))/i // Nvidia Portable/Anbernic/Logitech
], [[VENDOR, strMapper, { 'Nvidia': 'Shield', 'Anbernic': 'RGCUBE', 'Logitech': 'GR0006' }], MODEL, [TYPE, CONSOLE]], [
///////////////////
// WEARABLES
@@ -866,7 +866,7 @@
/\b(sm-[lr]\d\d[0156][fnuw]?s?|gear live)\b/i // Samsung Galaxy Watch
], [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]], [
/((pebble))app/i, // Pebble
/(asus|google|lg|oppo) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch
/(asus|google|lg|oppo|xiaomi) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch / Xiaomi Watch
], [VENDOR, MODEL, [TYPE, WEARABLE]], [
/(ow(?:19|20)?we?[1-3]{1,3})/i // Oppo Watch
], [MODEL, [VENDOR, OPPO], [TYPE, WEARABLE]], [
@@ -970,7 +970,8 @@
// iOS/macOS
/[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
/(?:ios;fbsv|ios(?=.+ip(?:ad|hone))|ip(?:ad|hone)(?: |.+i(?:pad)?)os)[\/ ]([\w\.]+)/i,
/(?:ios;fbsv|ios(?=.+ip(?:ad|hone)|.+apple ?tv)|ip(?:ad|hone)(?: |.+i(?:pad)?)os|apple ?tv.+ios)[\/ ]([\w\.]+)/i,
/\btvos ?([\w\.]+)/i,
/cfnetwork\/.+darwin/i
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
/(mac os x) ?([\w\. ]*)/i,
@@ -1144,25 +1145,25 @@
};
}
if (!NAVIGATOR_UADATA) {
IData.prototype.then = function (cb) {
var that = this;
var IDataResolve = function () {
for (var prop in that) {
if (that.hasOwnProperty(prop)) {
this[prop] = that[prop];
}
IData.prototype.then = function (cb) {
var that = this;
var IDataResolve = function () {
for (var prop in that) {
if (that.hasOwnProperty(prop)) {
this[prop] = that[prop];
}
};
IDataResolve.prototype = {
is : IData.prototype.is,
toString : IData.prototype.toString
};
var resolveData = new IDataResolve();
cb(resolveData);
return resolveData;
}
};
}
IDataResolve.prototype = {
is : IData.prototype.is,
toString : IData.prototype.toString,
withClientHints : IData.prototype.withClientHints,
withFeatureCheck : IData.prototype.withFeatureCheck
};
var resolveData = new IDataResolve();
cb(resolveData);
return resolveData;
};
return new IData();
};
@@ -1194,189 +1195,6 @@
}
function UAItem (itemType, ua, rgxMap, uaCH) {
this.get = function (prop) {
if (!prop) return this.data;
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
};
this.set = function (prop, val) {
this.data[prop] = val;
return this;
};
this.setCH = function (ch) {
this.uaCH = ch;
return this;
};
this.detectFeature = function () {
if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
switch (this.itemType) {
case BROWSER:
// Brave-specific detection
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
this.set(NAME, 'Brave');
}
break;
case DEVICE:
// Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
this.set(TYPE, MOBILE);
}
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
this.set(MODEL, 'iPad')
.set(TYPE, TABLET);
}
break;
case OS:
// Chrome-specific detection: check for 'platform' value of navigator.userAgentData
if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
}
break;
case RESULT:
var data = this.data;
var detect = function (itemType) {
return data[itemType]
.getItem()
.detectFeature()
.get();
};
this.set(BROWSER, detect(BROWSER))
.set(CPU, detect(CPU))
.set(DEVICE, detect(DEVICE))
.set(ENGINE, detect(ENGINE))
.set(OS, detect(OS));
}
}
return this;
};
this.parseUA = function () {
if (this.itemType != RESULT) {
rgxMapper.call(this.data, this.ua, this.rgxMap);
}
switch (this.itemType) {
case BROWSER:
this.set(MAJOR, majorize(this.get(VERSION)));
break;
case OS:
if (this.get(NAME) == 'iOS' && this.get(VERSION) == '18.6') {
// Based on the assumption that iOS version is tightly coupled with Safari version
var realVersion = /\) Version\/([\d\.]+)/.exec(this.ua); // Get Safari version
if (realVersion && parseInt(realVersion[1].substring(0,2), 10) >= 26) {
this.set(VERSION, realVersion[1]); // Set as iOS version
}
}
break;
}
return this;
};
this.parseCH = function () {
var uaCH = this.uaCH,
rgxMap = this.rgxMap;
switch (this.itemType) {
case BROWSER:
case ENGINE:
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
if (brands) {
for (var i=0; i<brands.length; i++) {
var brandName = brands[i].brand || brands[i],
brandVersion = brands[i].version;
if (this.itemType == BROWSER &&
!/not.a.brand/i.test(brandName) &&
(!prevName ||
(/Chrom/.test(prevName) && brandName != CHROMIUM) ||
(prevName == EDGE && /WebView2/.test(brandName))
)) {
brandName = strMapper(brandName, browserHintsMap);
prevName = this.get(NAME);
if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) {
this.set(NAME, brandName)
.set(VERSION, brandVersion)
.set(MAJOR, majorize(brandVersion));
}
prevName = brandName;
}
if (this.itemType == ENGINE && brandName == CHROMIUM) {
this.set(VERSION, brandVersion);
}
}
}
break;
case CPU:
var archName = uaCH[ARCHITECTURE];
if (archName) {
if (archName && uaCH[BITNESS] == '64') archName += '64';
rgxMapper.call(this.data, archName + ';', rgxMap);
}
break;
case DEVICE:
if (uaCH[MOBILE]) {
this.set(TYPE, MOBILE);
}
if (uaCH[MODEL]) {
this.set(MODEL, uaCH[MODEL]);
if (!this.get(TYPE) || !this.get(VENDOR)) {
var reParse = {};
rgxMapper.call(reParse, 'droid 9; ' + uaCH[MODEL] + ')', rgxMap);
if (!this.get(TYPE) && !!reParse.type) {
this.set(TYPE, reParse.type);
}
if (!this.get(VENDOR) && !!reParse.vendor) {
this.set(VENDOR, reParse.vendor);
}
}
}
if (uaCH[FORMFACTORS]) {
var ff;
if (typeof uaCH[FORMFACTORS] !== 'string') {
var idx = 0;
while (!ff && idx < uaCH[FORMFACTORS].length) {
ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap);
}
} else {
ff = strMapper(uaCH[FORMFACTORS], formFactorsMap);
}
this.set(TYPE, ff);
}
break;
case OS:
var osName = uaCH[PLATFORM];
if(osName) {
var osVersion = uaCH[PLATFORMVER];
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
this.set(NAME, osName)
.set(VERSION, osVersion);
}
// Xbox-Specific Detection
if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') {
this.set(NAME, 'Xbox')
.set(VERSION, undefined);
}
break;
case RESULT:
var data = this.data;
var parse = function (itemType) {
return data[itemType]
.getItem()
.setCH(uaCH)
.parseCH()
.get();
};
this.set(BROWSER, parse(BROWSER))
.set(CPU, parse(CPU))
.set(DEVICE, parse(DEVICE))
.set(ENGINE, parse(ENGINE))
.set(OS, parse(OS));
}
return this;
};
setProps.call(this, [
['itemType', itemType],
['ua', ua],
@@ -1384,10 +1202,191 @@
['rgxMap', rgxMap],
['data', createIData(this, itemType)]
]);
return this;
}
UAItem.prototype.get = function (prop) {
if (!prop) return this.data;
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
};
UAItem.prototype.set = function (prop, val) {
this.data[prop] = val;
return this;
};
UAItem.prototype.setCH = function (ch) {
this.uaCH = ch;
return this;
};
UAItem.prototype.detectFeature = function () {
if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
switch (this.itemType) {
case BROWSER:
// Brave-specific detection
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
this.set(NAME, 'Brave');
}
break;
case DEVICE:
// Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
this.set(TYPE, MOBILE);
}
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
this.set(MODEL, 'iPad')
.set(TYPE, TABLET);
}
break;
case OS:
// Chrome-specific detection: check for 'platform' value of navigator.userAgentData
if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
}
break;
case RESULT:
var data = this.data;
var detect = function (itemType) {
return data[itemType]
.getItem()
.detectFeature()
.get();
};
this.set(BROWSER, detect(BROWSER))
.set(CPU, detect(CPU))
.set(DEVICE, detect(DEVICE))
.set(ENGINE, detect(ENGINE))
.set(OS, detect(OS));
}
}
return this;
};
UAItem.prototype.parseUA = function () {
if (this.itemType != RESULT) {
rgxMapper.call(this.data, this.ua, this.rgxMap);
}
switch (this.itemType) {
case BROWSER:
this.set(MAJOR, majorize(this.get(VERSION)));
break;
case OS:
if (this.get(NAME) == 'iOS' && this.get(VERSION) == '18.6') {
// Based on the assumption that iOS version is tightly coupled with Safari version
var realVersion = /\) Version\/([\d\.]+)/.exec(this.ua); // Get Safari version
if (realVersion && parseInt(realVersion[1].substring(0,2), 10) >= 26) {
this.set(VERSION, realVersion[1]); // Set as iOS version
}
}
break;
}
return this;
};
UAItem.prototype.parseCH = function () {
var uaCH = this.uaCH,
rgxMap = this.rgxMap;
switch (this.itemType) {
case BROWSER:
case ENGINE:
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
if (brands) {
for (var i=0; i<brands.length; i++) {
var brandName = brands[i].brand || brands[i],
brandVersion = brands[i].version;
if (this.itemType == BROWSER &&
!/not.a.brand/i.test(brandName) &&
(!prevName ||
(/Chrom/.test(prevName) && brandName != CHROMIUM) ||
(prevName == EDGE && /WebView2/.test(brandName))
)) {
brandName = strMapper(brandName, browserHintsMap);
prevName = this.get(NAME);
if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) {
this.set(NAME, brandName)
.set(VERSION, brandVersion)
.set(MAJOR, majorize(brandVersion));
}
prevName = brandName;
}
if (this.itemType == ENGINE && brandName == CHROMIUM) {
this.set(VERSION, brandVersion);
}
}
}
break;
case CPU:
var archName = uaCH[ARCHITECTURE];
if (archName) {
if (archName && uaCH[BITNESS] == '64') archName += '64';
rgxMapper.call(this.data, archName + ';', rgxMap);
}
break;
case DEVICE:
if (uaCH[MOBILE]) {
this.set(TYPE, MOBILE);
}
if (uaCH[MODEL]) {
this.set(MODEL, uaCH[MODEL]);
if (!this.get(TYPE) || !this.get(VENDOR)) {
var reParse = {};
rgxMapper.call(reParse, 'droid 9; ' + uaCH[MODEL] + ')', rgxMap);
if (!this.get(TYPE) && !!reParse.type) {
this.set(TYPE, reParse.type);
}
if (!this.get(VENDOR) && !!reParse.vendor) {
this.set(VENDOR, reParse.vendor);
}
}
}
if (uaCH[FORMFACTORS]) {
var ff;
if (typeof uaCH[FORMFACTORS] !== 'string') {
var idx = 0;
while (!ff && idx < uaCH[FORMFACTORS].length) {
ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap);
}
} else {
ff = strMapper(uaCH[FORMFACTORS], formFactorsMap);
}
this.set(TYPE, ff);
}
break;
case OS:
var osName = uaCH[PLATFORM];
if(osName) {
var osVersion = uaCH[PLATFORMVER];
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
this.set(NAME, osName)
.set(VERSION, osVersion);
}
// Xbox-Specific Detection
if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') {
this.set(NAME, 'Xbox')
.set(VERSION, undefined);
}
break;
case RESULT:
var data = this.data;
var parse = function (itemType) {
return data[itemType]
.getItem()
.setCH(uaCH)
.parseCH()
.get();
};
this.set(BROWSER, parse(BROWSER))
.set(CPU, parse(CPU))
.set(DEVICE, parse(DEVICE))
.set(ENGINE, parse(ENGINE))
.set(OS, parse(OS));
}
return this;
};
function UAParser (ua, extensions, headers) {
if (typeof ua === TYPEOF.OBJECT) {