Merge tag '2.0.6' into pro-business

This commit is contained in:
Faisal Salman
2025-10-27 13:38:53 +07:00
35 changed files with 1073 additions and 328 deletions

View File

@@ -23,8 +23,12 @@
- **Support for Custom/Predefined Extensions:** - **Support for Custom/Predefined Extensions:**
- Pass custom regexes or predefined extensions as a list to `UAParser()` - Pass custom regexes or predefined extensions as a list to `UAParser()`
- **Support for CLI Parsing:** - **Support for CLI Processing:**
- Parse a user-agent directly from the command line using `npx ua-parser-js "[User-Agent]"` - Directly parse user-agent strings from the command line:
`npx ua-parser-js "<User-Agent>"`
- Process batch data from files:
`npx ua-parser-js --input-file=log.txt >> result.json` or
`npx ua-parser-js --input-file=log.txt --output-file=result.json`
- **Enhanced Detection with Client Hints:** - **Enhanced Detection with Client Hints:**
- `withClientHints()`: Improves detection accuracy by leveraging client hints - `withClientHints()`: Improves detection accuracy by leveraging client hints
@@ -65,6 +69,20 @@
--- ---
## Version 2.0.6
- Add new CLI feature: processing batch user-agent data from file and output as JSON
- Fix `setUA()`: trim leading space from user-agent string input
- Replace `undici` dependency with node's internal `Headers`
- Add new browser: Bing, Qwant
- Add new device vendor: Hisense, Wiko
- Improve browser detection: Mozilla, Pale Moon
- Improve CPU detection: 68k
- Improve device detection: Apple, BlackBerry, Huawei, Nokia, Xiaomi
- Improve OS detection: iOS 26
- `extensions` submodule:
- Add new fetcher: Discordbot, KeybaseBot, Slackbot, Slackbot-LinkExpanding, Slack-ImgProxy, Twitterbot
- Add new crawler: Qwantbot-news, SurdotlyBot, SwiftBot
## Version 2.0.5 ## Version 2.0.5
- Add new browser: Zalo - Add new browser: Zalo

View File

@@ -23,8 +23,12 @@ https://docs.uaparser.dev
import { UAParser } from '@ua-parser-js/pro-business'; import { UAParser } from '@ua-parser-js/pro-business';
``` ```
```js
import { isFrozenUA } from '@ua-parser-js/pro-business/helpers';
```
# License # License
UAParser.js PRO Business UAParser.js PRO Business
Copyright (c) 2023-2024 Faisal Salman <<f@faisalman.com>> Copyright (c) 2023-2025 Faisal Salman <<f@faisalman.com>>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

16
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@ua-parser-js/pro-business", "name": "@ua-parser-js/pro-business",
"version": "2.0.5", "version": "2.0.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@ua-parser-js/pro-business", "name": "@ua-parser-js/pro-business",
"version": "2.0.5", "version": "2.0.6",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -25,8 +25,7 @@
"dependencies": { "dependencies": {
"detect-europe-js": "^0.1.2", "detect-europe-js": "^0.1.2",
"is-standalone-pwa": "^0.1.1", "is-standalone-pwa": "^0.1.1",
"ua-is-frozen": "^0.1.2", "ua-is-frozen": "^0.1.2"
"undici": "^7.12.0"
}, },
"bin": { "bin": {
"pro-business": "script/cli.js" "pro-business": "script/cli.js"
@@ -2665,15 +2664,6 @@
"node": ">=0.8.0" "node": ">=0.8.0"
} }
}, },
"node_modules/undici": {
"version": "7.12.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz",
"integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==",
"license": "MIT",
"engines": {
"node": ">=20.18.1"
}
},
"node_modules/validate-npm-package-license": { "node_modules/validate-npm-package-license": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",

View File

@@ -1,25 +1,25 @@
{ {
"title": "UAParser.js PRO Business", "title": "UAParser.js PRO Business",
"name": "@ua-parser-js/pro-business", "name": "@ua-parser-js/pro-business",
"version": "2.0.5", "version": "2.0.6",
"author": "Faisal Salman <f@faisalman.com> (http://faisalman.com)", "author": "Faisal Salman <f@faisalman.com> (http://faisalman.com)",
"description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment",
"keywords": [ "keywords": [
"user-agent", "user-agent",
"client-hints", "client-hints",
"parser",
"browser", "browser",
"engine", "engine",
"os", "os",
"device", "device",
"cpu", "cpu",
"jquery-plugin",
"ecosystem:jquery",
"ua-parser-js", "ua-parser-js",
"browser-detection", "browser-detection",
"device-detection", "device-detection",
"os-detection", "os-detection",
"bot-detection" "bot-detection",
"ai-detection",
"app-detection",
"crawler-detection"
], ],
"homepage": "https://uaparser.dev", "homepage": "https://uaparser.dev",
"contributors": [ "contributors": [
@@ -220,14 +220,13 @@
"test:eslint": "eslint src && eslint script", "test:eslint": "eslint src && eslint script",
"test:jshint": "jshint src/main", "test:jshint": "jshint src/main",
"test:lockfile-lint": "npx lockfile-lint -p package-lock.json", "test:lockfile-lint": "npx lockfile-lint -p package-lock.json",
"test:mocha": "mocha test/unit", "test:mocha": "mocha --recursive test/unit",
"test:playwright": "npx playwright install && playwright test test/e2e --browser all" "test:playwright": "npx playwright install && playwright test test/e2e --browser all"
}, },
"dependencies": { "dependencies": {
"detect-europe-js": "^0.1.2", "detect-europe-js": "^0.1.2",
"is-standalone-pwa": "^0.1.1", "is-standalone-pwa": "^0.1.1",
"ua-is-frozen": "^0.1.2", "ua-is-frozen": "^0.1.2"
"undici": "^7.12.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/parser": "7.15.8", "@babel/parser": "7.15.8",

View File

@@ -1,4 +1,94 @@
#!/usr/bin/env node #!/usr/bin/env node
const UAParser = require('ua-parser-js'); try {
console.log(JSON.stringify(process.argv.slice(2).map(ua => UAParser(ua)), null, 4)); const fs = require('node:fs');
const path = require('node:path');
const { performance } = require('node:perf_hooks');
const readline = require('node:readline');
const { parseArgs } = require('node:util');
const UAParser = require('../src/main/ua-parser');
const { Bots, Emails, ExtraDevices, InApps, Vehicles } = require('../src/extensions/ua-parser-extensions');
if (!process.argv[2].startsWith('-')) {
const results = process.argv.slice(2).map(ua => UAParser(ua));
console.log(JSON.stringify(results, null, 4));
process.exit(0);
} else if (['-h', '--help'].includes(process.argv[2])) {
console.log('Usage: npx ua-parser-js <string>');
console.log(' or npx ua-parser-js --input-file <filepath> [--output-file <filepath>]');
console.log('-i, --input-file');
console.log('-o, --output-file');
process.exit(0);
} else {
const startPerf = performance.now();
const {
values: {
'input-file': inputFile,
'output-file': outputFile
},
} = parseArgs({
options: {
'input-file': { type: 'string', short: 'i' },
'output-file': { type: 'string', short: 'o' }
}
});
if (!inputFile) {
console.error('Input file must be present');
process.exit(1);
}
const inputPath = path.resolve(__dirname, inputFile);
const outputPath = outputFile ? path.resolve(__dirname, outputFile) : null;
if (!fs.existsSync(inputPath)) {
console.error(`Input file not found: ${inputPath}`);
process.exit(1);
}
const inputStream = fs.createReadStream(inputPath, 'utf8');
const rl = readline.createInterface({
input: inputStream,
crlfDelay: Infinity
});
const outputStream = outputPath ? fs.createWriteStream(outputPath, { encoding : 'utf8' }) : process.stdout;
const uap = new UAParser([Bots, Emails, ExtraDevices, InApps, Vehicles]);
let lineNumber = 0;
outputStream.write('[\n');
rl.on('line', line => {
const result = uap.setUA(line).getResult();
const json = JSON.stringify(result, null, 4);
if (lineNumber > 0) outputStream.write(',\n');
outputStream.write(json);
lineNumber++;
});
rl.on('close', () => {
outputStream.write('\n]');
if (outputPath) {
outputStream.end(() => {
const finishPerf = performance.now();
console.log(`Done!`);
console.log(`Number of lines found: ${lineNumber}`);
console.log(`Task finished in: ${(finishPerf - startPerf).toFixed(3)}ms`);
console.log(`Output written to: ${outputPath}`);
process.exit(0);
});
} else {
process.exit(0);
}
});
}
} catch (err) {
console.error(err);
process.exit(1);
}

View File

@@ -3,7 +3,7 @@
// Source: /src/enums/ua-parser-enums.js // Source: /src/enums/ua-parser-enums.js
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Enums for UAParser.js v2.0.5 /* Enums for UAParser.js v2.0.6
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License */ UAParser.js PRO Business License */
@@ -24,6 +24,7 @@ export const BrowserName: Readonly<{
AVG: 'AVG Secure Browser', AVG: 'AVG Secure Browser',
BAIDU: 'Baidu Browser', BAIDU: 'Baidu Browser',
BASILISK: 'Basilisk', BASILISK: 'Basilisk',
BING: 'Bing',
BLAZER: 'Blazer', BLAZER: 'Blazer',
BOLT: 'Bolt', BOLT: 'Bolt',
BOWSER: 'Bowser', BOWSER: 'Bowser',
@@ -129,6 +130,7 @@ export const BrowserName: Readonly<{
QUARK: 'Quark', QUARK: 'Quark',
QUPZILLA: 'QupZilla', QUPZILLA: 'QupZilla',
QUTEBROWSER: 'qutebrowser', QUTEBROWSER: 'qutebrowser',
QWANT: 'Qwant',
REKONQ: 'rekonq', REKONQ: 'rekonq',
ROCKMELT: 'Rockmelt', ROCKMELT: 'Rockmelt',
SAFARI: 'Safari', SAFARI: 'Safari',
@@ -243,6 +245,7 @@ export const DeviceVendor: Readonly<{
GEEKSPHONE: 'GeeksPhone', GEEKSPHONE: 'GeeksPhone',
GENERIC: 'Generic', GENERIC: 'Generic',
GOOGLE: 'Google', GOOGLE: 'Google',
HISENSE: 'Hisense',
HMD: 'HMD', HMD: 'HMD',
HP: 'HP', HP: 'HP',
HTC: 'HTC', HTC: 'HTC',
@@ -291,6 +294,7 @@ export const DeviceVendor: Readonly<{
VIVO: 'Vivo', VIVO: 'Vivo',
VIZIO: 'Vizio', VIZIO: 'Vizio',
VODAFONE: 'Vodafone', VODAFONE: 'Vodafone',
WIKO: 'Wiko',
XBOX: 'Xbox', XBOX: 'Xbox',
XIAOMI: 'Xiaomi', XIAOMI: 'Xiaomi',
ZEBRA: 'Zebra', ZEBRA: 'Zebra',
@@ -489,6 +493,7 @@ export const Extension: Readonly<{
DUCKDUCKGO_BOT: 'DuckDuckBot', DUCKDUCKGO_BOT: 'DuckDuckBot',
DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot', DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot',
ELASTIC: 'Elastic', ELASTIC: 'Elastic',
ELASTIC_SWIFTYPE_BOT: 'Swiftbot',
EXALEAD_EXABOT: 'Exabot', EXALEAD_EXABOT: 'Exabot',
FIRECRAWL_AGENT: 'FirecrawlAgent', FIRECRAWL_AGENT: 'FirecrawlAgent',
FREESPOKE: 'Freespoke', FREESPOKE: 'Freespoke',
@@ -538,6 +543,7 @@ export const Extension: Readonly<{
PERPLEXITY_BOT: 'PerplexityBot', PERPLEXITY_BOT: 'PerplexityBot',
QIHOO_360_SPIDER: '360Spider', QIHOO_360_SPIDER: '360Spider',
QWANT_BOT: 'Qwantbot', QWANT_BOT: 'Qwantbot',
QWANT_BOT_NEWS: 'Qwantbot-news',
REPLICATE_BOT: 'Replicate-Bot', REPLICATE_BOT: 'Replicate-Bot',
RUNPOD_BOT: 'RunPod-Bot', RUNPOD_BOT: 'RunPod-Bot',
SB_INTUITIONS_BOT: 'SBIntuitionsBot', SB_INTUITIONS_BOT: 'SBIntuitionsBot',
@@ -551,6 +557,7 @@ export const Extension: Readonly<{
SOGOU_PIC_SPIDER: 'Sogou Pic Spider', SOGOU_PIC_SPIDER: 'Sogou Pic Spider',
SOGOU_WEB_SPIDER: 'Sogou web spider', SOGOU_WEB_SPIDER: 'Sogou web spider',
STARTPAGE: 'Startpage', STARTPAGE: 'Startpage',
SURLY_BOT: 'SurdotlyBot',
TIMPI_BOT: 'Timpibot', TIMPI_BOT: 'Timpibot',
TOGETHER_BOT: 'Together-Bot', TOGETHER_BOT: 'Together-Bot',
TURNITIN_BOT: 'TurnitinBot', TURNITIN_BOT: 'TurnitinBot',
@@ -631,6 +638,7 @@ export const Extension: Readonly<{
BLUESKY: 'Bluesky', BLUESKY: 'Bluesky',
BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot', BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot',
COHERE_AI: 'Cohere-AI', COHERE_AI: 'Cohere-AI',
DISCORD_BOT: 'Discordbot',
DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot', DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot',
GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse', GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse',
GOOGLE_FEEDFETCHER: 'FeedFetcher-Google', GOOGLE_FEEDFETCHER: 'FeedFetcher-Google',
@@ -643,6 +651,7 @@ export const Extension: Readonly<{
HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher', HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher',
IFRAMELY: 'Iframely', IFRAMELY: 'Iframely',
KAKAOTALK_SCRAP: 'kakaotalk-scrap', KAKAOTALK_SCRAP: 'kakaotalk-scrap',
KEYBASE_BOT: 'KeybaseBot',
META_EXTERNALFETCHER: 'meta-externalfetcher', META_EXTERNALFETCHER: 'meta-externalfetcher',
META_WHATSAPP: 'WhatsApp', META_WHATSAPP: 'WhatsApp',
MICROSOFT_BINGPREVIEW: 'BingPreview', MICROSOFT_BINGPREVIEW: 'BingPreview',
@@ -654,6 +663,9 @@ export const Extension: Readonly<{
PERPLEXITY_USER: 'Perplexity-User', PERPLEXITY_USER: 'Perplexity-User',
PINTEREST_BOT: 'Pinterestbot', PINTEREST_BOT: 'Pinterestbot',
SEMRUSH_SITEAUDITBOT: 'SiteAuditBot', SEMRUSH_SITEAUDITBOT: 'SiteAuditBot',
SLACK_BOT: 'Slackbot',
SLACK_BOT_LINKEXPANDING: 'Slackbot-LinkExpanding',
SLACK_IMGPROXY: 'Slack-ImgProxy',
SNAP_URL_PREVIEW: 'Snap URL Preview', SNAP_URL_PREVIEW: 'Snap URL Preview',
SKYPE_URIPREVIEW: 'SkypeUriPreview', SKYPE_URIPREVIEW: 'SkypeUriPreview',
TELEGRAM_BOT: 'TelegramBot', TELEGRAM_BOT: 'TelegramBot',
@@ -663,6 +675,7 @@ export const Extension: Readonly<{
VERCEL_BOT: 'Vercelbot', VERCEL_BOT: 'Vercelbot',
VERCEL_FLAGS: 'vercelflags', VERCEL_FLAGS: 'vercelflags',
VERCEL_TRACING: 'verceltracing', VERCEL_TRACING: 'verceltracing',
X_TWITTERBOT: 'Twitterbot',
YANDEX_CALENDAR: 'YandexCalendar', YANDEX_CALENDAR: 'YandexCalendar',
YANDEX_DIRECT: 'YandexDirect', YANDEX_DIRECT: 'YandexDirect',
YANDEX_DIRECTDYN: 'YandexDirectDyn', YANDEX_DIRECTDYN: 'YandexDirectDyn',

View File

@@ -1,5 +1,5 @@
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Enums for UAParser.js v2.0.5 /* Enums for UAParser.js v2.0.6
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License */ UAParser.js PRO Business License */
@@ -20,6 +20,7 @@ const BrowserName = Object.freeze({
AVG: 'AVG Secure Browser', AVG: 'AVG Secure Browser',
BAIDU: 'Baidu Browser', BAIDU: 'Baidu Browser',
BASILISK: 'Basilisk', BASILISK: 'Basilisk',
BING: 'Bing',
BLAZER: 'Blazer', BLAZER: 'Blazer',
BOLT: 'Bolt', BOLT: 'Bolt',
BOWSER: 'Bowser', BOWSER: 'Bowser',
@@ -125,6 +126,7 @@ const BrowserName = Object.freeze({
QUARK: 'Quark', QUARK: 'Quark',
QUPZILLA: 'QupZilla', QUPZILLA: 'QupZilla',
QUTEBROWSER: 'qutebrowser', QUTEBROWSER: 'qutebrowser',
QWANT: 'Qwant',
REKONQ: 'rekonq', REKONQ: 'rekonq',
ROCKMELT: 'Rockmelt', ROCKMELT: 'Rockmelt',
SAFARI: 'Safari', SAFARI: 'Safari',
@@ -239,6 +241,7 @@ const DeviceVendor = Object.freeze({
GEEKSPHONE: 'GeeksPhone', GEEKSPHONE: 'GeeksPhone',
GENERIC: 'Generic', GENERIC: 'Generic',
GOOGLE: 'Google', GOOGLE: 'Google',
HISENSE: 'Hisense',
HMD: 'HMD', HMD: 'HMD',
HP: 'HP', HP: 'HP',
HTC: 'HTC', HTC: 'HTC',
@@ -287,6 +290,7 @@ const DeviceVendor = Object.freeze({
VIVO: 'Vivo', VIVO: 'Vivo',
VIZIO: 'Vizio', VIZIO: 'Vizio',
VODAFONE: 'Vodafone', VODAFONE: 'Vodafone',
WIKO: 'Wiko',
XBOX: 'Xbox', XBOX: 'Xbox',
XIAOMI: 'Xiaomi', XIAOMI: 'Xiaomi',
ZEBRA: 'Zebra', ZEBRA: 'Zebra',
@@ -485,6 +489,7 @@ const Extension = Object.freeze({
DUCKDUCKGO_BOT: 'DuckDuckBot', DUCKDUCKGO_BOT: 'DuckDuckBot',
DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot', DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot',
ELASTIC: 'Elastic', ELASTIC: 'Elastic',
ELASTIC_SWIFTYPE_BOT: 'Swiftbot',
EXALEAD_EXABOT: 'Exabot', EXALEAD_EXABOT: 'Exabot',
FIRECRAWL_AGENT: 'FirecrawlAgent', FIRECRAWL_AGENT: 'FirecrawlAgent',
FREESPOKE: 'Freespoke', FREESPOKE: 'Freespoke',
@@ -534,6 +539,7 @@ const Extension = Object.freeze({
PERPLEXITY_BOT: 'PerplexityBot', PERPLEXITY_BOT: 'PerplexityBot',
QIHOO_360_SPIDER: '360Spider', QIHOO_360_SPIDER: '360Spider',
QWANT_BOT: 'Qwantbot', QWANT_BOT: 'Qwantbot',
QWANT_BOT_NEWS: 'Qwantbot-news',
REPLICATE_BOT: 'Replicate-Bot', REPLICATE_BOT: 'Replicate-Bot',
RUNPOD_BOT: 'RunPod-Bot', RUNPOD_BOT: 'RunPod-Bot',
SB_INTUITIONS_BOT: 'SBIntuitionsBot', SB_INTUITIONS_BOT: 'SBIntuitionsBot',
@@ -547,6 +553,7 @@ const Extension = Object.freeze({
SOGOU_PIC_SPIDER: 'Sogou Pic Spider', SOGOU_PIC_SPIDER: 'Sogou Pic Spider',
SOGOU_WEB_SPIDER: 'Sogou web spider', SOGOU_WEB_SPIDER: 'Sogou web spider',
STARTPAGE: 'Startpage', STARTPAGE: 'Startpage',
SURLY_BOT: 'SurdotlyBot',
TIMPI_BOT: 'Timpibot', TIMPI_BOT: 'Timpibot',
TOGETHER_BOT: 'Together-Bot', TOGETHER_BOT: 'Together-Bot',
TURNITIN_BOT: 'TurnitinBot', TURNITIN_BOT: 'TurnitinBot',
@@ -627,6 +634,7 @@ const Extension = Object.freeze({
BLUESKY: 'Bluesky', BLUESKY: 'Bluesky',
BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot', BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot',
COHERE_AI: 'Cohere-AI', COHERE_AI: 'Cohere-AI',
DISCORD_BOT: 'Discordbot',
DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot', DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot',
GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse', GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse',
GOOGLE_FEEDFETCHER: 'FeedFetcher-Google', GOOGLE_FEEDFETCHER: 'FeedFetcher-Google',
@@ -639,6 +647,7 @@ const Extension = Object.freeze({
HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher', HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher',
IFRAMELY: 'Iframely', IFRAMELY: 'Iframely',
KAKAOTALK_SCRAP: 'kakaotalk-scrap', KAKAOTALK_SCRAP: 'kakaotalk-scrap',
KEYBASE_BOT: 'KeybaseBot',
META_EXTERNALFETCHER: 'meta-externalfetcher', META_EXTERNALFETCHER: 'meta-externalfetcher',
META_WHATSAPP: 'WhatsApp', META_WHATSAPP: 'WhatsApp',
MICROSOFT_BINGPREVIEW: 'BingPreview', MICROSOFT_BINGPREVIEW: 'BingPreview',
@@ -650,6 +659,9 @@ const Extension = Object.freeze({
PERPLEXITY_USER: 'Perplexity-User', PERPLEXITY_USER: 'Perplexity-User',
PINTEREST_BOT: 'Pinterestbot', PINTEREST_BOT: 'Pinterestbot',
SEMRUSH_SITEAUDITBOT: 'SiteAuditBot', SEMRUSH_SITEAUDITBOT: 'SiteAuditBot',
SLACK_BOT: 'Slackbot',
SLACK_BOT_LINKEXPANDING: 'Slackbot-LinkExpanding',
SLACK_IMGPROXY: 'Slack-ImgProxy',
SNAP_URL_PREVIEW: 'Snap URL Preview', SNAP_URL_PREVIEW: 'Snap URL Preview',
SKYPE_URIPREVIEW: 'SkypeUriPreview', SKYPE_URIPREVIEW: 'SkypeUriPreview',
TELEGRAM_BOT: 'TelegramBot', TELEGRAM_BOT: 'TelegramBot',
@@ -659,6 +671,7 @@ const Extension = Object.freeze({
VERCEL_BOT: 'Vercelbot', VERCEL_BOT: 'Vercelbot',
VERCEL_FLAGS: 'vercelflags', VERCEL_FLAGS: 'vercelflags',
VERCEL_TRACING: 'verceltracing', VERCEL_TRACING: 'verceltracing',
X_TWITTERBOT: 'Twitterbot',
YANDEX_CALENDAR: 'YandexCalendar', YANDEX_CALENDAR: 'YandexCalendar',
YANDEX_DIRECT: 'YandexDirect', YANDEX_DIRECT: 'YandexDirect',
YANDEX_DIRECTDYN: 'YandexDirectDyn', YANDEX_DIRECTDYN: 'YandexDirectDyn',

View File

@@ -3,7 +3,7 @@
// Source: /src/enums/ua-parser-enums.js // Source: /src/enums/ua-parser-enums.js
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Enums for UAParser.js v2.0.5 /* Enums for UAParser.js v2.0.6
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License */ UAParser.js PRO Business License */
@@ -24,6 +24,7 @@ const BrowserName = Object.freeze({
AVG: 'AVG Secure Browser', AVG: 'AVG Secure Browser',
BAIDU: 'Baidu Browser', BAIDU: 'Baidu Browser',
BASILISK: 'Basilisk', BASILISK: 'Basilisk',
BING: 'Bing',
BLAZER: 'Blazer', BLAZER: 'Blazer',
BOLT: 'Bolt', BOLT: 'Bolt',
BOWSER: 'Bowser', BOWSER: 'Bowser',
@@ -129,6 +130,7 @@ const BrowserName = Object.freeze({
QUARK: 'Quark', QUARK: 'Quark',
QUPZILLA: 'QupZilla', QUPZILLA: 'QupZilla',
QUTEBROWSER: 'qutebrowser', QUTEBROWSER: 'qutebrowser',
QWANT: 'Qwant',
REKONQ: 'rekonq', REKONQ: 'rekonq',
ROCKMELT: 'Rockmelt', ROCKMELT: 'Rockmelt',
SAFARI: 'Safari', SAFARI: 'Safari',
@@ -243,6 +245,7 @@ const DeviceVendor = Object.freeze({
GEEKSPHONE: 'GeeksPhone', GEEKSPHONE: 'GeeksPhone',
GENERIC: 'Generic', GENERIC: 'Generic',
GOOGLE: 'Google', GOOGLE: 'Google',
HISENSE: 'Hisense',
HMD: 'HMD', HMD: 'HMD',
HP: 'HP', HP: 'HP',
HTC: 'HTC', HTC: 'HTC',
@@ -291,6 +294,7 @@ const DeviceVendor = Object.freeze({
VIVO: 'Vivo', VIVO: 'Vivo',
VIZIO: 'Vizio', VIZIO: 'Vizio',
VODAFONE: 'Vodafone', VODAFONE: 'Vodafone',
WIKO: 'Wiko',
XBOX: 'Xbox', XBOX: 'Xbox',
XIAOMI: 'Xiaomi', XIAOMI: 'Xiaomi',
ZEBRA: 'Zebra', ZEBRA: 'Zebra',
@@ -489,6 +493,7 @@ const Extension = Object.freeze({
DUCKDUCKGO_BOT: 'DuckDuckBot', DUCKDUCKGO_BOT: 'DuckDuckBot',
DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot', DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot',
ELASTIC: 'Elastic', ELASTIC: 'Elastic',
ELASTIC_SWIFTYPE_BOT: 'Swiftbot',
EXALEAD_EXABOT: 'Exabot', EXALEAD_EXABOT: 'Exabot',
FIRECRAWL_AGENT: 'FirecrawlAgent', FIRECRAWL_AGENT: 'FirecrawlAgent',
FREESPOKE: 'Freespoke', FREESPOKE: 'Freespoke',
@@ -538,6 +543,7 @@ const Extension = Object.freeze({
PERPLEXITY_BOT: 'PerplexityBot', PERPLEXITY_BOT: 'PerplexityBot',
QIHOO_360_SPIDER: '360Spider', QIHOO_360_SPIDER: '360Spider',
QWANT_BOT: 'Qwantbot', QWANT_BOT: 'Qwantbot',
QWANT_BOT_NEWS: 'Qwantbot-news',
REPLICATE_BOT: 'Replicate-Bot', REPLICATE_BOT: 'Replicate-Bot',
RUNPOD_BOT: 'RunPod-Bot', RUNPOD_BOT: 'RunPod-Bot',
SB_INTUITIONS_BOT: 'SBIntuitionsBot', SB_INTUITIONS_BOT: 'SBIntuitionsBot',
@@ -551,6 +557,7 @@ const Extension = Object.freeze({
SOGOU_PIC_SPIDER: 'Sogou Pic Spider', SOGOU_PIC_SPIDER: 'Sogou Pic Spider',
SOGOU_WEB_SPIDER: 'Sogou web spider', SOGOU_WEB_SPIDER: 'Sogou web spider',
STARTPAGE: 'Startpage', STARTPAGE: 'Startpage',
SURLY_BOT: 'SurdotlyBot',
TIMPI_BOT: 'Timpibot', TIMPI_BOT: 'Timpibot',
TOGETHER_BOT: 'Together-Bot', TOGETHER_BOT: 'Together-Bot',
TURNITIN_BOT: 'TurnitinBot', TURNITIN_BOT: 'TurnitinBot',
@@ -631,6 +638,7 @@ const Extension = Object.freeze({
BLUESKY: 'Bluesky', BLUESKY: 'Bluesky',
BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot', BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot',
COHERE_AI: 'Cohere-AI', COHERE_AI: 'Cohere-AI',
DISCORD_BOT: 'Discordbot',
DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot', DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot',
GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse', GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse',
GOOGLE_FEEDFETCHER: 'FeedFetcher-Google', GOOGLE_FEEDFETCHER: 'FeedFetcher-Google',
@@ -643,6 +651,7 @@ const Extension = Object.freeze({
HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher', HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher',
IFRAMELY: 'Iframely', IFRAMELY: 'Iframely',
KAKAOTALK_SCRAP: 'kakaotalk-scrap', KAKAOTALK_SCRAP: 'kakaotalk-scrap',
KEYBASE_BOT: 'KeybaseBot',
META_EXTERNALFETCHER: 'meta-externalfetcher', META_EXTERNALFETCHER: 'meta-externalfetcher',
META_WHATSAPP: 'WhatsApp', META_WHATSAPP: 'WhatsApp',
MICROSOFT_BINGPREVIEW: 'BingPreview', MICROSOFT_BINGPREVIEW: 'BingPreview',
@@ -654,6 +663,9 @@ const Extension = Object.freeze({
PERPLEXITY_USER: 'Perplexity-User', PERPLEXITY_USER: 'Perplexity-User',
PINTEREST_BOT: 'Pinterestbot', PINTEREST_BOT: 'Pinterestbot',
SEMRUSH_SITEAUDITBOT: 'SiteAuditBot', SEMRUSH_SITEAUDITBOT: 'SiteAuditBot',
SLACK_BOT: 'Slackbot',
SLACK_BOT_LINKEXPANDING: 'Slackbot-LinkExpanding',
SLACK_IMGPROXY: 'Slack-ImgProxy',
SNAP_URL_PREVIEW: 'Snap URL Preview', SNAP_URL_PREVIEW: 'Snap URL Preview',
SKYPE_URIPREVIEW: 'SkypeUriPreview', SKYPE_URIPREVIEW: 'SkypeUriPreview',
TELEGRAM_BOT: 'TelegramBot', TELEGRAM_BOT: 'TelegramBot',
@@ -663,6 +675,7 @@ const Extension = Object.freeze({
VERCEL_BOT: 'Vercelbot', VERCEL_BOT: 'Vercelbot',
VERCEL_FLAGS: 'vercelflags', VERCEL_FLAGS: 'vercelflags',
VERCEL_TRACING: 'verceltracing', VERCEL_TRACING: 'verceltracing',
X_TWITTERBOT: 'Twitterbot',
YANDEX_CALENDAR: 'YandexCalendar', YANDEX_CALENDAR: 'YandexCalendar',
YANDEX_DIRECT: 'YandexDirect', YANDEX_DIRECT: 'YandexDirect',
YANDEX_DIRECTDYN: 'YandexDirectDyn', YANDEX_DIRECTDYN: 'YandexDirectDyn',

View File

@@ -1,5 +1,5 @@
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Extensions for UAParser.js v2.0.5 /* Extensions for UAParser.js v2.0.6
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License */ UAParser.js PRO Business License */
@@ -63,8 +63,10 @@ const Crawlers = Object.freeze({
// PerplexityBot - https://perplexity.ai/perplexitybot // PerplexityBot - https://perplexity.ai/perplexitybot
// SBIntuitionsBot - https://www.sbintuitions.co.jp/bot/ // SBIntuitionsBot - https://www.sbintuitions.co.jp/bot/
// SeznamBot - http://napoveda.seznam.cz/seznambot-intro // SeznamBot - http://napoveda.seznam.cz/seznambot-intro
// SurdotlyBot - http://sur.ly/bot.html
// Swiftbot - https://swiftype.com/swiftbot
// YepBot - https://yep.com/yepbot/ // YepBot - https://yep.com/yepbot/
/((?: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|yep)bot)\/([\w\.-]+)/i, /((?: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,
// Algolia Crawler // Algolia Crawler
/(algolia crawler(?: renderscript)?)\/?([\w\.]*)/i, /(algolia crawler(?: renderscript)?)\/?([\w\.]*)/i,
@@ -98,7 +100,7 @@ const Crawlers = Object.freeze({
/(oncrawl) mobile\/([\w\.]+)/i, /(oncrawl) mobile\/([\w\.]+)/i,
// Qwantbot - https://help.qwant.com/bot // Qwantbot - https://help.qwant.com/bot
/(qwantbot)[-\w]*\/?([\w\.]*)/i, /(qwantbot(?:-news)?)[-\w]*\/?([\w\.]*)/i,
// SemrushBot - http://www.semrush.com/bot.html // SemrushBot - http://www.semrush.com/bot.html
/((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i, /((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i,
@@ -283,8 +285,8 @@ const Fetchers = Object.freeze({
[NAME, VERSION, [TYPE, FETCHER]], [NAME, VERSION, [TYPE, FETCHER]],
[ [
// Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / Snapchat / Vercelbot / Yandex Bots // Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / KeybaseBot / Snapchat / Vercelbot / Yandex Bots
/((?:better uptime |telegram|vercel)bot|chrome-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 /((?:better uptime |keybase|telegram|vercel)bot|chrome-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
], ],
[NAME, [TYPE, FETCHER]], [NAME, [TYPE, FETCHER]],
], ],

View File

@@ -3,7 +3,7 @@
// Source: /src/extensions/ua-parser-extensions.js // Source: /src/extensions/ua-parser-extensions.js
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Extensions for UAParser.js v2.0.5 /* Extensions for UAParser.js v2.0.6
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License */ UAParser.js PRO Business License */
@@ -67,8 +67,10 @@ const Crawlers = Object.freeze({
// PerplexityBot - https://perplexity.ai/perplexitybot // PerplexityBot - https://perplexity.ai/perplexitybot
// SBIntuitionsBot - https://www.sbintuitions.co.jp/bot/ // SBIntuitionsBot - https://www.sbintuitions.co.jp/bot/
// SeznamBot - http://napoveda.seznam.cz/seznambot-intro // SeznamBot - http://napoveda.seznam.cz/seznambot-intro
// SurdotlyBot - http://sur.ly/bot.html
// Swiftbot - https://swiftype.com/swiftbot
// YepBot - https://yep.com/yepbot/ // YepBot - https://yep.com/yepbot/
/((?: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|yep)bot)\/([\w\.-]+)/i, /((?: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,
// Algolia Crawler // Algolia Crawler
/(algolia crawler(?: renderscript)?)\/?([\w\.]*)/i, /(algolia crawler(?: renderscript)?)\/?([\w\.]*)/i,
@@ -102,7 +104,7 @@ const Crawlers = Object.freeze({
/(oncrawl) mobile\/([\w\.]+)/i, /(oncrawl) mobile\/([\w\.]+)/i,
// Qwantbot - https://help.qwant.com/bot // Qwantbot - https://help.qwant.com/bot
/(qwantbot)[-\w]*\/?([\w\.]*)/i, /(qwantbot(?:-news)?)[-\w]*\/?([\w\.]*)/i,
// SemrushBot - http://www.semrush.com/bot.html // SemrushBot - http://www.semrush.com/bot.html
/((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i, /((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i,
@@ -287,8 +289,8 @@ const Fetchers = Object.freeze({
[NAME, VERSION, [TYPE, FETCHER]], [NAME, VERSION, [TYPE, FETCHER]],
[ [
// Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / Snapchat / Vercelbot / Yandex Bots // Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / KeybaseBot / Snapchat / Vercelbot / Yandex Bots
/((?:better uptime |telegram|vercel)bot|chrome-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 /((?:better uptime |keybase|telegram|vercel)bot|chrome-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
], ],
[NAME, [TYPE, FETCHER]], [NAME, [TYPE, FETCHER]],
], ],

View File

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

View File

@@ -3,7 +3,7 @@
// Source: /src/helpers/ua-parser-helpers.js // Source: /src/helpers/ua-parser-helpers.js
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Helpers for UAParser.js v2.0.5 /* Helpers for UAParser.js v2.0.6
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License */ UAParser.js PRO Business License */

View File

@@ -1,8 +1,7 @@
// Type definitions for UAParser.js v2.0.5 // Type definitions for UAParser.js v2.0.6
// Project: https://github.com/faisalman/ua-parser-js // Project: https://github.com/faisalman/ua-parser-js
// Definitions by: Faisal Salman <https://github.com/faisalman> // Definitions by: Faisal Salman <https://github.com/faisalman>
import type { Headers } from "undici";
import { BrowserType, CPUArch, DeviceType, EngineName } from "../enums/ua-parser-enums"; import { BrowserType, CPUArch, DeviceType, EngineName } from "../enums/ua-parser-enums";
declare namespace UAParser { declare namespace UAParser {

View File

@@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v2.0.5 /* UAParser.js v2.0.6
Copyright © 2012-2025 Faisal Salman <f@faisalman.com> Copyright © 2012-2025 Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License *//* UAParser.js PRO Business License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
@@ -19,25 +19,25 @@
// Constants // Constants
///////////// /////////////
var LIBVERSION = '2.0.5', var LIBVERSION = '2.0.6',
UA_MAX_LENGTH = 500, UA_MAX_LENGTH = 500,
USER_AGENT = 'user-agent', USER_AGENT = 'user-agent',
EMPTY = '', EMPTY = '',
UNKNOWN = '?', UNKNOWN = '?',
TYPEOF = {
// typeof FUNCTION : 'function',
FUNC_TYPE = 'function', OBJECT : 'object',
UNDEF_TYPE = 'undefined', STRING : 'string',
OBJ_TYPE = 'object', UNDEFINED : 'undefined'
STR_TYPE = 'string', },
// properties // properties
UA_BROWSER = 'browser', BROWSER = 'browser',
UA_CPU = 'cpu', CPU = 'cpu',
UA_DEVICE = 'device', DEVICE = 'device',
UA_ENGINE = 'engine', ENGINE = 'engine',
UA_OS = 'os', OS = 'os',
UA_RESULT = 'result', RESULT = 'result',
NAME = 'name', NAME = 'name',
TYPE = 'type', TYPE = 'type',
@@ -66,15 +66,15 @@
PLATFORM = 'platform', PLATFORM = 'platform',
PLATFORMVER = 'platformVersion', PLATFORMVER = 'platformVersion',
BITNESS = 'bitness', BITNESS = 'bitness',
CH_HEADER = 'sec-ch-ua', CH = 'sec-ch-ua',
CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_FULL_VER_LIST= CH + '-full-version-list',
CH_HEADER_ARCH = CH_HEADER + '-arch', CH_ARCH = CH + '-arch',
CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, CH_BITNESS = CH + '-' + BITNESS,
CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors', CH_FORM_FACTORS = CH + '-form-factors',
CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, CH_MOBILE = CH + '-' + MOBILE,
CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, CH_MODEL = CH + '-' + MODEL,
CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_PLATFORM = CH + '-' + PLATFORM,
CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', CH_PLATFORM_VER = CH_PLATFORM + '-version',
CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS], CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS],
// device vendors // device vendors
@@ -114,7 +114,7 @@
// os // os
WINDOWS = 'Windows'; WINDOWS = 'Windows';
var isWindow = typeof window !== UNDEF_TYPE, var isWindow = typeof window !== TYPEOF.UNDEFINED,
NAVIGATOR = (isWindow && window.navigator) ? NAVIGATOR = (isWindow && window.navigator) ?
window.navigator : window.navigator :
undefined, undefined,
@@ -150,7 +150,7 @@
return enums; return enums;
}, },
has = function (str1, str2) { has = function (str1, str2) {
if (typeof str1 === OBJ_TYPE && str1.length > 0) { if (typeof str1 === TYPEOF.OBJECT && str1.length > 0) {
for (var i in str1) { for (var i in str1) {
if (lowerize(str2) == lowerize(str1[i])) return true; if (lowerize(str2) == lowerize(str1[i])) return true;
} }
@@ -164,7 +164,7 @@
} }
}, },
isString = function (val) { isString = function (val) {
return typeof val === STR_TYPE; return typeof val === TYPEOF.STRING;
}, },
itemListToArray = function (header) { itemListToArray = function (header) {
if (!header) return undefined; if (!header) return undefined;
@@ -191,7 +191,7 @@
if (!arr.hasOwnProperty(i)) continue; if (!arr.hasOwnProperty(i)) continue;
var propName = arr[i]; var propName = arr[i];
if (typeof propName == OBJ_TYPE && propName.length == 2) { if (typeof propName == TYPEOF.OBJECT && propName.length == 2) {
this[propName[0]] = propName[1]; this[propName[0]] = propName[1];
} else { } else {
this[propName] = undefined; this[propName] = undefined;
@@ -206,10 +206,8 @@
return strip(/\\?\"/g, str); return strip(/\\?\"/g, str);
}, },
trim = function (str, len) { trim = function (str, len) {
if (isString(str)) { str = strip(/^\s\s*/, String(str));
str = strip(/^\s\s*/, str); return typeof len === TYPEOF.UNDEFINED ? str : str.substring(0, len);
return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
}
}; };
/////////////// ///////////////
@@ -240,9 +238,9 @@
match = matches[++k]; match = matches[++k];
q = props[p]; q = props[p];
// check if given property is actually array // check if given property is actually array
if (typeof q === OBJ_TYPE && q.length > 0) { if (typeof q === TYPEOF.OBJECT && q.length > 0) {
if (q.length === 2) { if (q.length === 2) {
if (typeof q[1] == FUNC_TYPE) { if (typeof q[1] == TYPEOF.FUNCTION) {
// assign modified match // assign modified match
this[q[0]] = q[1].call(this, match); this[q[0]] = q[1].call(this, match);
} else { } else {
@@ -251,7 +249,7 @@
} }
} else if (q.length >= 3) { } else if (q.length >= 3) {
// Check whether q[1] FUNCTION or REGEX // Check whether q[1] FUNCTION or REGEX
if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) { if (typeof q[1] === TYPEOF.FUNCTION && !(q[1].exec && q[1].test)) {
if (q.length > 3) { if (q.length > 3) {
this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined; this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined;
} else { } else {
@@ -283,7 +281,7 @@
for (var i in map) { for (var i in map) {
// check if current value is array // check if current value is array
if (typeof map[i] === OBJ_TYPE && map[i].length > 0) { if (typeof map[i] === TYPEOF.OBJECT && map[i].length > 0) {
for (var j = 0; j < map[i].length; j++) { for (var j = 0; j < map[i].length; j++) {
if (has(map[i][j], str)) { if (has(map[i][j], str)) {
return (i === UNKNOWN) ? undefined : i; return (i === UNKNOWN) ? undefined : i;
@@ -377,10 +375,11 @@
/(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet
/(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer /(?: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 // 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)\/([-\w\.]+)/i, /(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 // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
/(heytap|ovi|115|surf)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf /(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 /(ecosia|weibo)(?:__| \w+@)([\d\.]+)/i // Ecosia/Weibo
], [NAME, VERSION], [ ], [NAME, VERSION], [
/quark(?:pc)?\/([-\w\.]+)/i // Quark /quark(?:pc)?\/([-\w\.]+)/i // Quark
@@ -449,6 +448,7 @@
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
/(alipay)client\/([\w\.]+)/i, // Alipay /(alipay)client\/([\w\.]+)/i, // Alipay
/(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter
/(bing)(?:web|sapphire)\/([\w\.]+)/i, // Bing
/(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna /(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna
], [NAME, VERSION, [TYPE, INAPP]], [ ], [NAME, VERSION, [TYPE, INAPP]], [
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
@@ -506,10 +506,10 @@
/(swiftfox)/i, // Swiftfox /(swiftfox)/i, // Swiftfox
/(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i, /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i,
// IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
/(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i, /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|basilisk|waterfox)\/([-\w\.]+)$/i,
// Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
/(firefox)\/([\w\.]+)/i, // Other Firefox-based /(firefox)\/([\w\.]+)/i, // Other Firefox-based
/(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla /(mozilla)\/([\w\.]+(?= .+rv\:.+gecko\/\d+)|[0-4][\w\.]+(?!.+compatible))/i, // Mozilla
// Other // Other
/(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, /(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
@@ -547,6 +547,8 @@
/((ppc|powerpc)(64)?)( mac|;|\))/i, // PowerPC /((ppc|powerpc)(64)?)( mac|;|\))/i, // PowerPC
/(?:osf1|[freopnt]{3,4}bsd) (alpha)/i // Alpha /(?:osf1|[freopnt]{3,4}bsd) (alpha)/i // Alpha
], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [ ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [
/mc680.0/i
], [[ARCHITECTURE, '68k']], [
/winnt.+\[axp/i /winnt.+\[axp/i
], [[ARCHITECTURE, 'alpha']] ], [[ARCHITECTURE, 'alpha']]
], ],
@@ -566,11 +568,10 @@
], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [
// Apple // Apple
/(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone /(?:\/|\()(ip(?:hone|od)[\w, ]*)[\/\);]/i // iPod/iPhone
], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [ ], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [
/\((ipad);[-\w\),; ]+apple/i, // iPad /\b(?:ios|apple\w+)\/.+[\(\/](ipad)/i, // iPad
/applecoremedia\/[\w\.]+ \((ipad)/i, /\b(ipad)[\d,]*[;\] ].+(mac |i(pad)?)os/i
/\b(ipad)\d\d?,\d\d?[;\]].+ios/i
], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [ ], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [
/(macintosh);/i /(macintosh);/i
], [MODEL, [VENDOR, APPLE]], [ ], [MODEL, [VENDOR, APPLE]], [
@@ -588,13 +589,13 @@
// Huawei // Huawei
/\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i /\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i
], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [ ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [
/(?:huawei)([-\w ]+)[;\)]/i, /(?:huawei) ?([-\w ]+)[;\)]/i,
/\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][\dc][adnt]?)\b(?!.+d\/s)/i
], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [ ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
// Xiaomi // Xiaomi
/oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i, /oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i,
/\b((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets /\b(?:xiao)?((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [ ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
/\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
@@ -602,7 +603,7 @@
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
/\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi /\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 /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 lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/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, // Xiaomi Mi
/ ([\w ]+) miui\/v?\d/i / ([\w ]+) miui\/v?\d/i
], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
@@ -658,7 +659,7 @@
/(nokia) (t[12][01])/i /(nokia) (t[12][01])/i
], [VENDOR, MODEL, [TYPE, TABLET]], [ ], [VENDOR, MODEL, [TYPE, TABLET]], [
/(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i, /(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i,
/nokia[-_ ]?(([-\w\. ]*))/i /nokia[-_ ]?(([-\w\. ]*?))( bui|\)|;|\/)/i
], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [ ], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [
// Google // Google
@@ -689,7 +690,7 @@
/(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook /(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook
], [MODEL, VENDOR, [TYPE, TABLET]], [ ], [MODEL, VENDOR, [TYPE, TABLET]], [
/\b((?:bb[a-f]|st[hv])100-\d)/i, /\b((?:bb[a-f]|st[hv])100-\d)/i,
/\(bb10; (\w+)/i // BlackBerry 10 /(?:blackberry|\(bb10;) (\w+)/i
], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [ ], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [
// Asus // Asus
@@ -769,10 +770,12 @@
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i, /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i,
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan
/; (blu|hmd|imo|infinix|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL/Wiko
/; (blu|hmd|imo|infinix|lava|oneplus|tcl|wiko)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i,
/(hp) ([\w ]+\w)/i, // HP iPAQ /(hp) ([\w ]+\w)/i, // HP iPAQ
/(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia
/(oppo) ?([\w ]+) bui/i, // OPPO /(oppo) ?([\w ]+) bui/i, // OPPO
/(hisense) ([ehv][\w ]+)\)/i, // Hisense
/droid[^;]+; (philips)[_ ]([sv-x][\d]{3,4}[xz]?)/i // Philips /droid[^;]+; (philips)[_ ]([sv-x][\d]{3,4}[xz]?)/i // Philips
], [VENDOR, MODEL, [TYPE, MOBILE]], [ ], [VENDOR, MODEL, [TYPE, MOBILE]], [
@@ -912,7 +915,7 @@
], [MODEL, [TYPE, SMARTTV]], [ ], [MODEL, [TYPE, SMARTTV]], [
/\b((4k|android|smart|opera)[- ]?tv|tv; rv:|large screen[\w ]+safari)\b/i /\b((4k|android|smart|opera)[- ]?tv|tv; rv:|large screen[\w ]+safari)\b/i
], [[TYPE, SMARTTV]], [ ], [[TYPE, SMARTTV]], [
/droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+?(mobile|vr|\d) safari/i /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew|; hmsc).+?(mobile|vr|\d) safari/i
], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [ ], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [
/\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet
], [[TYPE, TABLET]], [ ], [[TYPE, TABLET]], [
@@ -965,7 +968,7 @@
// iOS/macOS // iOS/macOS
/[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
/(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i, /(?:ios;fbsv|ios(?=.+ip(?:ad|hone))|ip(?:ad|hone)(?: |.+i(?:pad)?)os)[\/ ]([\w\.]+)/i,
/cfnetwork\/.+darwin/i /cfnetwork\/.+darwin/i
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
/(mac os x) ?([\w\. ]*)/i, /(mac os x) ?([\w\. ]*)/i,
@@ -1050,27 +1053,27 @@
var defaultProps = (function () { var defaultProps = (function () {
var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
setProps.call(props.init, [ setProps.call(props.init, [
[UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]], [BROWSER, [NAME, VERSION, MAJOR, TYPE]],
[UA_CPU, [ARCHITECTURE]], [CPU, [ARCHITECTURE]],
[UA_DEVICE, [TYPE, MODEL, VENDOR]], [DEVICE, [TYPE, MODEL, VENDOR]],
[UA_ENGINE, [NAME, VERSION]], [ENGINE, [NAME, VERSION]],
[UA_OS, [NAME, VERSION]] [OS, [NAME, VERSION]]
]); ]);
setProps.call(props.isIgnore, [ setProps.call(props.isIgnore, [
[UA_BROWSER, [VERSION, MAJOR]], [BROWSER, [VERSION, MAJOR]],
[UA_ENGINE, [VERSION]], [ENGINE, [VERSION]],
[UA_OS, [VERSION]] [OS, [VERSION]]
]); ]);
setProps.call(props.isIgnoreRgx, [ setProps.call(props.isIgnoreRgx, [
[UA_BROWSER, / ?browser$/i], [BROWSER, / ?browser$/i],
[UA_OS, / ?os$/i] [OS, / ?os$/i]
]); ]);
setProps.call(props.toString, [ setProps.call(props.toString, [
[UA_BROWSER, [NAME, VERSION]], [BROWSER, [NAME, VERSION]],
[UA_CPU, [ARCHITECTURE]], [CPU, [ARCHITECTURE]],
[UA_DEVICE, [VENDOR, MODEL]], [DEVICE, [VENDOR, MODEL]],
[UA_ENGINE, [NAME, VERSION]], [ENGINE, [NAME, VERSION]],
[UA_OS, [NAME, VERSION]] [OS, [NAME, VERSION]]
]); ]);
return props; return props;
})(); })();
@@ -1114,14 +1117,14 @@
return item.detectFeature().get(); return item.detectFeature().get();
}; };
if (itemType != UA_RESULT) { if (itemType != RESULT) {
IData.prototype.is = function (strToCheck) { IData.prototype.is = function (strToCheck) {
var is = false; var is = false;
for (var i in this) { for (var i in this) {
if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) { if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) {
is = true; is = true;
if (strToCheck != UNDEF_TYPE) break; if (strToCheck != TYPEOF.UNDEFINED) break;
} else if (strToCheck == UNDEF_TYPE && is) { } else if (strToCheck == TYPEOF.UNDEFINED && is) {
is = !is; is = !is;
break; break;
} }
@@ -1131,11 +1134,11 @@
IData.prototype.toString = function () { IData.prototype.toString = function () {
var str = EMPTY; var str = EMPTY;
for (var i in toString_props) { for (var i in toString_props) {
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { if (typeof(this[toString_props[i]]) !== TYPEOF.UNDEFINED) {
str += (str ? ' ' : EMPTY) + this[toString_props[i]]; str += (str ? ' ' : EMPTY) + this[toString_props[i]];
} }
} }
return str || UNDEF_TYPE; return str || TYPEOF.UNDEFINED;
}; };
} }
@@ -1171,19 +1174,19 @@
setProps.call(this, CH_ALL_VALUES); setProps.call(this, CH_ALL_VALUES);
if (isHttpUACH) { if (isHttpUACH) {
setProps.call(this, [ setProps.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])], [BRANDS, itemListToArray(uach[CH])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], [FULLVERLIST, itemListToArray(uach[CH_FULL_VER_LIST])],
[MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])], [MOBILE, /\?1/.test(uach[CH_MOBILE])],
[MODEL, stripQuotes(uach[CH_HEADER_MODEL])], [MODEL, stripQuotes(uach[CH_MODEL])],
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORM, stripQuotes(uach[CH_PLATFORM])],
[PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [PLATFORMVER, stripQuotes(uach[CH_PLATFORM_VER])],
[ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], [ARCHITECTURE, stripQuotes(uach[CH_ARCH])],
[FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])], [FORMFACTORS, itemListToArray(uach[CH_FORM_FACTORS])],
[BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] [BITNESS, stripQuotes(uach[CH_BITNESS])]
]); ]);
} else { } else {
for (var prop in uach) { for (var prop in uach) {
if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop]; if(this.hasOwnProperty(prop) && typeof uach[prop] !== TYPEOF.UNDEFINED) this[prop] = uach[prop];
} }
} }
} }
@@ -1208,30 +1211,30 @@
this.detectFeature = function () { this.detectFeature = function () {
if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) { if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
switch (this.itemType) { switch (this.itemType) {
case UA_BROWSER: case BROWSER:
// Brave-specific detection // Brave-specific detection
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
this.set(NAME, 'Brave'); this.set(NAME, 'Brave');
} }
break; break;
case UA_DEVICE: case DEVICE:
// Chrome-specific detection: check for 'mobile' value of navigator.userAgentData // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
this.set(TYPE, MOBILE); this.set(TYPE, MOBILE);
} }
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
this.set(MODEL, 'iPad') this.set(MODEL, 'iPad')
.set(TYPE, TABLET); .set(TYPE, TABLET);
} }
break; break;
case UA_OS: case OS:
// Chrome-specific detection: check for 'platform' value of navigator.userAgentData // Chrome-specific detection: check for 'platform' value of navigator.userAgentData
if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
} }
break; break;
case UA_RESULT: case RESULT:
var data = this.data; var data = this.data;
var detect = function (itemType) { var detect = function (itemType) {
return data[itemType] return data[itemType]
@@ -1239,22 +1242,33 @@
.detectFeature() .detectFeature()
.get(); .get();
}; };
this.set(UA_BROWSER, detect(UA_BROWSER)) this.set(BROWSER, detect(BROWSER))
.set(UA_CPU, detect(UA_CPU)) .set(CPU, detect(CPU))
.set(UA_DEVICE, detect(UA_DEVICE)) .set(DEVICE, detect(DEVICE))
.set(UA_ENGINE, detect(UA_ENGINE)) .set(ENGINE, detect(ENGINE))
.set(UA_OS, detect(UA_OS)); .set(OS, detect(OS));
} }
} }
return this; return this;
}; };
this.parseUA = function () { this.parseUA = function () {
if (this.itemType != UA_RESULT) { if (this.itemType != RESULT) {
rgxMapper.call(this.data, this.ua, this.rgxMap); rgxMapper.call(this.data, this.ua, this.rgxMap);
} }
if (this.itemType == UA_BROWSER) { switch (this.itemType) {
case BROWSER:
this.set(MAJOR, majorize(this.get(VERSION))); 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; return this;
}; };
@@ -1264,14 +1278,14 @@
rgxMap = this.rgxMap; rgxMap = this.rgxMap;
switch (this.itemType) { switch (this.itemType) {
case UA_BROWSER: case BROWSER:
case UA_ENGINE: case ENGINE:
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName; var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
if (brands) { if (brands) {
for (var i=0; i<brands.length; i++) { for (var i=0; i<brands.length; i++) {
var brandName = brands[i].brand || brands[i], var brandName = brands[i].brand || brands[i],
brandVersion = brands[i].version; brandVersion = brands[i].version;
if (this.itemType == UA_BROWSER && if (this.itemType == BROWSER &&
!/not.a.brand/i.test(brandName) && !/not.a.brand/i.test(brandName) &&
(!prevName || (!prevName ||
(/Chrom/.test(prevName) && brandName != CHROMIUM) || (/Chrom/.test(prevName) && brandName != CHROMIUM) ||
@@ -1286,20 +1300,20 @@
} }
prevName = brandName; prevName = brandName;
} }
if (this.itemType == UA_ENGINE && brandName == CHROMIUM) { if (this.itemType == ENGINE && brandName == CHROMIUM) {
this.set(VERSION, brandVersion); this.set(VERSION, brandVersion);
} }
} }
} }
break; break;
case UA_CPU: case CPU:
var archName = uaCH[ARCHITECTURE]; var archName = uaCH[ARCHITECTURE];
if (archName) { if (archName) {
if (archName && uaCH[BITNESS] == '64') archName += '64'; if (archName && uaCH[BITNESS] == '64') archName += '64';
rgxMapper.call(this.data, archName + ';', rgxMap); rgxMapper.call(this.data, archName + ';', rgxMap);
} }
break; break;
case UA_DEVICE: case DEVICE:
if (uaCH[MOBILE]) { if (uaCH[MOBILE]) {
this.set(TYPE, MOBILE); this.set(TYPE, MOBILE);
} }
@@ -1329,7 +1343,7 @@
this.set(TYPE, ff); this.set(TYPE, ff);
} }
break; break;
case UA_OS: case OS:
var osName = uaCH[PLATFORM]; var osName = uaCH[PLATFORM];
if(osName) { if(osName) {
var osVersion = uaCH[PLATFORMVER]; var osVersion = uaCH[PLATFORMVER];
@@ -1343,7 +1357,7 @@
.set(VERSION, undefined); .set(VERSION, undefined);
} }
break; break;
case UA_RESULT: case RESULT:
var data = this.data; var data = this.data;
var parse = function (itemType) { var parse = function (itemType) {
return data[itemType] return data[itemType]
@@ -1352,11 +1366,11 @@
.parseCH() .parseCH()
.get(); .get();
}; };
this.set(UA_BROWSER, parse(UA_BROWSER)) this.set(BROWSER, parse(BROWSER))
.set(UA_CPU, parse(UA_CPU)) .set(CPU, parse(CPU))
.set(UA_DEVICE, parse(UA_DEVICE)) .set(DEVICE, parse(DEVICE))
.set(UA_ENGINE, parse(UA_ENGINE)) .set(ENGINE, parse(ENGINE))
.set(UA_OS, parse(UA_OS)); .set(OS, parse(OS));
} }
return this; return this;
}; };
@@ -1374,9 +1388,9 @@
function UAParser (ua, extensions, headers) { function UAParser (ua, extensions, headers) {
if (typeof ua === OBJ_TYPE) { if (typeof ua === TYPEOF.OBJECT) {
if (isExtensions(ua, true)) { if (isExtensions(ua, true)) {
if (typeof extensions === OBJ_TYPE) { if (typeof extensions === TYPEOF.OBJECT) {
headers = extensions; // case UAParser(extensions, headers) headers = extensions; // case UAParser(extensions, headers)
} }
extensions = ua; // case UAParser(extensions) extensions = ua; // case UAParser(extensions)
@@ -1385,13 +1399,13 @@
extensions = undefined; extensions = undefined;
} }
ua = undefined; ua = undefined;
} else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) { } else if (typeof ua === TYPEOF.STRING && !isExtensions(extensions, true)) {
headers = extensions; // case UAParser(ua, headers) headers = extensions; // case UAParser(ua, headers)
extensions = undefined; extensions = undefined;
} }
if (headers) { if (headers) {
if (typeof headers.append === FUNC_TYPE) { if (typeof headers.append === TYPEOF.FUNCTION) {
// Convert Headers object into a plain object // Convert Headers object into a plain object
var kv = {}; var kv = {};
headers.forEach(function (v, k) { kv[String(k).toLowerCase()] = v; }); headers.forEach(function (v, k) { kv[String(k).toLowerCase()] = v; });
@@ -1412,7 +1426,7 @@
return new UAParser(ua, extensions, headers).getResult(); return new UAParser(ua, extensions, headers).getResult();
} }
var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string var userAgent = typeof ua === TYPEOF.STRING ? ua : // Passed user-agent string
(headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers
((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent
EMPTY)), // empty string EMPTY)), // empty string
@@ -1423,15 +1437,15 @@
defaultRegexes, defaultRegexes,
createItemFunc = function (itemType) { createItemFunc = function (itemType) {
if (itemType == UA_RESULT) { if (itemType == RESULT) {
return function () { return function () {
return new UAItem(itemType, userAgent, regexMap, httpUACH) return new UAItem(itemType, userAgent, regexMap, httpUACH)
.set('ua', userAgent) .set('ua', userAgent)
.set(UA_BROWSER, this.getBrowser()) .set(BROWSER, this.getBrowser())
.set(UA_CPU, this.getCPU()) .set(CPU, this.getCPU())
.set(UA_DEVICE, this.getDevice()) .set(DEVICE, this.getDevice())
.set(UA_ENGINE, this.getEngine()) .set(ENGINE, this.getEngine())
.set(UA_OS, this.getOS()) .set(OS, this.getOS())
.get(); .get();
}; };
} else { } else {
@@ -1445,16 +1459,15 @@
// public methods // public methods
setProps.call(this, [ setProps.call(this, [
['getBrowser', createItemFunc(UA_BROWSER)], ['getBrowser', createItemFunc(BROWSER)],
['getCPU', createItemFunc(UA_CPU)], ['getCPU', createItemFunc(CPU)],
['getDevice', createItemFunc(UA_DEVICE)], ['getDevice', createItemFunc(DEVICE)],
['getEngine', createItemFunc(UA_ENGINE)], ['getEngine', createItemFunc(ENGINE)],
['getOS', createItemFunc(UA_OS)], ['getOS', createItemFunc(OS)],
['getResult', createItemFunc(UA_RESULT)], ['getResult', createItemFunc(RESULT)],
['getUA', function () { return userAgent; }], ['getUA', function () { return userAgent; }],
['setUA', function (ua) { ['setUA', function (ua) {
if (isString(ua)) if (isString(ua)) userAgent = trim(ua, UA_MAX_LENGTH);
userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
return this; return this;
}] }]
]) ])
@@ -1474,15 +1487,15 @@
////////// //////////
// check js environment // check js environment
if (typeof exports !== UNDEF_TYPE) { if (typeof exports !== TYPEOF.UNDEFINED) {
// nodejs env // nodejs env
if (typeof module !== UNDEF_TYPE && module.exports) { if (typeof module !== TYPEOF.UNDEFINED && module.exports) {
exports = module.exports = UAParser; exports = module.exports = UAParser;
} }
exports.UAParser = UAParser; exports.UAParser = UAParser;
} else { } else {
// requirejs env (optional) // requirejs env (optional)
if (typeof define === FUNC_TYPE && define.amd) { if (typeof define === TYPEOF.FUNCTION && define.amd) {
define(function () { define(function () {
return UAParser; return UAParser;
}); });

View File

@@ -3,7 +3,7 @@
// Source: /src/main/ua-parser.js // Source: /src/main/ua-parser.js
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v2.0.5 /* UAParser.js v2.0.6
Copyright © 2012-2025 Faisal Salman <f@faisalman.com> Copyright © 2012-2025 Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License *//* UAParser.js PRO Business License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
@@ -21,25 +21,25 @@
// Constants // Constants
///////////// /////////////
var LIBVERSION = '2.0.5', var LIBVERSION = '2.0.6',
UA_MAX_LENGTH = 500, UA_MAX_LENGTH = 500,
USER_AGENT = 'user-agent', USER_AGENT = 'user-agent',
EMPTY = '', EMPTY = '',
UNKNOWN = '?', UNKNOWN = '?',
TYPEOF = {
// typeof FUNCTION : 'function',
FUNC_TYPE = 'function', OBJECT : 'object',
UNDEF_TYPE = 'undefined', STRING : 'string',
OBJ_TYPE = 'object', UNDEFINED : 'undefined'
STR_TYPE = 'string', },
// properties // properties
UA_BROWSER = 'browser', BROWSER = 'browser',
UA_CPU = 'cpu', CPU = 'cpu',
UA_DEVICE = 'device', DEVICE = 'device',
UA_ENGINE = 'engine', ENGINE = 'engine',
UA_OS = 'os', OS = 'os',
UA_RESULT = 'result', RESULT = 'result',
NAME = 'name', NAME = 'name',
TYPE = 'type', TYPE = 'type',
@@ -68,15 +68,15 @@
PLATFORM = 'platform', PLATFORM = 'platform',
PLATFORMVER = 'platformVersion', PLATFORMVER = 'platformVersion',
BITNESS = 'bitness', BITNESS = 'bitness',
CH_HEADER = 'sec-ch-ua', CH = 'sec-ch-ua',
CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_FULL_VER_LIST= CH + '-full-version-list',
CH_HEADER_ARCH = CH_HEADER + '-arch', CH_ARCH = CH + '-arch',
CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, CH_BITNESS = CH + '-' + BITNESS,
CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors', CH_FORM_FACTORS = CH + '-form-factors',
CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, CH_MOBILE = CH + '-' + MOBILE,
CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, CH_MODEL = CH + '-' + MODEL,
CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_PLATFORM = CH + '-' + PLATFORM,
CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', CH_PLATFORM_VER = CH_PLATFORM + '-version',
CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS], CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS],
// device vendors // device vendors
@@ -116,7 +116,7 @@
// os // os
WINDOWS = 'Windows'; WINDOWS = 'Windows';
var isWindow = typeof window !== UNDEF_TYPE, var isWindow = typeof window !== TYPEOF.UNDEFINED,
NAVIGATOR = (isWindow && window.navigator) ? NAVIGATOR = (isWindow && window.navigator) ?
window.navigator : window.navigator :
undefined, undefined,
@@ -152,7 +152,7 @@
return enums; return enums;
}, },
has = function (str1, str2) { has = function (str1, str2) {
if (typeof str1 === OBJ_TYPE && str1.length > 0) { if (typeof str1 === TYPEOF.OBJECT && str1.length > 0) {
for (var i in str1) { for (var i in str1) {
if (lowerize(str2) == lowerize(str1[i])) return true; if (lowerize(str2) == lowerize(str1[i])) return true;
} }
@@ -166,7 +166,7 @@
} }
}, },
isString = function (val) { isString = function (val) {
return typeof val === STR_TYPE; return typeof val === TYPEOF.STRING;
}, },
itemListToArray = function (header) { itemListToArray = function (header) {
if (!header) return undefined; if (!header) return undefined;
@@ -193,7 +193,7 @@
if (!arr.hasOwnProperty(i)) continue; if (!arr.hasOwnProperty(i)) continue;
var propName = arr[i]; var propName = arr[i];
if (typeof propName == OBJ_TYPE && propName.length == 2) { if (typeof propName == TYPEOF.OBJECT && propName.length == 2) {
this[propName[0]] = propName[1]; this[propName[0]] = propName[1];
} else { } else {
this[propName] = undefined; this[propName] = undefined;
@@ -208,10 +208,8 @@
return strip(/\\?\"/g, str); return strip(/\\?\"/g, str);
}, },
trim = function (str, len) { trim = function (str, len) {
if (isString(str)) { str = strip(/^\s\s*/, String(str));
str = strip(/^\s\s*/, str); return typeof len === TYPEOF.UNDEFINED ? str : str.substring(0, len);
return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
}
}; };
/////////////// ///////////////
@@ -242,9 +240,9 @@
match = matches[++k]; match = matches[++k];
q = props[p]; q = props[p];
// check if given property is actually array // check if given property is actually array
if (typeof q === OBJ_TYPE && q.length > 0) { if (typeof q === TYPEOF.OBJECT && q.length > 0) {
if (q.length === 2) { if (q.length === 2) {
if (typeof q[1] == FUNC_TYPE) { if (typeof q[1] == TYPEOF.FUNCTION) {
// assign modified match // assign modified match
this[q[0]] = q[1].call(this, match); this[q[0]] = q[1].call(this, match);
} else { } else {
@@ -253,7 +251,7 @@
} }
} else if (q.length >= 3) { } else if (q.length >= 3) {
// Check whether q[1] FUNCTION or REGEX // Check whether q[1] FUNCTION or REGEX
if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) { if (typeof q[1] === TYPEOF.FUNCTION && !(q[1].exec && q[1].test)) {
if (q.length > 3) { if (q.length > 3) {
this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined; this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined;
} else { } else {
@@ -285,7 +283,7 @@
for (var i in map) { for (var i in map) {
// check if current value is array // check if current value is array
if (typeof map[i] === OBJ_TYPE && map[i].length > 0) { if (typeof map[i] === TYPEOF.OBJECT && map[i].length > 0) {
for (var j = 0; j < map[i].length; j++) { for (var j = 0; j < map[i].length; j++) {
if (has(map[i][j], str)) { if (has(map[i][j], str)) {
return (i === UNKNOWN) ? undefined : i; return (i === UNKNOWN) ? undefined : i;
@@ -379,10 +377,11 @@
/(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet
/(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer /(?: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 // 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)\/([-\w\.]+)/i, /(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 // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
/(heytap|ovi|115|surf)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf /(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 /(ecosia|weibo)(?:__| \w+@)([\d\.]+)/i // Ecosia/Weibo
], [NAME, VERSION], [ ], [NAME, VERSION], [
/quark(?:pc)?\/([-\w\.]+)/i // Quark /quark(?:pc)?\/([-\w\.]+)/i // Quark
@@ -451,6 +450,7 @@
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
/(alipay)client\/([\w\.]+)/i, // Alipay /(alipay)client\/([\w\.]+)/i, // Alipay
/(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter
/(bing)(?:web|sapphire)\/([\w\.]+)/i, // Bing
/(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna /(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna
], [NAME, VERSION, [TYPE, INAPP]], [ ], [NAME, VERSION, [TYPE, INAPP]], [
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
@@ -508,10 +508,10 @@
/(swiftfox)/i, // Swiftfox /(swiftfox)/i, // Swiftfox
/(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i, /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i,
// IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
/(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i, /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|basilisk|waterfox)\/([-\w\.]+)$/i,
// Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
/(firefox)\/([\w\.]+)/i, // Other Firefox-based /(firefox)\/([\w\.]+)/i, // Other Firefox-based
/(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla /(mozilla)\/([\w\.]+(?= .+rv\:.+gecko\/\d+)|[0-4][\w\.]+(?!.+compatible))/i, // Mozilla
// Other // Other
/(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, /(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
@@ -549,6 +549,8 @@
/((ppc|powerpc)(64)?)( mac|;|\))/i, // PowerPC /((ppc|powerpc)(64)?)( mac|;|\))/i, // PowerPC
/(?:osf1|[freopnt]{3,4}bsd) (alpha)/i // Alpha /(?:osf1|[freopnt]{3,4}bsd) (alpha)/i // Alpha
], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [ ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [
/mc680.0/i
], [[ARCHITECTURE, '68k']], [
/winnt.+\[axp/i /winnt.+\[axp/i
], [[ARCHITECTURE, 'alpha']] ], [[ARCHITECTURE, 'alpha']]
], ],
@@ -568,11 +570,10 @@
], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [
// Apple // Apple
/(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone /(?:\/|\()(ip(?:hone|od)[\w, ]*)[\/\);]/i // iPod/iPhone
], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [ ], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [
/\((ipad);[-\w\),; ]+apple/i, // iPad /\b(?:ios|apple\w+)\/.+[\(\/](ipad)/i, // iPad
/applecoremedia\/[\w\.]+ \((ipad)/i, /\b(ipad)[\d,]*[;\] ].+(mac |i(pad)?)os/i
/\b(ipad)\d\d?,\d\d?[;\]].+ios/i
], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [ ], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [
/(macintosh);/i /(macintosh);/i
], [MODEL, [VENDOR, APPLE]], [ ], [MODEL, [VENDOR, APPLE]], [
@@ -590,13 +591,13 @@
// Huawei // Huawei
/\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i /\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i
], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [ ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [
/(?:huawei)([-\w ]+)[;\)]/i, /(?:huawei) ?([-\w ]+)[;\)]/i,
/\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][\dc][adnt]?)\b(?!.+d\/s)/i
], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [ ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
// Xiaomi // Xiaomi
/oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i, /oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i,
/\b((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets /\b(?:xiao)?((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [ ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
/\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
@@ -604,7 +605,7 @@
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
/\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi /\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 /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 lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/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, // Xiaomi Mi
/ ([\w ]+) miui\/v?\d/i / ([\w ]+) miui\/v?\d/i
], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
@@ -660,7 +661,7 @@
/(nokia) (t[12][01])/i /(nokia) (t[12][01])/i
], [VENDOR, MODEL, [TYPE, TABLET]], [ ], [VENDOR, MODEL, [TYPE, TABLET]], [
/(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i, /(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i,
/nokia[-_ ]?(([-\w\. ]*))/i /nokia[-_ ]?(([-\w\. ]*?))( bui|\)|;|\/)/i
], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [ ], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [
// Google // Google
@@ -691,7 +692,7 @@
/(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook /(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook
], [MODEL, VENDOR, [TYPE, TABLET]], [ ], [MODEL, VENDOR, [TYPE, TABLET]], [
/\b((?:bb[a-f]|st[hv])100-\d)/i, /\b((?:bb[a-f]|st[hv])100-\d)/i,
/\(bb10; (\w+)/i // BlackBerry 10 /(?:blackberry|\(bb10;) (\w+)/i
], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [ ], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [
// Asus // Asus
@@ -771,10 +772,12 @@
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i, /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i,
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan
/; (blu|hmd|imo|infinix|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL/Wiko
/; (blu|hmd|imo|infinix|lava|oneplus|tcl|wiko)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i,
/(hp) ([\w ]+\w)/i, // HP iPAQ /(hp) ([\w ]+\w)/i, // HP iPAQ
/(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia
/(oppo) ?([\w ]+) bui/i, // OPPO /(oppo) ?([\w ]+) bui/i, // OPPO
/(hisense) ([ehv][\w ]+)\)/i, // Hisense
/droid[^;]+; (philips)[_ ]([sv-x][\d]{3,4}[xz]?)/i // Philips /droid[^;]+; (philips)[_ ]([sv-x][\d]{3,4}[xz]?)/i // Philips
], [VENDOR, MODEL, [TYPE, MOBILE]], [ ], [VENDOR, MODEL, [TYPE, MOBILE]], [
@@ -914,7 +917,7 @@
], [MODEL, [TYPE, SMARTTV]], [ ], [MODEL, [TYPE, SMARTTV]], [
/\b((4k|android|smart|opera)[- ]?tv|tv; rv:|large screen[\w ]+safari)\b/i /\b((4k|android|smart|opera)[- ]?tv|tv; rv:|large screen[\w ]+safari)\b/i
], [[TYPE, SMARTTV]], [ ], [[TYPE, SMARTTV]], [
/droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+?(mobile|vr|\d) safari/i /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew|; hmsc).+?(mobile|vr|\d) safari/i
], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [ ], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [
/\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet
], [[TYPE, TABLET]], [ ], [[TYPE, TABLET]], [
@@ -967,7 +970,7 @@
// iOS/macOS // iOS/macOS
/[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
/(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i, /(?:ios;fbsv|ios(?=.+ip(?:ad|hone))|ip(?:ad|hone)(?: |.+i(?:pad)?)os)[\/ ]([\w\.]+)/i,
/cfnetwork\/.+darwin/i /cfnetwork\/.+darwin/i
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
/(mac os x) ?([\w\. ]*)/i, /(mac os x) ?([\w\. ]*)/i,
@@ -1052,27 +1055,27 @@
var defaultProps = (function () { var defaultProps = (function () {
var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
setProps.call(props.init, [ setProps.call(props.init, [
[UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]], [BROWSER, [NAME, VERSION, MAJOR, TYPE]],
[UA_CPU, [ARCHITECTURE]], [CPU, [ARCHITECTURE]],
[UA_DEVICE, [TYPE, MODEL, VENDOR]], [DEVICE, [TYPE, MODEL, VENDOR]],
[UA_ENGINE, [NAME, VERSION]], [ENGINE, [NAME, VERSION]],
[UA_OS, [NAME, VERSION]] [OS, [NAME, VERSION]]
]); ]);
setProps.call(props.isIgnore, [ setProps.call(props.isIgnore, [
[UA_BROWSER, [VERSION, MAJOR]], [BROWSER, [VERSION, MAJOR]],
[UA_ENGINE, [VERSION]], [ENGINE, [VERSION]],
[UA_OS, [VERSION]] [OS, [VERSION]]
]); ]);
setProps.call(props.isIgnoreRgx, [ setProps.call(props.isIgnoreRgx, [
[UA_BROWSER, / ?browser$/i], [BROWSER, / ?browser$/i],
[UA_OS, / ?os$/i] [OS, / ?os$/i]
]); ]);
setProps.call(props.toString, [ setProps.call(props.toString, [
[UA_BROWSER, [NAME, VERSION]], [BROWSER, [NAME, VERSION]],
[UA_CPU, [ARCHITECTURE]], [CPU, [ARCHITECTURE]],
[UA_DEVICE, [VENDOR, MODEL]], [DEVICE, [VENDOR, MODEL]],
[UA_ENGINE, [NAME, VERSION]], [ENGINE, [NAME, VERSION]],
[UA_OS, [NAME, VERSION]] [OS, [NAME, VERSION]]
]); ]);
return props; return props;
})(); })();
@@ -1116,14 +1119,14 @@
return item.detectFeature().get(); return item.detectFeature().get();
}; };
if (itemType != UA_RESULT) { if (itemType != RESULT) {
IData.prototype.is = function (strToCheck) { IData.prototype.is = function (strToCheck) {
var is = false; var is = false;
for (var i in this) { for (var i in this) {
if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) { if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) {
is = true; is = true;
if (strToCheck != UNDEF_TYPE) break; if (strToCheck != TYPEOF.UNDEFINED) break;
} else if (strToCheck == UNDEF_TYPE && is) { } else if (strToCheck == TYPEOF.UNDEFINED && is) {
is = !is; is = !is;
break; break;
} }
@@ -1133,11 +1136,11 @@
IData.prototype.toString = function () { IData.prototype.toString = function () {
var str = EMPTY; var str = EMPTY;
for (var i in toString_props) { for (var i in toString_props) {
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { if (typeof(this[toString_props[i]]) !== TYPEOF.UNDEFINED) {
str += (str ? ' ' : EMPTY) + this[toString_props[i]]; str += (str ? ' ' : EMPTY) + this[toString_props[i]];
} }
} }
return str || UNDEF_TYPE; return str || TYPEOF.UNDEFINED;
}; };
} }
@@ -1173,19 +1176,19 @@
setProps.call(this, CH_ALL_VALUES); setProps.call(this, CH_ALL_VALUES);
if (isHttpUACH) { if (isHttpUACH) {
setProps.call(this, [ setProps.call(this, [
[BRANDS, itemListToArray(uach[CH_HEADER])], [BRANDS, itemListToArray(uach[CH])],
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], [FULLVERLIST, itemListToArray(uach[CH_FULL_VER_LIST])],
[MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])], [MOBILE, /\?1/.test(uach[CH_MOBILE])],
[MODEL, stripQuotes(uach[CH_HEADER_MODEL])], [MODEL, stripQuotes(uach[CH_MODEL])],
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORM, stripQuotes(uach[CH_PLATFORM])],
[PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [PLATFORMVER, stripQuotes(uach[CH_PLATFORM_VER])],
[ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], [ARCHITECTURE, stripQuotes(uach[CH_ARCH])],
[FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])], [FORMFACTORS, itemListToArray(uach[CH_FORM_FACTORS])],
[BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] [BITNESS, stripQuotes(uach[CH_BITNESS])]
]); ]);
} else { } else {
for (var prop in uach) { for (var prop in uach) {
if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop]; if(this.hasOwnProperty(prop) && typeof uach[prop] !== TYPEOF.UNDEFINED) this[prop] = uach[prop];
} }
} }
} }
@@ -1210,30 +1213,30 @@
this.detectFeature = function () { this.detectFeature = function () {
if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) { if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
switch (this.itemType) { switch (this.itemType) {
case UA_BROWSER: case BROWSER:
// Brave-specific detection // Brave-specific detection
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
this.set(NAME, 'Brave'); this.set(NAME, 'Brave');
} }
break; break;
case UA_DEVICE: case DEVICE:
// Chrome-specific detection: check for 'mobile' value of navigator.userAgentData // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
this.set(TYPE, MOBILE); this.set(TYPE, MOBILE);
} }
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties // iPadOS-specific detection: identified as Mac, but has some iOS-only properties
if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
this.set(MODEL, 'iPad') this.set(MODEL, 'iPad')
.set(TYPE, TABLET); .set(TYPE, TABLET);
} }
break; break;
case UA_OS: case OS:
// Chrome-specific detection: check for 'platform' value of navigator.userAgentData // Chrome-specific detection: check for 'platform' value of navigator.userAgentData
if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
} }
break; break;
case UA_RESULT: case RESULT:
var data = this.data; var data = this.data;
var detect = function (itemType) { var detect = function (itemType) {
return data[itemType] return data[itemType]
@@ -1241,22 +1244,33 @@
.detectFeature() .detectFeature()
.get(); .get();
}; };
this.set(UA_BROWSER, detect(UA_BROWSER)) this.set(BROWSER, detect(BROWSER))
.set(UA_CPU, detect(UA_CPU)) .set(CPU, detect(CPU))
.set(UA_DEVICE, detect(UA_DEVICE)) .set(DEVICE, detect(DEVICE))
.set(UA_ENGINE, detect(UA_ENGINE)) .set(ENGINE, detect(ENGINE))
.set(UA_OS, detect(UA_OS)); .set(OS, detect(OS));
} }
} }
return this; return this;
}; };
this.parseUA = function () { this.parseUA = function () {
if (this.itemType != UA_RESULT) { if (this.itemType != RESULT) {
rgxMapper.call(this.data, this.ua, this.rgxMap); rgxMapper.call(this.data, this.ua, this.rgxMap);
} }
if (this.itemType == UA_BROWSER) { switch (this.itemType) {
case BROWSER:
this.set(MAJOR, majorize(this.get(VERSION))); 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; return this;
}; };
@@ -1266,14 +1280,14 @@
rgxMap = this.rgxMap; rgxMap = this.rgxMap;
switch (this.itemType) { switch (this.itemType) {
case UA_BROWSER: case BROWSER:
case UA_ENGINE: case ENGINE:
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName; var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
if (brands) { if (brands) {
for (var i=0; i<brands.length; i++) { for (var i=0; i<brands.length; i++) {
var brandName = brands[i].brand || brands[i], var brandName = brands[i].brand || brands[i],
brandVersion = brands[i].version; brandVersion = brands[i].version;
if (this.itemType == UA_BROWSER && if (this.itemType == BROWSER &&
!/not.a.brand/i.test(brandName) && !/not.a.brand/i.test(brandName) &&
(!prevName || (!prevName ||
(/Chrom/.test(prevName) && brandName != CHROMIUM) || (/Chrom/.test(prevName) && brandName != CHROMIUM) ||
@@ -1288,20 +1302,20 @@
} }
prevName = brandName; prevName = brandName;
} }
if (this.itemType == UA_ENGINE && brandName == CHROMIUM) { if (this.itemType == ENGINE && brandName == CHROMIUM) {
this.set(VERSION, brandVersion); this.set(VERSION, brandVersion);
} }
} }
} }
break; break;
case UA_CPU: case CPU:
var archName = uaCH[ARCHITECTURE]; var archName = uaCH[ARCHITECTURE];
if (archName) { if (archName) {
if (archName && uaCH[BITNESS] == '64') archName += '64'; if (archName && uaCH[BITNESS] == '64') archName += '64';
rgxMapper.call(this.data, archName + ';', rgxMap); rgxMapper.call(this.data, archName + ';', rgxMap);
} }
break; break;
case UA_DEVICE: case DEVICE:
if (uaCH[MOBILE]) { if (uaCH[MOBILE]) {
this.set(TYPE, MOBILE); this.set(TYPE, MOBILE);
} }
@@ -1331,7 +1345,7 @@
this.set(TYPE, ff); this.set(TYPE, ff);
} }
break; break;
case UA_OS: case OS:
var osName = uaCH[PLATFORM]; var osName = uaCH[PLATFORM];
if(osName) { if(osName) {
var osVersion = uaCH[PLATFORMVER]; var osVersion = uaCH[PLATFORMVER];
@@ -1345,7 +1359,7 @@
.set(VERSION, undefined); .set(VERSION, undefined);
} }
break; break;
case UA_RESULT: case RESULT:
var data = this.data; var data = this.data;
var parse = function (itemType) { var parse = function (itemType) {
return data[itemType] return data[itemType]
@@ -1354,11 +1368,11 @@
.parseCH() .parseCH()
.get(); .get();
}; };
this.set(UA_BROWSER, parse(UA_BROWSER)) this.set(BROWSER, parse(BROWSER))
.set(UA_CPU, parse(UA_CPU)) .set(CPU, parse(CPU))
.set(UA_DEVICE, parse(UA_DEVICE)) .set(DEVICE, parse(DEVICE))
.set(UA_ENGINE, parse(UA_ENGINE)) .set(ENGINE, parse(ENGINE))
.set(UA_OS, parse(UA_OS)); .set(OS, parse(OS));
} }
return this; return this;
}; };
@@ -1376,9 +1390,9 @@
function UAParser (ua, extensions, headers) { function UAParser (ua, extensions, headers) {
if (typeof ua === OBJ_TYPE) { if (typeof ua === TYPEOF.OBJECT) {
if (isExtensions(ua, true)) { if (isExtensions(ua, true)) {
if (typeof extensions === OBJ_TYPE) { if (typeof extensions === TYPEOF.OBJECT) {
headers = extensions; // case UAParser(extensions, headers) headers = extensions; // case UAParser(extensions, headers)
} }
extensions = ua; // case UAParser(extensions) extensions = ua; // case UAParser(extensions)
@@ -1387,13 +1401,13 @@
extensions = undefined; extensions = undefined;
} }
ua = undefined; ua = undefined;
} else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) { } else if (typeof ua === TYPEOF.STRING && !isExtensions(extensions, true)) {
headers = extensions; // case UAParser(ua, headers) headers = extensions; // case UAParser(ua, headers)
extensions = undefined; extensions = undefined;
} }
if (headers) { if (headers) {
if (typeof headers.append === FUNC_TYPE) { if (typeof headers.append === TYPEOF.FUNCTION) {
// Convert Headers object into a plain object // Convert Headers object into a plain object
var kv = {}; var kv = {};
headers.forEach(function (v, k) { kv[String(k).toLowerCase()] = v; }); headers.forEach(function (v, k) { kv[String(k).toLowerCase()] = v; });
@@ -1414,7 +1428,7 @@
return new UAParser(ua, extensions, headers).getResult(); return new UAParser(ua, extensions, headers).getResult();
} }
var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string var userAgent = typeof ua === TYPEOF.STRING ? ua : // Passed user-agent string
(headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers
((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent
EMPTY)), // empty string EMPTY)), // empty string
@@ -1425,15 +1439,15 @@
defaultRegexes, defaultRegexes,
createItemFunc = function (itemType) { createItemFunc = function (itemType) {
if (itemType == UA_RESULT) { if (itemType == RESULT) {
return function () { return function () {
return new UAItem(itemType, userAgent, regexMap, httpUACH) return new UAItem(itemType, userAgent, regexMap, httpUACH)
.set('ua', userAgent) .set('ua', userAgent)
.set(UA_BROWSER, this.getBrowser()) .set(BROWSER, this.getBrowser())
.set(UA_CPU, this.getCPU()) .set(CPU, this.getCPU())
.set(UA_DEVICE, this.getDevice()) .set(DEVICE, this.getDevice())
.set(UA_ENGINE, this.getEngine()) .set(ENGINE, this.getEngine())
.set(UA_OS, this.getOS()) .set(OS, this.getOS())
.get(); .get();
}; };
} else { } else {
@@ -1447,16 +1461,15 @@
// public methods // public methods
setProps.call(this, [ setProps.call(this, [
['getBrowser', createItemFunc(UA_BROWSER)], ['getBrowser', createItemFunc(BROWSER)],
['getCPU', createItemFunc(UA_CPU)], ['getCPU', createItemFunc(CPU)],
['getDevice', createItemFunc(UA_DEVICE)], ['getDevice', createItemFunc(DEVICE)],
['getEngine', createItemFunc(UA_ENGINE)], ['getEngine', createItemFunc(ENGINE)],
['getOS', createItemFunc(UA_OS)], ['getOS', createItemFunc(OS)],
['getResult', createItemFunc(UA_RESULT)], ['getResult', createItemFunc(RESULT)],
['getUA', function () { return userAgent; }], ['getUA', function () { return userAgent; }],
['setUA', function (ua) { ['setUA', function (ua) {
if (isString(ua)) if (isString(ua)) userAgent = trim(ua, UA_MAX_LENGTH);
userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
return this; return this;
}] }]
]) ])

View File

@@ -271,6 +271,26 @@
"major" : "11" "major" : "11"
} }
}, },
{
"desc" : "Bing",
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/605.1.15 BingSapphire/31.8.430522001",
"expect" :
{
"name" : "Bing",
"version" : "31.8.430522001",
"major" : "31"
}
},
{
"desc" : "Bing",
"ua" : "Mozilla/5.0 (Linux; Android 9; MIX 2 Build/PKQ1.190118.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4893.0 Mobile Safari/537.36 BingWeb/6.9.12",
"expect" :
{
"name" : "Bing",
"version" : "6.9.12",
"major" : "6"
}
},
{ {
"desc" : "Blazer", "desc" : "Blazer",
"ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; PalmSource/hspr-H102; Blazer/4.0) 16;320x320", "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; PalmSource/hspr-H102; Blazer/4.0) 16;320x320",
@@ -611,6 +631,16 @@
"major" : "55" "major" : "55"
} }
}, },
{
"desc" : "PaleMoon",
"ua" : "(Windows NT 6.2; WOW64) KHTML/4.11 Gecko/20130308 Firefox/23.0 (PaleMoon/20.3)",
"expect" :
{
"name" : "PaleMoon",
"version" : "20.3",
"major" : "20"
}
},
{ {
"desc" : "PaleMoon", "desc" : "PaleMoon",
"ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:52.9) Gecko/20100101 Goanna/3.4 Firefox/52.9 PaleMoon/27.6.1", "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:52.9) Gecko/20100101 Goanna/3.4 Firefox/52.9 PaleMoon/27.6.1",
@@ -1338,6 +1368,16 @@
"major" : "5" "major" : "5"
} }
}, },
{
"desc" : "Mozilla",
"ua" : "Mozilla/2.02 [fr] (WinNT; I)",
"expect" :
{
"name" : "Mozilla",
"version" : "2.02",
"major" : "2"
}
},
{ {
"desc" : "MSIE", "desc" : "MSIE",
"ua" : "Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC)", "ua" : "Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC)",
@@ -1688,6 +1728,36 @@
"major" : "2" "major" : "2"
} }
}, },
{
"desc" : "Qwant",
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) QwantMobile/6.7.6 Mobile/15E148 Safari/605.1.15",
"expect" :
{
"name" : "Qwant",
"version" : "6.7.6",
"major" : "6"
}
},
{
"desc" : "Qwant",
"ua" : "QwantMobile/2.0 (Android 8.0.0; Mobile; rv:59.0) Gecko/59.0 Firefox/59.0 QwantBrowser/59.0",
"expect" :
{
"name" : "Qwant",
"version" : "59.0",
"major" : "59"
}
},
{
"desc" : "Qwant",
"ua" : "QwantMobile/2.0 (iPad; CPU OS 15_8_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) QwantiOS/2.7.0b1 Mobile/15E148 Safari/605.1.15",
"expect" :
{
"name" : "Qwant",
"version" : "2.0",
"major" : "2"
}
},
{ {
"desc" : "Rekonq 2", "desc" : "Rekonq 2",
"ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.21 (KHTML, like Gecko) rekonq/2.2.1 Safari/537.21", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.21 (KHTML, like Gecko) rekonq/2.2.1 Safari/537.21",

View File

@@ -289,7 +289,15 @@
}, },
{ {
"desc" : "68k", "desc" : "68k",
"ua" : "'Mozilla/1.1 (Macintosh; U; 68K)'", "ua" : "Mozilla/1.1 (Macintosh; U; 68K)",
"expect" :
{
"architecture" : "68k"
}
},
{
"desc" : "MC680x0",
"ua" : "AmigaVoyager/3.2 (AmigaOS/MC680x0)",
"expect" : "expect" :
{ {
"architecture" : "68k" "architecture" : "68k"

View File

@@ -44,6 +44,105 @@
"type": "tablet" "type": "tablet"
} }
}, },
{
"desc": "iPad using Chrome",
"ua": "Mozilla/5.0 (iPad13,10; CPU OS 16_6_2 like Mac OS X) AppleWebKit/602.3 (KHTML, like Gecko) CriOS/97.0.0.5927.72 Mobile/11S296 Safari/602.3",
"expect": {
"vendor": "Apple",
"model": "iPad",
"type": "tablet"
}
},
{
"desc": "iPad using DuckDuckGo",
"ua": "Mozilla/5.0 (iPad; iPad13,6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) DuckDuckGo/130.0.0.0 Mobile/15E148 Safari/604.1",
"expect": {
"vendor": "Apple",
"model": "iPad",
"type": "tablet"
}
},
{
"desc": "iPad using Discord",
"ua": "Discord/52.0 (iPad; iOS 14.4; Scale/2.00)",
"expect": {
"vendor": "Apple",
"model": "iPad",
"type": "tablet"
}
},
{
"desc": "iPad using Firefox",
"ua": "Mozilla/5.0 (iPad; iPad14,2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Firefox/120.0 Mobile/15E148 Safari/604.1",
"expect": {
"vendor": "Apple",
"model": "iPad",
"type": "tablet"
}
},
{
"desc": "iPad using iTunes",
"ua": "itunesstored/1.0 iOS/15.6.1 model/iPad13,8 hwp/t8103 build/19G82 (5; dt:238)",
"expect": {
"vendor": "Apple",
"model": "iPad",
"type": "tablet"
}
},
{
"desc": "iPad using MS OneNote",
"ua": "Microsoft Office OneNote/16.81/240108 (iOS/16.5.1; Tablet; AppStore; Apple/iPad8,3)",
"expect": {
"vendor": "Apple",
"model": "iPad",
"type": "tablet"
}
},
{
"desc": "iPad using MS Word",
"ua": "Microsoft Office Word/2.44.1211 (iOS/13.7; Tablet; es-MX; AppStore; Apple/iPad11,3)",
"expect": {
"vendor": "Apple",
"model": "iPad",
"type": "tablet"
}
},
{
"desc": "iPad using Quora",
"ua": "Quora 8.4.30 rv:3230 env:prod (iPad11,3; iPadOS 17.7; en_GB) AppleWebKit",
"expect": {
"vendor": "Apple",
"model": "iPad",
"type": "tablet"
}
},
{
"desc": "iPad using TuneIn Radio",
"ua": "TuneIn Radio/27.1.0; iPad6,3; iPadOS/16.6",
"expect": {
"vendor": "Apple",
"model": "iPad",
"type": "tablet"
}
},
{
"desc": "iPad using TuneIn Radio Pro",
"ua": "TuneIn Radio Pro/21.4.1; iPad8,9; iOS/15.0",
"expect": {
"vendor": "Apple",
"model": "iPad",
"type": "tablet"
}
},
{
"desc": "iPad using YouTube",
"ua": "com.google.ios.youtube/20.31.6 (iPad13,5; U; CPU iPadOS 18_6 like Mac OS X; en_US)",
"expect": {
"vendor": "Apple",
"model": "iPad",
"type": "tablet"
}
},
{ {
"desc": "iPod", "desc": "iPod",
"ua": "Mozilla/5.0 (iPod touch; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53", "ua": "Mozilla/5.0 (iPod touch; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53",
@@ -62,6 +161,15 @@
"type": "mobile" "type": "mobile"
} }
}, },
{
"desc": "iPhone using Spotify",
"ua": "Spotify/8.7.70 iOS/16.0 (iPhone15,3)",
"expect": {
"vendor": "Apple",
"model": "iPhone15,3",
"type": "mobile"
}
},
{ {
"desc": "iPhone SE", "desc": "iPhone SE",
"ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone8,4;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone8,4;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]",

View File

@@ -1,7 +1,25 @@
[ [
{
"desc": "BlackBerry 9650",
"ua": "Mozilla/5.0 (BlackBerry; U; BlackBerry 9650; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.524 Mobile Safari/534.8+",
"expect": {
"vendor": "BlackBerry",
"model": "9650",
"type": "mobile"
}
},
{
"desc": "BlackBerry 9780",
"ua": "Mozilla/5.0 (BlackBerry; U; BlackBerry 9780; en) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.546 Mobile Safari/534.8+",
"expect": {
"vendor": "BlackBerry",
"model": "9780",
"type": "mobile"
}
},
{ {
"desc": "BlackBerry Priv", "desc": "BlackBerry Priv",
"ua": "User-Agent: Mozilla/5.0 (Linux; Android 5.1.1; STV100-1 Build/LMY47V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/46.0.2490.76 Mobile Safari/537.36", "ua": "Mozilla/5.0 (Linux; Android 5.1.1; STV100-1 Build/LMY47V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/46.0.2490.76 Mobile Safari/537.36",
"expect": { "expect": {
"vendor": "BlackBerry", "vendor": "BlackBerry",
"model": "STV100-1", "model": "STV100-1",
@@ -28,7 +46,7 @@
}, },
{ {
"desc": "BlackBerry Key2 LE", "desc": "BlackBerry Key2 LE",
"ua": "User-Agent: Mozilla/5.0 (Linux; Android 8.1.0; BBE100-1 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497 Mobile Safari/537.36", "ua": "Mozilla/5.0 (Linux; Android 8.1.0; BBE100-1 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497 Mobile Safari/537.36",
"expect": { "expect": {
"vendor": "BlackBerry", "vendor": "BlackBerry",
"model": "BBE100-1", "model": "BBE100-1",

View File

@@ -0,0 +1,20 @@
[
{
"desc": "Hisense E50 Lite",
"ua": "Mozilla/5.0 (Linux; Android 11; Hisense E50 Lite) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.126 Mobile Safari/537.36",
"expect": {
"vendor": "Hisense",
"model": "E50 Lite",
"type": "mobile"
}
},
{
"desc": "Hisense V40s",
"ua": "Mozilla/5.0 (Linux; Android 11; Hisense V40s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.125 Mobile Safari/537.36",
"expect": {
"vendor": "Hisense",
"model": "V40s",
"type": "mobile"
}
}
]

View File

@@ -701,6 +701,15 @@
"type": "mobile" "type": "mobile"
} }
}, },
{
"desc": "Huawei P10 Lite",
"ua": "Mozilla/5.0 (Linux; Android 8.0.0; WAS-L03T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5199.205 Mobile Safari/537.36",
"expect": {
"vendor": "Huawei",
"model": "WAS-L03T",
"type": "mobile"
}
},
{ {
"desc": "Huawei P20 Lite", "desc": "Huawei P20 Lite",
"ua": "Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 Mobile Safari/537.36", "ua": "Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 Mobile Safari/537.36",
@@ -728,6 +737,15 @@
"type": "mobile" "type": "mobile"
} }
}, },
{
"desc": "Huawei P30",
"ua": "Mozilla/5.0 (Linux; Android 10; ELE-L04) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.109 Mobile Safari/537.36",
"expect": {
"vendor": "Huawei",
"model": "ELE-L04",
"type": "mobile"
}
},
{ {
"desc": "Huawei P30", "desc": "Huawei P30",
"ua": "Mozilla/5.0 (Linux; Android 9; ELE-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36", "ua": "Mozilla/5.0 (Linux; Android 9; ELE-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36",
@@ -782,6 +800,15 @@
"type": "mobile" "type": "mobile"
} }
}, },
{
"desc": "Huawei Nova",
"ua": "Mozilla/5.0 (Linux; Android 7.0; HUAWEI CAN-L13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5113.212 Mobile Safari/537.36",
"expect": {
"vendor": "Huawei",
"model": "CAN-L13",
"type": "mobile"
}
},
{ {
"desc": "Huawei Nova 5T", "desc": "Huawei Nova 5T",
"ua": "Mozilla/5.0 (Linux; Android 10; YAL-L21) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L21) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36",
@@ -899,6 +926,15 @@
"type": "mobile" "type": "mobile"
} }
}, },
{
"desc": "Huawei Y7p",
"ua": "Mozilla/5.0 (Linux; Android 10; ART-L28; HMSCore 6.8.0.311) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4476.0 HuaweiBrowser/12.1.2.312 Mobile Safari/537.36",
"expect": {
"vendor": "Huawei",
"model": "ART-L28",
"type": "mobile"
}
},
{ {
"desc": "Huawei Y7p", "desc": "Huawei Y7p",
"ua": "Mozilla/5.0 (Linux; Android 9; ART-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36", "ua": "Mozilla/5.0 (Linux; Android 9; ART-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36",

View File

@@ -1,4 +1,13 @@
[ [
{
"desc": "Nokia 1",
"ua": "Mozilla/5.0 (Linux; Android 10; Nokia 1 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5304.15 Mobile Safari/537.36",
"expect": {
"vendor": "Nokia",
"model": "1",
"type": "mobile"
}
},
{ {
"desc": "Nokia3xx", "desc": "Nokia3xx",
"ua": "Nokia303/14.87 CLDC-1.1", "ua": "Nokia303/14.87 CLDC-1.1",

View File

@@ -0,0 +1,29 @@
[
{
"desc": "Wiko Life 3",
"ua": "Mozilla/5.0 (Linux; Android 11; Wiko U316AT) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5199.205 Mobile Safari/537.36",
"expect": {
"vendor": "Wiko",
"model": "U316AT",
"type": "mobile"
}
},
{
"desc": "Wiko Ride 3",
"ua": "Mozilla/5.0 (Linux; Android 11; Wiko U614AS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36",
"expect": {
"vendor": "Wiko",
"model": "U614AS",
"type": "mobile"
}
},
{
"desc": "Wiko T10",
"ua": "Mozilla/5.0 (Linux; Android 11; WIKO T10 Build/RP1A.200720.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5304.62 Mobile Safari/537.36",
"expect": {
"vendor": "WIKO",
"model": "T10",
"type": "mobile"
}
}
]

View File

@@ -116,6 +116,15 @@
"type": "mobile" "type": "mobile"
} }
}, },
{
"desc": "Xiaomi Mi 11 Lite 5G",
"ua": "Mozilla/5.0 (Linux; U; Android 12; zh-CN; Mi 11 Lite 5G Build/SKQ1.211006.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.136 Mobile Safari/537.36 XiaoMi/MiuiBrowser/13.17.0-gn",
"expect": {
"vendor": "Xiaomi",
"model": "Mi 11 Lite 5G",
"type": "mobile"
}
},
{ {
"desc": "Xiaomi Mi 5s Plus", "desc": "Xiaomi Mi 5s Plus",
"ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; zh-cn; MI 5s Plus Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/8.7.1", "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; zh-cn; MI 5s Plus Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/8.7.1",
@@ -188,6 +197,15 @@
"type": "mobile" "type": "mobile"
} }
}, },
{
"desc": "Xiaomi Mi 10T",
"ua": "Mozilla/5.0 (Linux; U; Android 12; fr-CA; Mi 10T Build/SKQ1.211006.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.79 Mobile Safari/537.36 XiaoMi/MiuiBrowser/13.16.1-gn",
"expect": {
"vendor": "Xiaomi",
"model": "Mi 10T",
"type": "mobile"
}
},
{ {
"desc": "Xiaomi Mi A2", "desc": "Xiaomi Mi A2",
"ua": "Mozilla/5.0 (Linux; Android 9; Mi A2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36", "ua": "Mozilla/5.0 (Linux; Android 9; Mi A2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36",
@@ -314,6 +332,15 @@
"type": "tablet" "type": "tablet"
} }
}, },
{
"desc": "Xiaomi Pad 5",
"ua": "Mozilla/5.0 (Linux; U; Android 12; ca-ES; Xiaomi Pad 5 Build/SKQ1.220303.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.86 Mobile Safari/537.36 XiaoMi/MiuiBrowser/13.6.0-gn",
"expect": {
"vendor": "Xiaomi",
"model": "mi Pad 5",
"type": "tablet"
}
},
{ {
"desc": "Xiaomi Pad 6S Pro 12.4", "desc": "Xiaomi Pad 6S Pro 12.4",
"ua": "Mozilla/5.0 (Linux; Android 14; 24018RPACC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", "ua": "Mozilla/5.0 (Linux; Android 14; 24018RPACC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
@@ -458,6 +485,15 @@
"type": "mobile" "type": "mobile"
} }
}, },
{
"desc": "XiaoMi Redmi Note 10 Lite",
"ua": "Mozilla/5.0 (Linux; U; Android 12; es-VE; Mi Note 10 Lite Build/SKQ1.210908.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.88 Mobile Safari/537.36 XiaoMi/MiuiBrowser/13.16.1-gn",
"expect": {
"vendor": "Xiaomi",
"model": "Mi Note 10 Lite",
"type": "mobile"
}
},
{ {
"desc": "XiaoMi Redmi Note 10 Pro", "desc": "XiaoMi Redmi Note 10 Pro",
"ua": "Mozilla/5.0 (Linux; Android 13; M2101K6P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", "ua": "Mozilla/5.0 (Linux; Android 13; M2101K6P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36",

View File

@@ -965,7 +965,7 @@
"ua" : "Mozilla/5.0 (compatible; Qwantbot-news/2.0; +https://help.qwant.com/bot/)", "ua" : "Mozilla/5.0 (compatible; Qwantbot-news/2.0; +https://help.qwant.com/bot/)",
"expect" : "expect" :
{ {
"name" : "Qwantbot", "name" : "Qwantbot-news",
"version" : "2.0", "version" : "2.0",
"type" : "crawler" "type" : "crawler"
} }
@@ -1100,6 +1100,26 @@
"type" : "crawler" "type" : "crawler"
} }
}, },
{
"desc" : "SurdotlyBot",
"ua" : "Mozilla/5.0 (compatible; SurdotlyBot/1.0; +http://sur.ly/bot.html)",
"expect" :
{
"name" : "SurdotlyBot",
"version" : "1.0",
"type" : "crawler"
}
},
{
"desc" : "Swiftbot",
"ua" : "Swiftbot/1.0 (swiftype.com)",
"expect" :
{
"name" : "Swiftbot",
"version" : "1.0",
"type" : "crawler"
}
},
{ {
"desc" : "Teoma", "desc" : "Teoma",
"ua" : "Mozilla/2.0 (compatible; Ask Jeeves/Teoma; +http://sp.ask.com/docs/about/tech_crawling.html)", "ua" : "Mozilla/2.0 (compatible; Ask Jeeves/Teoma; +http://sp.ask.com/docs/about/tech_crawling.html)",

View File

@@ -51,7 +51,7 @@
}, },
{ {
"desc" : "Blueno", "desc" : "Blueno",
"ua" : "acebookexternalhit/1.1 (compatible; Blueno/1.0; +http://naver.me/scrap)", "ua" : "facebookexternalhit/1.1 (compatible; Blueno/1.0; +http://naver.me/scrap)",
"expect" : "expect" :
{ {
"name" : "Blueno", "name" : "Blueno",
@@ -119,6 +119,16 @@
"type" : "fetcher" "type" : "fetcher"
} }
}, },
{
"desc" : "Discordbot",
"ua" : "Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)",
"expect" :
{
"name" : "Discordbot",
"version" : "2.0",
"type" : "fetcher"
}
},
{ {
"desc" : "DuckAssistBot", "desc" : "DuckAssistBot",
"ua" : "DuckAssistBot/1.2; (+http://duckduckgo.com/duckassistbot.html)", "ua" : "DuckAssistBot/1.2; (+http://duckduckgo.com/duckassistbot.html)",
@@ -239,6 +249,16 @@
"type" : "fetcher" "type" : "fetcher"
} }
}, },
{
"desc" : "KeybaseBot",
"ua" : "Mozilla/5.0 (compatible; KeybaseBot; +https://keybase.io)",
"expect" :
{
"name" : "KeybaseBot",
"version" : "undefined",
"type" : "fetcher"
}
},
{ {
"desc" : "Meta-ExternalFetcher", "desc" : "Meta-ExternalFetcher",
"ua" : "meta-externalfetcher/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)", "ua" : "meta-externalfetcher/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)",
@@ -309,6 +329,36 @@
"type" : "fetcher" "type" : "fetcher"
} }
}, },
{
"desc" : "Slack-ImgProxy",
"ua" : "Slack-ImgProxy 0.19 (+https://api.slack.com/robots)",
"expect" :
{
"name" : "Slack-ImgProxy",
"version" : "0.19",
"type" : "fetcher"
}
},
{
"desc" : "Slackbot",
"ua" : "Slackbot 1.0 (+https://api.slack.com/robots)",
"expect" :
{
"name" : "Slackbot",
"version" : "1.0",
"type" : "fetcher"
}
},
{
"desc" : "Slackbot-LinkExpanding",
"ua" : "Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)",
"expect" :
{
"name" : "Slackbot-LinkExpanding",
"version" : "1.0",
"type" : "fetcher"
}
},
{ {
"desc" : "Snap URL Preview", "desc" : "Snap URL Preview",
"ua" : "Snap URL Preview Service; bot; snapchat; https://developers.snap.com/robots ", "ua" : "Snap URL Preview Service; bot; snapchat; https://developers.snap.com/robots ",
@@ -339,6 +389,16 @@
"type" : "fetcher" "type" : "fetcher"
} }
}, },
{
"desc" : "Twitterbot",
"ua" : "Twitterbot/1.0",
"expect" :
{
"name" : "Twitterbot",
"version" : "1.0",
"type" : "fetcher"
}
},
{ {
"desc" : "UptimeRobot", "desc" : "UptimeRobot",
"ua" : "Mozilla/5.0 (compatible; UptimeRobot/2.0; http://www.uptimerobot.com/)", "ua" : "Mozilla/5.0 (compatible; UptimeRobot/2.0; http://www.uptimerobot.com/)",

View File

@@ -1,4 +1,22 @@
[ [
{
"desc" : "iOS 18.6",
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.6 Mobile/15E148 Safari/604.1",
"expect" :
{
"name" : "iOS",
"version" : "18.6"
}
},
{
"desc" : "iOS 26",
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1",
"expect" :
{
"name" : "iOS",
"version" : "26.0"
}
},
{ {
"desc" : "iOS in App", "desc" : "iOS in App",
"ua" : "AppName/version CFNetwork/version Darwin/version", "ua" : "AppName/version CFNetwork/version Darwin/version",
@@ -17,6 +35,15 @@
"version" : "5.1.1" "version" : "5.1.1"
} }
}, },
{
"desc" : "iOS with DuckDuckGo",
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1 Ddg/26.0",
"expect" :
{
"name" : "iOS",
"version" : "18.7"
}
},
{ {
"desc" : "iOS with Opera Mini", "desc" : "iOS with Opera Mini",
"ua" : "Opera/9.80 (iPhone; Opera Mini/7.1.32694/27.1407; U; en) Presto/2.8.119 Version/11.10", "ua" : "Opera/9.80 (iPhone; Opera Mini/7.1.32694/27.1407; U; en) Presto/2.8.119 Version/11.10",
@@ -35,6 +62,33 @@
"version" : "13.6.1" "version" : "13.6.1"
} }
}, },
{
"desc": "iOS with Instagram",
"ua": "Instagram 5.0.2 (iPhone5,1; iPhone OS 7_0_4; en_US; en) AppleWebKit/420+",
"expect":
{
"name" : "iOS",
"version" : "7.0.4"
}
},
{
"desc": "iOS with MS Word App",
"ua": "Microsoft Office Word/2.44.1211 (iOS/13.7; Tablet; es-MX; AppStore; Apple/iPad11,3)",
"expect":
{
"name" : "iOS",
"version" : "13.7"
}
},
{
"desc": "iOS with Quora App",
"ua": "Quora 8.4.30 rv:3230 env:prod (iPad11,3; iPadOS 17.7; en_GB) AppleWebKit",
"expect":
{
"name" : "iOS",
"version" : "17.7"
}
},
{ {
"desc": "iOS with Slack App", "desc": "iOS with Slack App",
"ua": "com.tinyspeck.chatlyio/23.04.10 (iPhone; iOS 16.4.1; Scale/3.00)", "ua": "com.tinyspeck.chatlyio/23.04.10 (iPhone; iOS 16.4.1; Scale/3.00)",
@@ -44,6 +98,33 @@
"version" : "16.4.1" "version" : "16.4.1"
} }
}, },
{
"desc": "iOS with Snapchat",
"ua": "Snapchat/12.12.1.40 (iPhone15,2; iOS 16.2; gzip)",
"expect":
{
"name" : "iOS",
"version" : "16.2"
}
},
{
"desc": "iOS with Spotify App",
"ua": "Spotify/8.7.70 iOS/16.0 (iPhone15,3)",
"expect":
{
"name" : "iOS",
"version" : "16.0"
}
},
{
"desc": "iOS with TuneIn Radio App",
"ua": "TuneIn Radio/27.1.0; iPad6,3; iPadOS/16.6",
"expect":
{
"name" : "iOS",
"version" : "16.6"
}
},
{ {
"desc" : "iOS BE App", "desc" : "iOS BE App",
"ua" : "APP-BE Test/1.0 (iPad; Apple; CPU iPhone OS 7_0_2 like Mac OS X)", "ua" : "APP-BE Test/1.0 (iPad; Apple; CPU iPhone OS 7_0_2 like Mac OS X)",

37
test/unit/cli/cli.spec.js Normal file
View File

@@ -0,0 +1,37 @@
const assert = require('node:assert');
const { exec } = require('node:child_process');
const fs = require('node:fs');
const { UAParser } = require('../../../src/main/ua-parser');
const uap = new UAParser();
const input = [
'Opera/9.25 (Windows NT 6.0; U; ru)',
'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
];
const output = input.map(x => uap.setUA(x).getResult());
describe('npx ua-parser-js <string>', () => {
it ('print result to stdout', () => {
exec('node ./script/cli.js "TEST"', (err, stdout, stderr) => {
assert.deepEqual(JSON.parse(stdout), JSON.parse(JSON.stringify([uap.setUA("TEST").getResult()])));
});
})
});
describe('npx ua-parser-js --input-file=<filepath>', () => {
it ('load file and print result to stdout', () => {
exec('node ./script/cli.js --input-file="../test/unit/cli/input.txt"', (err, stdout, stderr) => {
assert.deepEqual(JSON.parse(stdout), JSON.parse(JSON.stringify(output)));
});
});
});
describe('npx ua-parser-js --input-file=<filepath> --output-file=<filepath>', () => {
it ('load file and save result to file', () => {
exec('node ./script/cli.js --input-file="../test/unit/cli/input.txt" --output-file="../test/unit/cli/output.json"', (err, stdout, stderr) => {
fs.readFile('test/unit/cli/output.json', (err, data) => {
assert.deepEqual(JSON.parse(data), JSON.parse(JSON.stringify(output)));
});
});
});
});

2
test/unit/cli/input.txt Normal file
View File

@@ -0,0 +1,2 @@
Opera/9.25 (Windows NT 6.0; U; ru)
Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)

32
test/unit/cli/output.json Normal file
View File

@@ -0,0 +1,32 @@
[
{
"ua": "Opera/9.25 (Windows NT 6.0; U; ru)",
"browser": {
"name": "Opera",
"version": "9.25",
"major": "9"
},
"cpu": {},
"device": {},
"engine": {},
"os": {
"name": "Windows",
"version": "Vista"
}
},
{
"ua": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)",
"browser": {
"name": "IE",
"version": "5.5",
"major": "5"
},
"cpu": {},
"device": {},
"engine": {},
"os": {
"name": "Windows",
"version": "NT"
}
}
]

View File

@@ -10,7 +10,6 @@ var cpus = require('../data/ua/cpu/cpu-all.json');
var devices = readJsonFiles('test/data/ua/device'); var devices = readJsonFiles('test/data/ua/device');
var engines = require('../data/ua/engine/engine-all.json'); var engines = require('../data/ua/engine/engine-all.json');
var os = readJsonFiles('test/data/ua/os'); var os = readJsonFiles('test/data/ua/os');
var { Headers } = require('undici');
function readJsonFiles(dir) { function readJsonFiles(dir) {
var list = []; var list = [];
@@ -163,6 +162,14 @@ describe('Extending Regex', function () {
assert.deepEqual(myParser3.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}); assert.deepEqual(myParser3.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"});
}); });
describe('User-agent with trailing space', function () {
it ('trailing space will be trimmed', function () {
const uastring = ' Opera/9.21 (Windows NT 5.1; U; ru) ';
const { ua } = UAParser(uastring);
assert.equal(ua, 'Opera/9.21 (Windows NT 5.1; U; ru) ');
});
});
describe('User-agent length', function () { describe('User-agent length', function () {
var UA_MAX_LENGTH = 500; var UA_MAX_LENGTH = 500;
@@ -375,12 +382,15 @@ describe('Read user-agent data from req.headers', function () {
assert.strictEqual(engine.name, "EdgeHTML"); assert.strictEqual(engine.name, "EdgeHTML");
}); });
// Headers supported in node 18+ - https://developer.mozilla.org/en-US/docs/Web/API/Headers
if (typeof Headers !== 'undefined') {
it('Fetch API\'s Header can be passed directly into headers', () => { it('Fetch API\'s Header can be passed directly into headers', () => {
const reqHeaders = new Headers(); const reqHeaders = new Headers();
reqHeaders.append('User-Agent', 'Midori/0.2.2 (X11; Linux i686; U; en-us) WebKit/531.2+'); reqHeaders.append('User-Agent', 'Midori/0.2.2 (X11; Linux i686; U; en-us) WebKit/531.2+');
const { browser } = UAParser(reqHeaders); const { browser } = UAParser(reqHeaders);
assert.strictEqual(browser.is('Midori'), true); assert.strictEqual(browser.is('Midori'), true);
}); });
}
it('Headers field name should be case insensitive', function () { it('Headers field name should be case insensitive', function () {
const hEaDeRs = { const hEaDeRs = {