mirror of
https://github.com/faisalman/ua-parser-js.git
synced 2026-01-16 08:27:12 +03:00
Compare commits
31 Commits
2.0.6-pro-
...
2.0.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7abb90bf3b | ||
|
|
f1eed9e0b0 | ||
|
|
66f38587b8 | ||
|
|
0273bf64e6 | ||
|
|
21186ae1c6 | ||
|
|
de2ef644ec | ||
|
|
cf4cba5d97 | ||
|
|
7fcf3f0ac4 | ||
|
|
68f8684d09 | ||
|
|
611221bcfb | ||
|
|
d54474d8e3 | ||
|
|
bab55a28df | ||
|
|
1696b87b58 | ||
|
|
7bc177de79 | ||
|
|
82d50451a2 | ||
|
|
96e3518e2e | ||
|
|
f1b9a12bda | ||
|
|
82801a36b6 | ||
|
|
6f35728377 | ||
|
|
1d8eab09f1 | ||
|
|
5a5b321347 | ||
|
|
e9f78ceb80 | ||
|
|
38301f8803 | ||
|
|
e5648826b9 | ||
|
|
a558cc1a5b | ||
|
|
cb9b50a81a | ||
|
|
042c57cc10 | ||
|
|
232fb321f1 | ||
|
|
a3a7a5e377 | ||
|
|
2d8c8fa142 | ||
|
|
d84ba1888b |
56
CHANGELOG.md
56
CHANGELOG.md
@@ -54,21 +54,67 @@
|
||||
- `browser.name`, `browser.type`, `cpu.architecture`, `device.type`, `device.vendor`, `engine.name`, `os.name`
|
||||
|
||||
- **`'ua-parser-js/extensions'`**: Predefined extensions for various use cases:
|
||||
- `Bots`, `Crawlers`, `CLIs`, `Emails`, `ExtraDevices`, `Fetchers`, `InApps`, `Libraries`, `Mediaplayers`
|
||||
- `Bots`, `Crawlers`, `CLIs`, `Emails`, `ExtraDevices`, `Fetchers`, `InApps`, `Libraries`, `Mediaplayers`, `Vehicles`
|
||||
|
||||
- **`'ua-parser-js/helpers'`**: Provides utility methods to extend detection functionality:
|
||||
- `getDeviceVendor()`: Guesses the device vendor based on its model name
|
||||
- `isAppleSilicon()`: Detects Apple Silicon device properties
|
||||
- `isAIBot()`: Checks if the user-agent is an AI bot
|
||||
- `isFrozenUA()`: Checks if the user-agent matches a frozen/reduced user-agent pattern
|
||||
|
||||
- **`'ua-parser-js/bot-detection'`**:
|
||||
- `isAIAssistant()`: Checks if the user-agent is an AI assistant
|
||||
- `isAICrawler()`: Checks if the user-agent is an AI crawler
|
||||
- `isBot()`: Checks if the user-agent is a bot
|
||||
|
||||
- **`'ua-parser-js/browser-detection'`**:
|
||||
- `isChromeFamily()`: Checks if the browser is Chrome-based (uses Blink engine) — e.g., New Opera, New Edge, Vivaldi, Brave, Arc, etc.
|
||||
- `isElectron()`: Detects if current window is running within Electron
|
||||
- `isFromEU()`: Detects if current browser's timezone is from an EU country
|
||||
- `isFrozenUA()`: Checks if the user-agent matches a frozen/reduced user-agent pattern
|
||||
- `isStandalonePWA()`: Detects if current window is a standalone PWA
|
||||
|
||||
- **`'ua-parser-js/device-detection'`**:
|
||||
- `getDeviceVendor()`: Guesses the device vendor based on its model name
|
||||
- `isAppleSilicon()`: Detects Apple Silicon device properties
|
||||
|
||||
---
|
||||
|
||||
## Version 2.0.8
|
||||
|
||||
- Resolve syntax error related to import renaming in ESM build
|
||||
- Add new browser: HiBrowser, Opera Neon
|
||||
- Add new engine: Dillo
|
||||
- Improve browser detection: Brave, TikTok
|
||||
- Improve device detection: OnePlus
|
||||
- Improve OS detection: Firefox OS
|
||||
- `extensions` submodule:
|
||||
- Add new CLI: PowerShell
|
||||
- Add new email: Alpine, Android, AquaMail, Balsa, Barca, Canary, Claws Mail, eM Client, Eudora, FairEmail, Geary, Gnus, Horde::IMP, Lotus-Notes, IncrediMail, K-9 Mail, Mailbird, MailMate, Mailspring, Mutt, Newton, Nine, NylasMail, Outlook-Express, Pegasus Mail, PocoMail, Postbox, ProtonMail Bridge, Quala, R2Mail2, Rainloop, Roundcube Webmail, SamsungEmail, Spicebird, SquirrelMail, Sylpheed, The Bat!, Trojita, Turnpike, tutanota-desktop, Wanderlust, Windows-Live-Mail
|
||||
- Add new library: http.rb, Jetty, ocaml-cohttp
|
||||
- `helpers` submodule:
|
||||
- Add new method: `getOutlookEdition()` to map Outlook versions to their marketing editions
|
||||
|
||||
## Version 2.0.7
|
||||
|
||||
- Add support for chaining `withClientHints()` & `withFeatureCheck()`
|
||||
- Add new browser: Atlas, Steam
|
||||
- Add new device vendor: Anbernic, Logitech, Valve
|
||||
- Improve device detection: Xiaomi
|
||||
- Improve OS detection: iOS
|
||||
- Split `helpers` submodule into several new submodules:
|
||||
- `bot-detection`:
|
||||
- `isAIAssistant()`
|
||||
- `isAICrawler()`
|
||||
- `isBot()`
|
||||
- `browser-detection`
|
||||
- `isChromeFamily()`
|
||||
- `isElectron()`
|
||||
- `isFromEU()`
|
||||
- `isStandalonePWA()`
|
||||
- `device-detection`
|
||||
- `getDeviceVendor()`
|
||||
- `isAppleSilicon()`
|
||||
- Update `extensions` submodule:
|
||||
- Add new fetcher: Nova Act
|
||||
- Add new library: Bun, Dart, Deno, hackney, Node.js, rest-client, undici
|
||||
|
||||
## 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
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
---
|
||||
|
||||
[](https://uaparser.dev)
|
||||
[](https://uaparser.dev)
|
||||
[](https://uaparser.dev)
|
||||
[](https://uaparser.dev)
|
||||
[](https://uaparser.dev)
|
||||
|
||||
@@ -277,4 +277,4 @@ Support the **open-source editions** of UAParser.js through one of the following
|
||||
)](https://store.faisalman.com/buy/3d71f2f3-cf4d-473c-892a-9d4497c890be)
|
||||
|
||||
<a href="https://opencollective.com/ua-parser-js"><img src="https://opencollective.com/ua-parser-js/organizations.svg?avatarHeight=64"></a>
|
||||
<a href="https://opencollective.com/ua-parser-js"><img src="https://opencollective.com/ua-parser-js/individuals.svg?avatarHeight=64"></a>
|
||||
<a href="https://opencollective.com/ua-parser-js"><img src="https://opencollective.com/ua-parser-js/individuals.svg?avatarHeight=64"></a>
|
||||
|
||||
4
dist/ua-parser.min.js
vendored
4
dist/ua-parser.min.js
vendored
File diff suppressed because one or more lines are too long
4
dist/ua-parser.min.mjs
vendored
4
dist/ua-parser.min.mjs
vendored
File diff suppressed because one or more lines are too long
4
dist/ua-parser.pack.js
vendored
4
dist/ua-parser.pack.js
vendored
File diff suppressed because one or more lines are too long
4
dist/ua-parser.pack.mjs
vendored
4
dist/ua-parser.pack.mjs
vendored
File diff suppressed because one or more lines are too long
1035
package-lock.json
generated
1035
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
27
package.json
27
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"title": "UAParser.js",
|
||||
"name": "ua-parser-js",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.8",
|
||||
"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",
|
||||
"keywords": [
|
||||
@@ -190,6 +190,21 @@
|
||||
"import": "./src/main/ua-parser.mjs",
|
||||
"types": "./src/main/ua-parser.d.ts"
|
||||
},
|
||||
"./bot-detection": {
|
||||
"require": "./src/bot-detection/bot-detection.js",
|
||||
"import": "./src/bot-detection/bot-detection.mjs",
|
||||
"types": "./src/bot-detection/bot-detection.d.ts"
|
||||
},
|
||||
"./browser-detection": {
|
||||
"require": "./src/browser-detection/browser-detection.js",
|
||||
"import": "./src/browser-detection/browser-detection.mjs",
|
||||
"types": "./src/browser-detection/browser-detection.d.ts"
|
||||
},
|
||||
"./device-detection": {
|
||||
"require": "./src/device-detection/device-detection.js",
|
||||
"import": "./src/device-detection/device-detection.mjs",
|
||||
"types": "./src/device-detection/device-detection.d.ts"
|
||||
},
|
||||
"./enums": {
|
||||
"require": "./src/enums/ua-parser-enums.js",
|
||||
"import": "./src/enums/ua-parser-enums.mjs",
|
||||
@@ -207,7 +222,7 @@
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"dist/*.?(m)js",
|
||||
"src"
|
||||
],
|
||||
"bin": "./script/cli.js",
|
||||
@@ -217,9 +232,9 @@
|
||||
"fuzz": "jazzer ./test/fuzz/redos.js --sync",
|
||||
"test": "./script/test-all.sh",
|
||||
"test:dts-lint": "tsd --typings src/main/ua-parser.d.ts --files test/static/dts-lint.ts",
|
||||
"test:eslint": "eslint src && eslint script",
|
||||
"test:eslint": "eslint --no-config-lookup src",
|
||||
"test:jshint": "jshint src/main",
|
||||
"test:lockfile-lint": "npx lockfile-lint -p package-lock.json",
|
||||
"test:lockfile-lint": "lockfile-lint -p package-lock.json",
|
||||
"test:mocha": "mocha --recursive test/unit",
|
||||
"test:playwright": "npx playwright install && playwright test test/e2e --browser all"
|
||||
},
|
||||
@@ -231,8 +246,10 @@
|
||||
"devDependencies": {
|
||||
"@babel/parser": "7.15.8",
|
||||
"@babel/traverse": "7.23.2",
|
||||
"@playwright/test": "^1.49.0",
|
||||
"@playwright/test": "^1.57.0",
|
||||
"eslint": "^9.39.1",
|
||||
"jshint": "~2.13.6",
|
||||
"lockfile-lint": "^4.14.1",
|
||||
"mocha": "~8.2.0",
|
||||
"requirejs": "2.3.2",
|
||||
"safe-regex": "^2.1.1",
|
||||
|
||||
@@ -4,6 +4,7 @@ const fs = require('fs');
|
||||
|
||||
const defaultReplacements = {
|
||||
mjs: [
|
||||
[/(?<=const.+)(:)(?=.+require)/ig, ' as'],
|
||||
[/const (.+?)\s*=\s*require\(\'\.(.+)\'\)/ig, 'import $1 from \'\.$2.mjs\''],
|
||||
[/const (.+?)\s*=\s*require\(\'(.+)\'\)/ig, 'import $1 from \'$2\''],
|
||||
[/module\.exports =/ig, 'export']
|
||||
@@ -40,6 +41,24 @@ const files = [
|
||||
...defaultReplacements.mjs
|
||||
]
|
||||
},
|
||||
{
|
||||
src : 'src/bot-detection/bot-detection.js',
|
||||
dest :'src/bot-detection/bot-detection.mjs',
|
||||
title : 'Generated ESM version of ua-parser-js/bot-detection',
|
||||
replacements : [...defaultReplacements.mjs]
|
||||
},
|
||||
{
|
||||
src : 'src/browser-detection/browser-detection.js',
|
||||
dest :'src/browser-detection/browser-detection.mjs',
|
||||
title : 'Generated ESM version of ua-parser-js/browser-detection',
|
||||
replacements : [...defaultReplacements.mjs]
|
||||
},
|
||||
{
|
||||
src : 'src/device-detection/device-detection.js',
|
||||
dest :'src/device-detection/device-detection.mjs',
|
||||
title : 'Generated ESM version of ua-parser-js/device-detection',
|
||||
replacements : [...defaultReplacements.mjs]
|
||||
},
|
||||
{
|
||||
src : 'src/enums/ua-parser-enums.js',
|
||||
dest :'src/enums/ua-parser-enums.mjs',
|
||||
|
||||
@@ -11,7 +11,7 @@ try {
|
||||
|
||||
if (!process.argv[2].startsWith('-')) {
|
||||
|
||||
const results = process.argv.slice(2).map(ua => UAParser(ua));
|
||||
const results = process.argv.slice(2).map(ua => UAParser(ua, [Bots, Emails, ExtraDevices, InApps, Vehicles]));
|
||||
console.log(JSON.stringify(results, null, 4));
|
||||
process.exit(0);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ echo '
|
||||
- lint js code
|
||||
'
|
||||
npm run test:jshint || exit 1
|
||||
#npm run test:eslint || exit 1
|
||||
npm run test:eslint || exit 1
|
||||
|
||||
echo '
|
||||
- test using mocha
|
||||
|
||||
7
src/bot-detection/bot-detection.d.ts
vendored
Normal file
7
src/bot-detection/bot-detection.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// Type definitions for bot-detection submodule of UAParser.js v2.0.8
|
||||
// Project: https://github.com/faisalman/ua-parser-js
|
||||
// Definitions by: Faisal Salman <https://github.com/faisalman>
|
||||
|
||||
export function isAIAssistant(ua: string): boolean;
|
||||
export function isAICrawler(ua: string): boolean;
|
||||
export function isBot(ua: string): boolean;
|
||||
188
src/bot-detection/bot-detection.js
Normal file
188
src/bot-detection/bot-detection.js
Normal file
@@ -0,0 +1,188 @@
|
||||
//////////////////////////////////////////////////
|
||||
/* bot-detection submodule of UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
const { UAParser } = require('../main/ua-parser');
|
||||
const { Bots, Crawlers, Fetchers } = require('../extensions/ua-parser-extensions');
|
||||
const { BrowserType, Extension } = require('../enums/ua-parser-enums');
|
||||
const { Crawler, Fetcher } = Extension.BrowserName;
|
||||
|
||||
class BotList {
|
||||
constructor(ext, prop, list) {
|
||||
this.ext = ext;
|
||||
this.prop = prop;
|
||||
this.list = list.map(x => x.toLowerCase());
|
||||
}
|
||||
includes(ua) {
|
||||
return this.list.includes(
|
||||
(typeof ua === 'string' ?
|
||||
new UAParser(ua, this.ext).getBrowser() :
|
||||
ua.browser
|
||||
)[this.prop]?.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
const BotTypes = new BotList(Bots, 'type', [
|
||||
BrowserType.CLI,
|
||||
BrowserType.CRAWLER,
|
||||
BrowserType.FETCHER,
|
||||
BrowserType.LIBRARY
|
||||
]);
|
||||
|
||||
const AIAssistants = new BotList(Fetchers, 'name', [
|
||||
|
||||
// Amazon
|
||||
Fetcher.AMAZON_NOVA_ACT,
|
||||
|
||||
// Anthropic
|
||||
Fetcher.ANTHROPIC_CLAUDE_USER,
|
||||
|
||||
// Cohere
|
||||
Fetcher.COHERE_AI,
|
||||
|
||||
// DuckDuckGo
|
||||
Fetcher.DUCKDUCKGO_ASSISTBOT,
|
||||
|
||||
// Google
|
||||
Fetcher.GOOGLE_GEMINI_DEEP_RESEARCH,
|
||||
|
||||
// Mistral AI
|
||||
Fetcher.MISTRALAI_USER,
|
||||
|
||||
// OpenAI
|
||||
Fetcher.OPENAI_CHATGPT_USER,
|
||||
|
||||
// Perplexity
|
||||
Fetcher.PERPLEXITY_USER
|
||||
]);
|
||||
|
||||
const AICrawlers = new BotList(Crawlers, 'name', [
|
||||
|
||||
// AI2
|
||||
Crawler.AI2_BOT,
|
||||
|
||||
// Amazon
|
||||
Crawler.AMAZON_BOT,
|
||||
|
||||
// Anthropic
|
||||
Crawler.ANTHROPIC_AI,
|
||||
Crawler.ANTHROPIC_CLAUDE_BOT,
|
||||
Crawler.ANTHROPIC_CLAUDE_SEARCHBOT,
|
||||
Crawler.ANTHROPIC_CLAUDE_WEB,
|
||||
|
||||
// Apple
|
||||
Crawler.APPLE_BOT,
|
||||
Crawler.APPLE_BOT_EXTENDED,
|
||||
|
||||
// Brave
|
||||
Crawler.BRAVE_BOT,
|
||||
|
||||
// ByteDance
|
||||
Crawler.BYTEDANCE_BYTESPIDER,
|
||||
Crawler.BYTEDANCE_TIKTOKSPIDER,
|
||||
|
||||
// Cohere
|
||||
Crawler.COHERE_TRAINING_DATA_CRAWLER,
|
||||
|
||||
// Common Crawl
|
||||
Crawler.COMMON_CRAWL_CCBOT,
|
||||
|
||||
// Coveo
|
||||
Crawler.COVEO_BOT,
|
||||
|
||||
// DataForSeo
|
||||
Crawler.DATAFORSEO_BOT,
|
||||
|
||||
// DeepSeek
|
||||
Crawler.DEEPSEEK_BOT,
|
||||
|
||||
// Diffbot
|
||||
Crawler.DIFFBOT,
|
||||
|
||||
// Google
|
||||
Crawler.GOOGLE_EXTENDED,
|
||||
Crawler.GOOGLE_OTHER,
|
||||
Crawler.GOOGLE_OTHER_IMAGE,
|
||||
Crawler.GOOGLE_OTHER_VIDEO,
|
||||
Crawler.GOOGLE_CLOUDVERTEXBOT,
|
||||
|
||||
// Hive AI
|
||||
Crawler.HIVE_IMAGESIFTBOT,
|
||||
|
||||
// Huawei
|
||||
Crawler.HUAWEI_PETALBOT,
|
||||
Crawler.HUAWEI_PANGUBOT,
|
||||
|
||||
// Hugging Face
|
||||
Crawler.HUGGINGFACE_BOT,
|
||||
|
||||
// Kangaroo
|
||||
Crawler.KANGAROO_BOT,
|
||||
|
||||
// Mendable.ai
|
||||
Crawler.FIRECRAWL_AGENT,
|
||||
|
||||
// Meta
|
||||
Crawler.META_FACEBOOKBOT,
|
||||
Crawler.META_EXTERNALAGENT,
|
||||
|
||||
// OpenAI
|
||||
Crawler.OPENAI_GPTBOT,
|
||||
Crawler.OPENAI_SEARCH_BOT,
|
||||
|
||||
// Perplexity
|
||||
Crawler.PERPLEXITY_BOT,
|
||||
|
||||
// Replicate
|
||||
Crawler.REPLICATE_BOT,
|
||||
|
||||
// Runpod
|
||||
Crawler.RUNPOD_BOT,
|
||||
|
||||
// SB Intuitions
|
||||
Crawler.SB_INTUITIONS_BOT,
|
||||
|
||||
// Semrush
|
||||
Crawler.SEMRUSH_BOT_CONTENTSHAKE,
|
||||
|
||||
// Timpi
|
||||
Crawler.TIMPI_BOT,
|
||||
|
||||
// Together AI
|
||||
Crawler.TOGETHER_BOT,
|
||||
|
||||
// Velen.io
|
||||
Crawler.HUNTER_VELENPUBLICWEBCRAWLER,
|
||||
|
||||
// Vercel
|
||||
Crawler.VERCEL_V0BOT,
|
||||
|
||||
// Webz.io
|
||||
Crawler.WEBZIO_OMGILI,
|
||||
Crawler.WEBZIO_OMGILI_BOT,
|
||||
Crawler.WEBZIO_EXTENDED,
|
||||
|
||||
// X
|
||||
Crawler.XAI_BOT,
|
||||
|
||||
// You.com
|
||||
Crawler.YOU_BOT,
|
||||
|
||||
// Zhipu AI
|
||||
Crawler.ZHIPU_CHATGLM_SPIDER
|
||||
]);
|
||||
|
||||
const isBot = ua => BotTypes.includes(ua);
|
||||
const isAIAssistant = ua => AIAssistants.includes(ua);
|
||||
const isAICrawler = ua => AICrawlers.includes(ua);
|
||||
|
||||
module.exports = {
|
||||
isAIAssistant,
|
||||
isAICrawler,
|
||||
isBot
|
||||
}
|
||||
192
src/bot-detection/bot-detection.mjs
Normal file
192
src/bot-detection/bot-detection.mjs
Normal file
@@ -0,0 +1,192 @@
|
||||
// Generated ESM version of ua-parser-js/bot-detection
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /src/bot-detection/bot-detection.js
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* bot-detection submodule of UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
import { UAParser } from '../main/ua-parser.mjs';
|
||||
import { Bots, Crawlers, Fetchers } from '../extensions/ua-parser-extensions.mjs';
|
||||
import { BrowserType, Extension } from '../enums/ua-parser-enums.mjs';
|
||||
const { Crawler, Fetcher } = Extension.BrowserName;
|
||||
|
||||
class BotList {
|
||||
constructor(ext, prop, list) {
|
||||
this.ext = ext;
|
||||
this.prop = prop;
|
||||
this.list = list.map(x => x.toLowerCase());
|
||||
}
|
||||
includes(ua) {
|
||||
return this.list.includes(
|
||||
(typeof ua === 'string' ?
|
||||
new UAParser(ua, this.ext).getBrowser() :
|
||||
ua.browser
|
||||
)[this.prop]?.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
const BotTypes = new BotList(Bots, 'type', [
|
||||
BrowserType.CLI,
|
||||
BrowserType.CRAWLER,
|
||||
BrowserType.FETCHER,
|
||||
BrowserType.LIBRARY
|
||||
]);
|
||||
|
||||
const AIAssistants = new BotList(Fetchers, 'name', [
|
||||
|
||||
// Amazon
|
||||
Fetcher.AMAZON_NOVA_ACT,
|
||||
|
||||
// Anthropic
|
||||
Fetcher.ANTHROPIC_CLAUDE_USER,
|
||||
|
||||
// Cohere
|
||||
Fetcher.COHERE_AI,
|
||||
|
||||
// DuckDuckGo
|
||||
Fetcher.DUCKDUCKGO_ASSISTBOT,
|
||||
|
||||
// Google
|
||||
Fetcher.GOOGLE_GEMINI_DEEP_RESEARCH,
|
||||
|
||||
// Mistral AI
|
||||
Fetcher.MISTRALAI_USER,
|
||||
|
||||
// OpenAI
|
||||
Fetcher.OPENAI_CHATGPT_USER,
|
||||
|
||||
// Perplexity
|
||||
Fetcher.PERPLEXITY_USER
|
||||
]);
|
||||
|
||||
const AICrawlers = new BotList(Crawlers, 'name', [
|
||||
|
||||
// AI2
|
||||
Crawler.AI2_BOT,
|
||||
|
||||
// Amazon
|
||||
Crawler.AMAZON_BOT,
|
||||
|
||||
// Anthropic
|
||||
Crawler.ANTHROPIC_AI,
|
||||
Crawler.ANTHROPIC_CLAUDE_BOT,
|
||||
Crawler.ANTHROPIC_CLAUDE_SEARCHBOT,
|
||||
Crawler.ANTHROPIC_CLAUDE_WEB,
|
||||
|
||||
// Apple
|
||||
Crawler.APPLE_BOT,
|
||||
Crawler.APPLE_BOT_EXTENDED,
|
||||
|
||||
// Brave
|
||||
Crawler.BRAVE_BOT,
|
||||
|
||||
// ByteDance
|
||||
Crawler.BYTEDANCE_BYTESPIDER,
|
||||
Crawler.BYTEDANCE_TIKTOKSPIDER,
|
||||
|
||||
// Cohere
|
||||
Crawler.COHERE_TRAINING_DATA_CRAWLER,
|
||||
|
||||
// Common Crawl
|
||||
Crawler.COMMON_CRAWL_CCBOT,
|
||||
|
||||
// Coveo
|
||||
Crawler.COVEO_BOT,
|
||||
|
||||
// DataForSeo
|
||||
Crawler.DATAFORSEO_BOT,
|
||||
|
||||
// DeepSeek
|
||||
Crawler.DEEPSEEK_BOT,
|
||||
|
||||
// Diffbot
|
||||
Crawler.DIFFBOT,
|
||||
|
||||
// Google
|
||||
Crawler.GOOGLE_EXTENDED,
|
||||
Crawler.GOOGLE_OTHER,
|
||||
Crawler.GOOGLE_OTHER_IMAGE,
|
||||
Crawler.GOOGLE_OTHER_VIDEO,
|
||||
Crawler.GOOGLE_CLOUDVERTEXBOT,
|
||||
|
||||
// Hive AI
|
||||
Crawler.HIVE_IMAGESIFTBOT,
|
||||
|
||||
// Huawei
|
||||
Crawler.HUAWEI_PETALBOT,
|
||||
Crawler.HUAWEI_PANGUBOT,
|
||||
|
||||
// Hugging Face
|
||||
Crawler.HUGGINGFACE_BOT,
|
||||
|
||||
// Kangaroo
|
||||
Crawler.KANGAROO_BOT,
|
||||
|
||||
// Mendable.ai
|
||||
Crawler.FIRECRAWL_AGENT,
|
||||
|
||||
// Meta
|
||||
Crawler.META_FACEBOOKBOT,
|
||||
Crawler.META_EXTERNALAGENT,
|
||||
|
||||
// OpenAI
|
||||
Crawler.OPENAI_GPTBOT,
|
||||
Crawler.OPENAI_SEARCH_BOT,
|
||||
|
||||
// Perplexity
|
||||
Crawler.PERPLEXITY_BOT,
|
||||
|
||||
// Replicate
|
||||
Crawler.REPLICATE_BOT,
|
||||
|
||||
// Runpod
|
||||
Crawler.RUNPOD_BOT,
|
||||
|
||||
// SB Intuitions
|
||||
Crawler.SB_INTUITIONS_BOT,
|
||||
|
||||
// Semrush
|
||||
Crawler.SEMRUSH_BOT_CONTENTSHAKE,
|
||||
|
||||
// Timpi
|
||||
Crawler.TIMPI_BOT,
|
||||
|
||||
// Together AI
|
||||
Crawler.TOGETHER_BOT,
|
||||
|
||||
// Velen.io
|
||||
Crawler.HUNTER_VELENPUBLICWEBCRAWLER,
|
||||
|
||||
// Vercel
|
||||
Crawler.VERCEL_V0BOT,
|
||||
|
||||
// Webz.io
|
||||
Crawler.WEBZIO_OMGILI,
|
||||
Crawler.WEBZIO_OMGILI_BOT,
|
||||
Crawler.WEBZIO_EXTENDED,
|
||||
|
||||
// X
|
||||
Crawler.XAI_BOT,
|
||||
|
||||
// You.com
|
||||
Crawler.YOU_BOT,
|
||||
|
||||
// Zhipu AI
|
||||
Crawler.ZHIPU_CHATGLM_SPIDER
|
||||
]);
|
||||
|
||||
const isBot = ua => BotTypes.includes(ua);
|
||||
const isAIAssistant = ua => AIAssistants.includes(ua);
|
||||
const isAICrawler = ua => AICrawlers.includes(ua);
|
||||
|
||||
export {
|
||||
isAIAssistant,
|
||||
isAICrawler,
|
||||
isBot
|
||||
}
|
||||
10
src/browser-detection/browser-detection.d.ts
vendored
Normal file
10
src/browser-detection/browser-detection.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Type definitions for browser-detection submodule of UAParser.js v2.0.8
|
||||
// Project: https://github.com/faisalman/ua-parser-js
|
||||
// Definitions by: Faisal Salman <https://github.com/faisalman>
|
||||
|
||||
import type { IResult } from "../main/ua-parser";
|
||||
|
||||
export function isChromeFamily(resultOrUA: IResult | string): boolean;
|
||||
export function isElectron(): boolean;
|
||||
export function isFromEU(): boolean;
|
||||
export function isStandalonePWA(): boolean;
|
||||
30
src/browser-detection/browser-detection.js
Normal file
30
src/browser-detection/browser-detection.js
Normal file
@@ -0,0 +1,30 @@
|
||||
//////////////////////////////////////////////////////
|
||||
/* browser-detection submodule of UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
const { UAParser } = require('../main/ua-parser');
|
||||
const { EngineName } = require('../enums/ua-parser-enums');
|
||||
const { isStandalonePWA } = require('is-standalone-pwa');
|
||||
const { isFromEU } = require('detect-europe-js');
|
||||
|
||||
const isChromeFamily = val => !!(
|
||||
(typeof val === 'string' ?
|
||||
new UAParser(val).getEngine() :
|
||||
val.engine
|
||||
)?.is(EngineName.BLINK));
|
||||
|
||||
const isElectron = () => !!(
|
||||
process?.versions?.hasOwnProperty('electron') || // node.js
|
||||
/ electron\//i.test(navigator?.userAgent)); // browser
|
||||
|
||||
module.exports = {
|
||||
isChromeFamily,
|
||||
isElectron,
|
||||
isFromEU,
|
||||
isStandalonePWA
|
||||
}
|
||||
34
src/browser-detection/browser-detection.mjs
Normal file
34
src/browser-detection/browser-detection.mjs
Normal file
@@ -0,0 +1,34 @@
|
||||
// Generated ESM version of ua-parser-js/browser-detection
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /src/browser-detection/browser-detection.js
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
/* browser-detection submodule of UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
import { UAParser } from '../main/ua-parser.mjs';
|
||||
import { EngineName } from '../enums/ua-parser-enums.mjs';
|
||||
import { isStandalonePWA } from 'is-standalone-pwa';
|
||||
import { isFromEU } from 'detect-europe-js';
|
||||
|
||||
const isChromeFamily = val => !!(
|
||||
(typeof val === 'string' ?
|
||||
new UAParser(val).getEngine() :
|
||||
val.engine
|
||||
)?.is(EngineName.BLINK));
|
||||
|
||||
const isElectron = () => !!(
|
||||
process?.versions?.hasOwnProperty('electron') || // node.js
|
||||
/ electron\//i.test(navigator?.userAgent)); // browser
|
||||
|
||||
export {
|
||||
isChromeFamily,
|
||||
isElectron,
|
||||
isFromEU,
|
||||
isStandalonePWA
|
||||
}
|
||||
8
src/device-detection/device-detection.d.ts
vendored
Normal file
8
src/device-detection/device-detection.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Type definitions for device-detection submodule of UAParser.js v2.0.8
|
||||
// Project: https://github.com/faisalman/ua-parser-js
|
||||
// Definitions by: Faisal Salman <https://github.com/faisalman>
|
||||
|
||||
import type { IResult } from "../main/ua-parser";
|
||||
|
||||
export function getDeviceVendor(model: string): string | undefined;
|
||||
export function isAppleSilicon(resultOrUA: IResult | string): boolean;
|
||||
43
src/device-detection/device-detection.js
Normal file
43
src/device-detection/device-detection.js
Normal file
@@ -0,0 +1,43 @@
|
||||
/////////////////////////////////////////////////////
|
||||
/* device-detection submodule of UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
const { UAParser } = require('../main/ua-parser');
|
||||
const { CPUArch, OSName } = require('../enums/ua-parser-enums');
|
||||
|
||||
const getDeviceVendor = (model) => new UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).getDevice().vendor;
|
||||
|
||||
const isAppleSilicon = (val) => {
|
||||
const { os, cpu } = typeof val !== 'string' ? val : {
|
||||
os: new UAParser(val).getOS(),
|
||||
cpu: new UAParser(val).getCPU()
|
||||
};
|
||||
if (os.is(OSName.MACOS)) {
|
||||
if (cpu.is(CPUArch.ARM)) {
|
||||
return true;
|
||||
} else if (typeof window !== 'undefined') {
|
||||
try {
|
||||
const canvas = document.createElement('canvas');
|
||||
const webgl = canvas.getContext('webgl2') ||
|
||||
canvas.getContext('webgl') ||
|
||||
canvas.getContext('experimental-webgl');
|
||||
return webgl
|
||||
.getParameter(webgl.getExtension('WEBGL_debug_renderer_info').UNMASKED_RENDERER_WEBGL)
|
||||
.match(/apple m\d/i);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getDeviceVendor,
|
||||
isAppleSilicon
|
||||
}
|
||||
47
src/device-detection/device-detection.mjs
Normal file
47
src/device-detection/device-detection.mjs
Normal file
@@ -0,0 +1,47 @@
|
||||
// Generated ESM version of ua-parser-js/device-detection
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /src/device-detection/device-detection.js
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/* device-detection submodule of UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
import { UAParser } from '../main/ua-parser.mjs';
|
||||
import { CPUArch, OSName } from '../enums/ua-parser-enums.mjs';
|
||||
|
||||
const getDeviceVendor = (model) => new UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).getDevice().vendor;
|
||||
|
||||
const isAppleSilicon = (val) => {
|
||||
const { os, cpu } = typeof val !== 'string' ? val : {
|
||||
os: new UAParser(val).getOS(),
|
||||
cpu: new UAParser(val).getCPU()
|
||||
};
|
||||
if (os.is(OSName.MACOS)) {
|
||||
if (cpu.is(CPUArch.ARM)) {
|
||||
return true;
|
||||
} else if (typeof window !== 'undefined') {
|
||||
try {
|
||||
const canvas = document.createElement('canvas');
|
||||
const webgl = canvas.getContext('webgl2') ||
|
||||
canvas.getContext('webgl') ||
|
||||
canvas.getContext('experimental-webgl');
|
||||
return webgl
|
||||
.getParameter(webgl.getExtension('WEBGL_debug_renderer_info').UNMASKED_RENDERER_WEBGL)
|
||||
.match(/apple m\d/i);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export {
|
||||
getDeviceVendor,
|
||||
isAppleSilicon
|
||||
}
|
||||
74
src/enums/ua-parser-enums.d.ts
vendored
74
src/enums/ua-parser-enums.d.ts
vendored
@@ -3,7 +3,7 @@
|
||||
// Source: /src/enums/ua-parser-enums.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Enums for UAParser.js v2.0.6
|
||||
/* Enums for UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
@@ -19,6 +19,7 @@ export const BrowserName: Readonly<{
|
||||
AMAYA: 'Amaya',
|
||||
ANDROID: 'Android Browser',
|
||||
ARORA: 'Arora',
|
||||
ATLAS: 'Atlas',
|
||||
AVANT: 'Avant',
|
||||
AVAST: 'Avast Secure Browser',
|
||||
AVG: 'AVG Secure Browser',
|
||||
@@ -65,6 +66,7 @@ export const BrowserName: Readonly<{
|
||||
GOOGLE_SEARCH: 'GSA',
|
||||
HELIO: 'Helio',
|
||||
HEYTAP: 'HeyTap',
|
||||
HIBROWSER: 'HiBrowser',
|
||||
HONOR: 'Honor',
|
||||
HUAWEI: 'Huawei Browser',
|
||||
ICAB: 'iCab',
|
||||
@@ -115,6 +117,7 @@ export const BrowserName: Readonly<{
|
||||
OPERA_GX: 'Opera GX',
|
||||
OPERA_MINI: 'Opera Mini',
|
||||
OPERA_MOBI: 'Opera Mobi',
|
||||
OPERA_NEON: 'Opera Neon',
|
||||
OPERA_TABLET: 'Opera Tablet',
|
||||
OPERA_TOUCH: 'Opera Touch',
|
||||
OTTER: 'Otter',
|
||||
@@ -147,6 +150,7 @@ export const BrowserName: Readonly<{
|
||||
SNAPCHAT: 'Snapchat',
|
||||
SOGOU_EXPLORER: 'Sogou Explorer',
|
||||
SOGOU_MOBILE: 'Sogou Mobile',
|
||||
STEAM: 'Steam',
|
||||
SURF: 'Surf',
|
||||
SWIFTFOX: 'Swiftfox',
|
||||
TESLA: 'Tesla',
|
||||
@@ -228,8 +232,9 @@ export const DeviceVendor: Readonly<{
|
||||
ACER: 'Acer',
|
||||
ADVAN: 'Advan',
|
||||
ALCATEL: 'Alcatel',
|
||||
APPLE: 'Apple',
|
||||
AMAZON: 'Amazon',
|
||||
ANBERNIC: 'Anbernic',
|
||||
APPLE: 'Apple',
|
||||
ARCHOS: 'Archos',
|
||||
ASUS: 'ASUS',
|
||||
ATT: 'AT&T',
|
||||
@@ -258,6 +263,7 @@ export const DeviceVendor: Readonly<{
|
||||
LAVA: 'Lava',
|
||||
LENOVO: 'Lenovo',
|
||||
LG: 'LG',
|
||||
LOGITECH: 'Logitech',
|
||||
MEIZU: 'Meizu',
|
||||
MICROMAX: 'Micromax',
|
||||
MICROSOFT: 'Microsoft',
|
||||
@@ -291,6 +297,7 @@ export const DeviceVendor: Readonly<{
|
||||
TECNO: 'TECNO',
|
||||
TESLA: 'Tesla',
|
||||
ULEFONE: 'Ulefone',
|
||||
VALVE: 'Valve',
|
||||
VIVO: 'Vivo',
|
||||
VIZIO: 'Vizio',
|
||||
VODAFONE: 'Vodafone',
|
||||
@@ -311,6 +318,7 @@ export const EngineName: Readonly<{
|
||||
AMAYA: 'Amaya',
|
||||
ARKWEB: 'ArkWeb',
|
||||
BLINK: 'Blink',
|
||||
DILLO: 'Dillo',
|
||||
EDGEHTML: 'EdgeHTML',
|
||||
FLOW: 'Flow',
|
||||
GECKO: 'Gecko',
|
||||
@@ -446,6 +454,7 @@ export const Extension: Readonly<{
|
||||
ELINKS: 'ELinks',
|
||||
HTTPIE: 'HTTPie',
|
||||
LYNX: 'Lynx',
|
||||
POWERSHELL: 'PowerShell',
|
||||
WGET: 'Wget'
|
||||
},
|
||||
Crawler: {
|
||||
@@ -608,29 +617,72 @@ export const Extension: Readonly<{
|
||||
},
|
||||
Email: {
|
||||
AIRMAIL: 'Airmail',
|
||||
ALPINE: 'Alpine',
|
||||
ANDROID_MAIL: 'Android',
|
||||
APPLE_MAIL: 'Mail',
|
||||
AQUA_MAIL: 'AquaMail',
|
||||
BALSA: 'Balsa',
|
||||
BARCA: 'Barca',
|
||||
BLUEMAIL: 'BlueMail',
|
||||
CANARY: 'Canary',
|
||||
CLAWS_MAIL: 'Claws Mail',
|
||||
DAUM_MAIL: 'DaumMail',
|
||||
EVOLUTION: 'Evolution',
|
||||
EM_CLIENT: 'eM Client',
|
||||
EUDORA: 'Eudora',
|
||||
EVOLUTION: 'Evolution',
|
||||
FAIR_EMAIL: 'FairEmail',
|
||||
FOXMAIL: 'Foxmail',
|
||||
GEARY: 'Geary',
|
||||
GNUS: 'Gnus',
|
||||
HORDE_IMP: 'Horde::IMP',
|
||||
IBM_NOTES: 'Lotus-Notes',
|
||||
INCREDIMAIL: 'IncrediMail',
|
||||
K9_MAIL: 'K-9 Mail',
|
||||
KMAIL: 'KMail',
|
||||
KMAIL2: 'kmail2',
|
||||
KONTACT: 'Kontact',
|
||||
MAILBIRD: 'Mailbird',
|
||||
MAILMATE: 'MailMate',
|
||||
MAILSPRING: 'Mailspring',
|
||||
MICROSOFT_OUTLOOK: 'Microsoft Outlook',
|
||||
MICROSOFT_OUTLOOK_MAC: 'MacOutlook',
|
||||
MUTT: 'Mutt',
|
||||
NAVER_MAILAPP: 'NaverMailApp',
|
||||
NEWTON: 'Newton',
|
||||
NINE: 'Nine',
|
||||
NYLAS_MAIL: 'NylasMail',
|
||||
OUTLOOK_EXPRESS: 'Outlook-Express',
|
||||
PEGASUS_MAIL: 'Pegasus Mail',
|
||||
POCOMAIL: 'PocoMail',
|
||||
POLYMAIL: 'Polymail',
|
||||
POSTBOX: 'Postbox',
|
||||
PROTON_MAIL: 'ProtonMail',
|
||||
PROTON_MAIL_BRIDGE: 'ProtonMail Bridge',
|
||||
QUALA_MAIL: 'Quala',
|
||||
R2MAIL2: 'R2Mail2',
|
||||
RAINLOOP: 'RainLoop',
|
||||
ROUNDCUBE: 'Roundcube Webmail',
|
||||
SAMSUNG_EMAIL: 'SamsungEmail',
|
||||
SPARK_MAIL: 'SparkDesktop',
|
||||
SPARROW: 'Sparrow',
|
||||
SPICEBIRD: 'Spicebird',
|
||||
SQUIRRELMAIL: 'SquirrelMail',
|
||||
SYLPHEED: 'Sylpheed',
|
||||
THE_BAT: 'The Bat!',
|
||||
THUNDERBIRD: 'Thunderbird',
|
||||
YAHOO_MAIL: 'Yahoo',
|
||||
TROJITA: 'Trojita',
|
||||
TURNPIKE: 'Turnpike',
|
||||
TUTANOTA: 'tutanota-desktop',
|
||||
WANDERLUST: 'Wanderlust',
|
||||
WINDOWS_LIVE_MAIL: 'Windows-Live-Mail',
|
||||
YAHOO_MAIL: 'Yahoo Mail',
|
||||
YAHOO_MAIL_IOS: 'Yahoo Mail',
|
||||
ZIMBRA: 'Zimbra',
|
||||
ZOHO_MAIL: 'ZohoMail-Desktop'
|
||||
},
|
||||
Fetcher: {
|
||||
AHREFS_SITEAUDIT: 'AhrefsSiteAudit',
|
||||
AMAZON_NOVA_ACT: 'NovaAct',
|
||||
ANTHROPIC_CLAUDE_USER: 'Claude-User',
|
||||
ASANA: 'Asana',
|
||||
BETTER_UPTIME_BOT: 'Better Uptime Bot',
|
||||
@@ -708,26 +760,36 @@ export const Extension: Readonly<{
|
||||
AIOHTTP: 'aiohttp',
|
||||
APACHE_HTTPCLIENT: 'Apache-HttpClient',
|
||||
AXIOS: 'axios',
|
||||
BUN: 'Bun',
|
||||
DART: 'Dart',
|
||||
DENO: 'Deno',
|
||||
GO_HTTP_CLIENT: 'go-http-client',
|
||||
GOT: 'got',
|
||||
GUZZLEHTTP: 'GuzzleHttp',
|
||||
HACKNEY: 'hackney',
|
||||
HTTP_RB: 'http.rb',
|
||||
JAVA: 'Java',
|
||||
JAVA_HTTPCLIENT: 'Java-http-client',
|
||||
JETTY: 'Jetty',
|
||||
JSDOM: 'jsdom',
|
||||
LIBWWW_PERL: 'libwww-perl',
|
||||
LUA_RESTY_HTTP: 'lua-resty-http',
|
||||
NEEDLE: 'Needle',
|
||||
NUTCH: 'Nutch',
|
||||
OKHTTP: 'OkHttp',
|
||||
NODE_FETCH: 'node-fetch',
|
||||
NODE_JS: 'Node.js',
|
||||
NODE_SUPERAGENT: 'node-superagent',
|
||||
OKHTTP: 'OkHttp',
|
||||
OCAML_COHTTP: 'ocaml-cohttp',
|
||||
PHP_SOAP: 'PHP-SOAP',
|
||||
POSTMAN_RUNTIME: 'PostmanRuntime',
|
||||
PYTHON_HTTPX: 'python-httpx',
|
||||
PYTHON_URLLIB: 'python-urllib',
|
||||
PYTHON_URLLIB3: 'python-urllib3',
|
||||
PYTHON_REQUESTS: 'python-requests',
|
||||
SCRAPY: 'Scrapy'
|
||||
REST_CLIENT: 'rest-client',
|
||||
SCRAPY: 'Scrapy',
|
||||
UNDICI: 'undici'
|
||||
}
|
||||
},
|
||||
DeviceVendor: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////
|
||||
/* Enums for UAParser.js v2.0.6
|
||||
/* Enums for UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
@@ -15,6 +15,7 @@ const BrowserName = Object.freeze({
|
||||
AMAYA: 'Amaya',
|
||||
ANDROID: 'Android Browser',
|
||||
ARORA: 'Arora',
|
||||
ATLAS: 'Atlas',
|
||||
AVANT: 'Avant',
|
||||
AVAST: 'Avast Secure Browser',
|
||||
AVG: 'AVG Secure Browser',
|
||||
@@ -61,6 +62,7 @@ const BrowserName = Object.freeze({
|
||||
GOOGLE_SEARCH: 'GSA',
|
||||
HELIO: 'Helio',
|
||||
HEYTAP: 'HeyTap',
|
||||
HIBROWSER: 'HiBrowser',
|
||||
HONOR: 'Honor',
|
||||
HUAWEI: 'Huawei Browser',
|
||||
ICAB: 'iCab',
|
||||
@@ -111,6 +113,7 @@ const BrowserName = Object.freeze({
|
||||
OPERA_GX: 'Opera GX',
|
||||
OPERA_MINI: 'Opera Mini',
|
||||
OPERA_MOBI: 'Opera Mobi',
|
||||
OPERA_NEON: 'Opera Neon',
|
||||
OPERA_TABLET: 'Opera Tablet',
|
||||
OPERA_TOUCH: 'Opera Touch',
|
||||
OTTER: 'Otter',
|
||||
@@ -143,6 +146,7 @@ const BrowserName = Object.freeze({
|
||||
SNAPCHAT: 'Snapchat',
|
||||
SOGOU_EXPLORER: 'Sogou Explorer',
|
||||
SOGOU_MOBILE: 'Sogou Mobile',
|
||||
STEAM: 'Steam',
|
||||
SURF: 'Surf',
|
||||
SWIFTFOX: 'Swiftfox',
|
||||
TESLA: 'Tesla',
|
||||
@@ -224,8 +228,9 @@ const DeviceVendor = Object.freeze({
|
||||
ACER: 'Acer',
|
||||
ADVAN: 'Advan',
|
||||
ALCATEL: 'Alcatel',
|
||||
APPLE: 'Apple',
|
||||
AMAZON: 'Amazon',
|
||||
ANBERNIC: 'Anbernic',
|
||||
APPLE: 'Apple',
|
||||
ARCHOS: 'Archos',
|
||||
ASUS: 'ASUS',
|
||||
ATT: 'AT&T',
|
||||
@@ -254,6 +259,7 @@ const DeviceVendor = Object.freeze({
|
||||
LAVA: 'Lava',
|
||||
LENOVO: 'Lenovo',
|
||||
LG: 'LG',
|
||||
LOGITECH: 'Logitech',
|
||||
MEIZU: 'Meizu',
|
||||
MICROMAX: 'Micromax',
|
||||
MICROSOFT: 'Microsoft',
|
||||
@@ -287,6 +293,7 @@ const DeviceVendor = Object.freeze({
|
||||
TECNO: 'TECNO',
|
||||
TESLA: 'Tesla',
|
||||
ULEFONE: 'Ulefone',
|
||||
VALVE: 'Valve',
|
||||
VIVO: 'Vivo',
|
||||
VIZIO: 'Vizio',
|
||||
VODAFONE: 'Vodafone',
|
||||
@@ -307,6 +314,7 @@ const EngineName = Object.freeze({
|
||||
AMAYA: 'Amaya',
|
||||
ARKWEB: 'ArkWeb',
|
||||
BLINK: 'Blink',
|
||||
DILLO: 'Dillo',
|
||||
EDGEHTML: 'EdgeHTML',
|
||||
FLOW: 'Flow',
|
||||
GECKO: 'Gecko',
|
||||
@@ -442,6 +450,7 @@ const Extension = Object.freeze({
|
||||
ELINKS: 'ELinks',
|
||||
HTTPIE: 'HTTPie',
|
||||
LYNX: 'Lynx',
|
||||
POWERSHELL: 'PowerShell',
|
||||
WGET: 'Wget'
|
||||
},
|
||||
Crawler: {
|
||||
@@ -604,29 +613,72 @@ const Extension = Object.freeze({
|
||||
},
|
||||
Email: {
|
||||
AIRMAIL: 'Airmail',
|
||||
ALPINE: 'Alpine',
|
||||
ANDROID_MAIL: 'Android',
|
||||
APPLE_MAIL: 'Mail',
|
||||
AQUA_MAIL: 'AquaMail',
|
||||
BALSA: 'Balsa',
|
||||
BARCA: 'Barca',
|
||||
BLUEMAIL: 'BlueMail',
|
||||
CANARY: 'Canary',
|
||||
CLAWS_MAIL: 'Claws Mail',
|
||||
DAUM_MAIL: 'DaumMail',
|
||||
EVOLUTION: 'Evolution',
|
||||
EM_CLIENT: 'eM Client',
|
||||
EUDORA: 'Eudora',
|
||||
EVOLUTION: 'Evolution',
|
||||
FAIR_EMAIL: 'FairEmail',
|
||||
FOXMAIL: 'Foxmail',
|
||||
GEARY: 'Geary',
|
||||
GNUS: 'Gnus',
|
||||
HORDE_IMP: 'Horde::IMP',
|
||||
IBM_NOTES: 'Lotus-Notes',
|
||||
INCREDIMAIL: 'IncrediMail',
|
||||
K9_MAIL: 'K-9 Mail',
|
||||
KMAIL: 'KMail',
|
||||
KMAIL2: 'kmail2',
|
||||
KONTACT: 'Kontact',
|
||||
MAILBIRD: 'Mailbird',
|
||||
MAILMATE: 'MailMate',
|
||||
MAILSPRING: 'Mailspring',
|
||||
MICROSOFT_OUTLOOK: 'Microsoft Outlook',
|
||||
MICROSOFT_OUTLOOK_MAC: 'MacOutlook',
|
||||
MUTT: 'Mutt',
|
||||
NAVER_MAILAPP: 'NaverMailApp',
|
||||
NEWTON: 'Newton',
|
||||
NINE: 'Nine',
|
||||
NYLAS_MAIL: 'NylasMail',
|
||||
OUTLOOK_EXPRESS: 'Outlook-Express',
|
||||
PEGASUS_MAIL: 'Pegasus Mail',
|
||||
POCOMAIL: 'PocoMail',
|
||||
POLYMAIL: 'Polymail',
|
||||
POSTBOX: 'Postbox',
|
||||
PROTON_MAIL: 'ProtonMail',
|
||||
PROTON_MAIL_BRIDGE: 'ProtonMail Bridge',
|
||||
QUALA_MAIL: 'Quala',
|
||||
R2MAIL2: 'R2Mail2',
|
||||
RAINLOOP: 'RainLoop',
|
||||
ROUNDCUBE: 'Roundcube Webmail',
|
||||
SAMSUNG_EMAIL: 'SamsungEmail',
|
||||
SPARK_MAIL: 'SparkDesktop',
|
||||
SPARROW: 'Sparrow',
|
||||
SPICEBIRD: 'Spicebird',
|
||||
SQUIRRELMAIL: 'SquirrelMail',
|
||||
SYLPHEED: 'Sylpheed',
|
||||
THE_BAT: 'The Bat!',
|
||||
THUNDERBIRD: 'Thunderbird',
|
||||
YAHOO_MAIL: 'Yahoo',
|
||||
TROJITA: 'Trojita',
|
||||
TURNPIKE: 'Turnpike',
|
||||
TUTANOTA: 'tutanota-desktop',
|
||||
WANDERLUST: 'Wanderlust',
|
||||
WINDOWS_LIVE_MAIL: 'Windows-Live-Mail',
|
||||
YAHOO_MAIL: 'Yahoo Mail',
|
||||
YAHOO_MAIL_IOS: 'Yahoo Mail',
|
||||
ZIMBRA: 'Zimbra',
|
||||
ZOHO_MAIL: 'ZohoMail-Desktop'
|
||||
},
|
||||
Fetcher: {
|
||||
AHREFS_SITEAUDIT: 'AhrefsSiteAudit',
|
||||
AMAZON_NOVA_ACT: 'NovaAct',
|
||||
ANTHROPIC_CLAUDE_USER: 'Claude-User',
|
||||
ASANA: 'Asana',
|
||||
BETTER_UPTIME_BOT: 'Better Uptime Bot',
|
||||
@@ -704,26 +756,36 @@ const Extension = Object.freeze({
|
||||
AIOHTTP: 'aiohttp',
|
||||
APACHE_HTTPCLIENT: 'Apache-HttpClient',
|
||||
AXIOS: 'axios',
|
||||
BUN: 'Bun',
|
||||
DART: 'Dart',
|
||||
DENO: 'Deno',
|
||||
GO_HTTP_CLIENT: 'go-http-client',
|
||||
GOT: 'got',
|
||||
GUZZLEHTTP: 'GuzzleHttp',
|
||||
HACKNEY: 'hackney',
|
||||
HTTP_RB: 'http.rb',
|
||||
JAVA: 'Java',
|
||||
JAVA_HTTPCLIENT: 'Java-http-client',
|
||||
JETTY: 'Jetty',
|
||||
JSDOM: 'jsdom',
|
||||
LIBWWW_PERL: 'libwww-perl',
|
||||
LUA_RESTY_HTTP: 'lua-resty-http',
|
||||
NEEDLE: 'Needle',
|
||||
NUTCH: 'Nutch',
|
||||
OKHTTP: 'OkHttp',
|
||||
NODE_FETCH: 'node-fetch',
|
||||
NODE_JS: 'Node.js',
|
||||
NODE_SUPERAGENT: 'node-superagent',
|
||||
OKHTTP: 'OkHttp',
|
||||
OCAML_COHTTP: 'ocaml-cohttp',
|
||||
PHP_SOAP: 'PHP-SOAP',
|
||||
POSTMAN_RUNTIME: 'PostmanRuntime',
|
||||
PYTHON_HTTPX: 'python-httpx',
|
||||
PYTHON_URLLIB: 'python-urllib',
|
||||
PYTHON_URLLIB3: 'python-urllib3',
|
||||
PYTHON_REQUESTS: 'python-requests',
|
||||
SCRAPY: 'Scrapy'
|
||||
REST_CLIENT: 'rest-client',
|
||||
SCRAPY: 'Scrapy',
|
||||
UNDICI: 'undici'
|
||||
}
|
||||
},
|
||||
DeviceVendor: {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Source: /src/enums/ua-parser-enums.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Enums for UAParser.js v2.0.6
|
||||
/* Enums for UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
@@ -19,6 +19,7 @@ const BrowserName = Object.freeze({
|
||||
AMAYA: 'Amaya',
|
||||
ANDROID: 'Android Browser',
|
||||
ARORA: 'Arora',
|
||||
ATLAS: 'Atlas',
|
||||
AVANT: 'Avant',
|
||||
AVAST: 'Avast Secure Browser',
|
||||
AVG: 'AVG Secure Browser',
|
||||
@@ -65,6 +66,7 @@ const BrowserName = Object.freeze({
|
||||
GOOGLE_SEARCH: 'GSA',
|
||||
HELIO: 'Helio',
|
||||
HEYTAP: 'HeyTap',
|
||||
HIBROWSER: 'HiBrowser',
|
||||
HONOR: 'Honor',
|
||||
HUAWEI: 'Huawei Browser',
|
||||
ICAB: 'iCab',
|
||||
@@ -115,6 +117,7 @@ const BrowserName = Object.freeze({
|
||||
OPERA_GX: 'Opera GX',
|
||||
OPERA_MINI: 'Opera Mini',
|
||||
OPERA_MOBI: 'Opera Mobi',
|
||||
OPERA_NEON: 'Opera Neon',
|
||||
OPERA_TABLET: 'Opera Tablet',
|
||||
OPERA_TOUCH: 'Opera Touch',
|
||||
OTTER: 'Otter',
|
||||
@@ -147,6 +150,7 @@ const BrowserName = Object.freeze({
|
||||
SNAPCHAT: 'Snapchat',
|
||||
SOGOU_EXPLORER: 'Sogou Explorer',
|
||||
SOGOU_MOBILE: 'Sogou Mobile',
|
||||
STEAM: 'Steam',
|
||||
SURF: 'Surf',
|
||||
SWIFTFOX: 'Swiftfox',
|
||||
TESLA: 'Tesla',
|
||||
@@ -228,8 +232,9 @@ const DeviceVendor = Object.freeze({
|
||||
ACER: 'Acer',
|
||||
ADVAN: 'Advan',
|
||||
ALCATEL: 'Alcatel',
|
||||
APPLE: 'Apple',
|
||||
AMAZON: 'Amazon',
|
||||
ANBERNIC: 'Anbernic',
|
||||
APPLE: 'Apple',
|
||||
ARCHOS: 'Archos',
|
||||
ASUS: 'ASUS',
|
||||
ATT: 'AT&T',
|
||||
@@ -258,6 +263,7 @@ const DeviceVendor = Object.freeze({
|
||||
LAVA: 'Lava',
|
||||
LENOVO: 'Lenovo',
|
||||
LG: 'LG',
|
||||
LOGITECH: 'Logitech',
|
||||
MEIZU: 'Meizu',
|
||||
MICROMAX: 'Micromax',
|
||||
MICROSOFT: 'Microsoft',
|
||||
@@ -291,6 +297,7 @@ const DeviceVendor = Object.freeze({
|
||||
TECNO: 'TECNO',
|
||||
TESLA: 'Tesla',
|
||||
ULEFONE: 'Ulefone',
|
||||
VALVE: 'Valve',
|
||||
VIVO: 'Vivo',
|
||||
VIZIO: 'Vizio',
|
||||
VODAFONE: 'Vodafone',
|
||||
@@ -311,6 +318,7 @@ const EngineName = Object.freeze({
|
||||
AMAYA: 'Amaya',
|
||||
ARKWEB: 'ArkWeb',
|
||||
BLINK: 'Blink',
|
||||
DILLO: 'Dillo',
|
||||
EDGEHTML: 'EdgeHTML',
|
||||
FLOW: 'Flow',
|
||||
GECKO: 'Gecko',
|
||||
@@ -446,6 +454,7 @@ const Extension = Object.freeze({
|
||||
ELINKS: 'ELinks',
|
||||
HTTPIE: 'HTTPie',
|
||||
LYNX: 'Lynx',
|
||||
POWERSHELL: 'PowerShell',
|
||||
WGET: 'Wget'
|
||||
},
|
||||
Crawler: {
|
||||
@@ -608,29 +617,72 @@ const Extension = Object.freeze({
|
||||
},
|
||||
Email: {
|
||||
AIRMAIL: 'Airmail',
|
||||
ALPINE: 'Alpine',
|
||||
ANDROID_MAIL: 'Android',
|
||||
APPLE_MAIL: 'Mail',
|
||||
AQUA_MAIL: 'AquaMail',
|
||||
BALSA: 'Balsa',
|
||||
BARCA: 'Barca',
|
||||
BLUEMAIL: 'BlueMail',
|
||||
CANARY: 'Canary',
|
||||
CLAWS_MAIL: 'Claws Mail',
|
||||
DAUM_MAIL: 'DaumMail',
|
||||
EVOLUTION: 'Evolution',
|
||||
EM_CLIENT: 'eM Client',
|
||||
EUDORA: 'Eudora',
|
||||
EVOLUTION: 'Evolution',
|
||||
FAIR_EMAIL: 'FairEmail',
|
||||
FOXMAIL: 'Foxmail',
|
||||
GEARY: 'Geary',
|
||||
GNUS: 'Gnus',
|
||||
HORDE_IMP: 'Horde::IMP',
|
||||
IBM_NOTES: 'Lotus-Notes',
|
||||
INCREDIMAIL: 'IncrediMail',
|
||||
K9_MAIL: 'K-9 Mail',
|
||||
KMAIL: 'KMail',
|
||||
KMAIL2: 'kmail2',
|
||||
KONTACT: 'Kontact',
|
||||
MAILBIRD: 'Mailbird',
|
||||
MAILMATE: 'MailMate',
|
||||
MAILSPRING: 'Mailspring',
|
||||
MICROSOFT_OUTLOOK: 'Microsoft Outlook',
|
||||
MICROSOFT_OUTLOOK_MAC: 'MacOutlook',
|
||||
MUTT: 'Mutt',
|
||||
NAVER_MAILAPP: 'NaverMailApp',
|
||||
NEWTON: 'Newton',
|
||||
NINE: 'Nine',
|
||||
NYLAS_MAIL: 'NylasMail',
|
||||
OUTLOOK_EXPRESS: 'Outlook-Express',
|
||||
PEGASUS_MAIL: 'Pegasus Mail',
|
||||
POCOMAIL: 'PocoMail',
|
||||
POLYMAIL: 'Polymail',
|
||||
POSTBOX: 'Postbox',
|
||||
PROTON_MAIL: 'ProtonMail',
|
||||
PROTON_MAIL_BRIDGE: 'ProtonMail Bridge',
|
||||
QUALA_MAIL: 'Quala',
|
||||
R2MAIL2: 'R2Mail2',
|
||||
RAINLOOP: 'RainLoop',
|
||||
ROUNDCUBE: 'Roundcube Webmail',
|
||||
SAMSUNG_EMAIL: 'SamsungEmail',
|
||||
SPARK_MAIL: 'SparkDesktop',
|
||||
SPARROW: 'Sparrow',
|
||||
SPICEBIRD: 'Spicebird',
|
||||
SQUIRRELMAIL: 'SquirrelMail',
|
||||
SYLPHEED: 'Sylpheed',
|
||||
THE_BAT: 'The Bat!',
|
||||
THUNDERBIRD: 'Thunderbird',
|
||||
YAHOO_MAIL: 'Yahoo',
|
||||
TROJITA: 'Trojita',
|
||||
TURNPIKE: 'Turnpike',
|
||||
TUTANOTA: 'tutanota-desktop',
|
||||
WANDERLUST: 'Wanderlust',
|
||||
WINDOWS_LIVE_MAIL: 'Windows-Live-Mail',
|
||||
YAHOO_MAIL: 'Yahoo Mail',
|
||||
YAHOO_MAIL_IOS: 'Yahoo Mail',
|
||||
ZIMBRA: 'Zimbra',
|
||||
ZOHO_MAIL: 'ZohoMail-Desktop'
|
||||
},
|
||||
Fetcher: {
|
||||
AHREFS_SITEAUDIT: 'AhrefsSiteAudit',
|
||||
AMAZON_NOVA_ACT: 'NovaAct',
|
||||
ANTHROPIC_CLAUDE_USER: 'Claude-User',
|
||||
ASANA: 'Asana',
|
||||
BETTER_UPTIME_BOT: 'Better Uptime Bot',
|
||||
@@ -708,26 +760,36 @@ const Extension = Object.freeze({
|
||||
AIOHTTP: 'aiohttp',
|
||||
APACHE_HTTPCLIENT: 'Apache-HttpClient',
|
||||
AXIOS: 'axios',
|
||||
BUN: 'Bun',
|
||||
DART: 'Dart',
|
||||
DENO: 'Deno',
|
||||
GO_HTTP_CLIENT: 'go-http-client',
|
||||
GOT: 'got',
|
||||
GUZZLEHTTP: 'GuzzleHttp',
|
||||
HACKNEY: 'hackney',
|
||||
HTTP_RB: 'http.rb',
|
||||
JAVA: 'Java',
|
||||
JAVA_HTTPCLIENT: 'Java-http-client',
|
||||
JETTY: 'Jetty',
|
||||
JSDOM: 'jsdom',
|
||||
LIBWWW_PERL: 'libwww-perl',
|
||||
LUA_RESTY_HTTP: 'lua-resty-http',
|
||||
NEEDLE: 'Needle',
|
||||
NUTCH: 'Nutch',
|
||||
OKHTTP: 'OkHttp',
|
||||
NODE_FETCH: 'node-fetch',
|
||||
NODE_JS: 'Node.js',
|
||||
NODE_SUPERAGENT: 'node-superagent',
|
||||
OKHTTP: 'OkHttp',
|
||||
OCAML_COHTTP: 'ocaml-cohttp',
|
||||
PHP_SOAP: 'PHP-SOAP',
|
||||
POSTMAN_RUNTIME: 'PostmanRuntime',
|
||||
PYTHON_HTTPX: 'python-httpx',
|
||||
PYTHON_URLLIB: 'python-urllib',
|
||||
PYTHON_URLLIB3: 'python-urllib3',
|
||||
PYTHON_REQUESTS: 'python-requests',
|
||||
SCRAPY: 'Scrapy'
|
||||
REST_CLIENT: 'rest-client',
|
||||
SCRAPY: 'Scrapy',
|
||||
UNDICI: 'undici'
|
||||
}
|
||||
},
|
||||
DeviceVendor: {
|
||||
|
||||
2
src/extensions/ua-parser-extensions.d.ts
vendored
2
src/extensions/ua-parser-extensions.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
// Type definitions for Helpers submodule of UAParser.js v2.0.5
|
||||
// Type definitions for Helpers submodule of UAParser.js v2.0.8
|
||||
// Project: https://github.com/faisalman/ua-parser-js
|
||||
// Definitions by: Faisal Salman <https://github.com/faisalman>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////
|
||||
/* Extensions for UAParser.js v2.0.6
|
||||
/* Extensions for UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
@@ -22,14 +22,27 @@ const INAPP = 'inapp';
|
||||
const MEDIAPLAYER = 'mediaplayer';
|
||||
const LIBRARY = 'library';
|
||||
|
||||
// Helper to normalize specific email client names
|
||||
const normalizeEmailName = function (str) {
|
||||
const map = {
|
||||
'YahooMobile': 'Yahoo Mail',
|
||||
'YahooMail': 'Yahoo Mail',
|
||||
'K-9': 'K-9 Mail',
|
||||
'K-9 Mail': 'K-9 Mail',
|
||||
'Zdesktop': 'Zimbra',
|
||||
'zdesktop': 'Zimbra'
|
||||
};
|
||||
return map[str] || str;
|
||||
};
|
||||
|
||||
//////////////////////
|
||||
// COMMAND LINE APPS
|
||||
/////////////////////
|
||||
|
||||
const CLIs = Object.freeze({
|
||||
browser : [
|
||||
// wget / curl / Lynx / ELinks / HTTPie
|
||||
[/(wget|curl|lynx|elinks|httpie)[\/ ]\(?([\w\.-]+)/i], [NAME, VERSION, [TYPE, CLI]]
|
||||
// wget / curl / Lynx / ELinks / HTTPie / PowerShell
|
||||
[/(wget|curl|lynx|elinks|httpie|powershell)[\/ ]\(?([\w\.-]+)/i], [NAME, VERSION, [TYPE, CLI]]
|
||||
]
|
||||
});
|
||||
|
||||
@@ -229,25 +242,55 @@ const ExtraDevices = Object.freeze({
|
||||
]
|
||||
});
|
||||
|
||||
///////////////
|
||||
//////////////
|
||||
// EMAIL APPS
|
||||
//////////////
|
||||
|
||||
const Emails = Object.freeze({
|
||||
browser : [
|
||||
[
|
||||
// Evolution / Kontact/KMail[2] / [Microsoft/Mac] Outlook / Thunderbird
|
||||
// Airmail / BlueMail / DaumMail / eMClient / Foxmail / NaverMailApp / Polymail
|
||||
// ProtonMail / SparkDesktop / Sparrow / Yahoo! Mail / Zimbra / ZohoMail-Desktop
|
||||
/((?:air|blue|daum|fox|poly|proton)mail|emclient|evolution|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|sparkdesktop|thunderbird|yahoo|zohomail-desktop)(?:m.+ail; |[\/ ])([\w\.]+)/i,
|
||||
// 1. Specific Android Mail Rule
|
||||
[/(android)\/([\w\.-]+email)/i],
|
||||
[NAME, VERSION, [TYPE, EMAIL]],
|
||||
|
||||
// Apple's Mail
|
||||
/(mail)\/([\w\.]+) cf/i
|
||||
], [NAME, VERSION, [TYPE, EMAIL]], [
|
||||
// 2. Standard Email Clients
|
||||
[
|
||||
new RegExp(
|
||||
'(' +
|
||||
// Clients ending in 'mail' (Case 1: Prefix + optional space + [e]mail)
|
||||
// Covers: AirMail, Claws Mail, FairEmail, SamsungEmail, Yahoo Mail, etc.
|
||||
'(?:air|aqua|blue|claws|daum|fair|fox|k-9|mac|nylas|pegasus|poco|poly|proton|samsung|squirrel|yahoo) ?e?mail(?:-desktop| app| bridge)?|' +
|
||||
// Standalone / Specific Names
|
||||
'microsoft outlook|r2mail2|spicebird|turnpike|yahoomobile|' +
|
||||
// Microsoft & Outlook Variants
|
||||
'(?:microsoft )?outlook(?:-express)?|macoutlook|windows-live-mail|' +
|
||||
// Specific Clients
|
||||
'alpine|balsa|barca|canary|emclient|eudora|evolution|geary|gnus|' +
|
||||
'horde::imp|incredimail|kmail2?|kontact|lotus-notes|' +
|
||||
'mail(?:bird|mate|spring)|mutt|navermailapp|newton|nine|postbox|' +
|
||||
'rainloop|roundcube webmail|spar(?:row|kdesktop)|sylpheed|' +
|
||||
'the bat!|thunderbird|trojita|tutanota-desktop|wanderlust|' +
|
||||
'zdesktop|zohomail-desktop' +
|
||||
')' +
|
||||
// Separator
|
||||
'(?:m.+ail; |[\\/ ])' +
|
||||
// Version (Updated to allow hyphens for Turnpike)
|
||||
'([\\w\\.-]+)',
|
||||
'i'
|
||||
)
|
||||
],
|
||||
[
|
||||
[NAME, normalizeEmailName],
|
||||
VERSION,
|
||||
[TYPE, EMAIL]
|
||||
],
|
||||
|
||||
// 3. Apple Mail Context
|
||||
[/(mail)\/([\w\.]+) cf/i],
|
||||
[NAME, VERSION, [TYPE, EMAIL]],
|
||||
|
||||
// Zimbra
|
||||
/zdesktop\/([\w\.]+)/i
|
||||
], [VERSION, [NAME, 'Zimbra'], [TYPE, EMAIL]]
|
||||
// 4. Zimbra Server
|
||||
[/(zimbra)\/([\w\.-]+)/i],
|
||||
[NAME, VERSION, [TYPE, EMAIL]]
|
||||
]
|
||||
});
|
||||
|
||||
@@ -258,7 +301,7 @@ const Emails = Object.freeze({
|
||||
const Fetchers = Object.freeze({
|
||||
browser : [
|
||||
[
|
||||
// Asana / Bitlybot / Better Uptime / BingPreview / Blueno / Cohere-AI / HubSpot Page Fetcher / kakaotalk-scrap / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot
|
||||
// Asana / Bitlybot / Better Uptime / BingPreview / Blueno / Cohere-AI / HubSpot Page Fetcher / kakaotalk-scrap / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot / WhatsApp
|
||||
// AhrefsSiteAudit - https://ahrefs.com/robot/site-audit
|
||||
// Buffer Link Preview Bot - https://scraper.buffer.com/about/bots/link-preview-bot
|
||||
// ChatGPT-User - https://platform.openai.com/docs/plugins/bot
|
||||
@@ -268,19 +311,19 @@ const Fetchers = Object.freeze({
|
||||
// Perplexity-User - https://docs.perplexity.ai/guides/bots
|
||||
// MistralAI-User - https://docs.mistral.ai/robots/
|
||||
// Yandex Bots - https://yandex.com/bots
|
||||
/(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|cohere-ai|hubspot page fetcher|mastodon|(?:bitly|bufferlinkpreview|discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero|zoom)bot|google-site-verification|iframely|kakaotalk-scrap|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|fordomain|pagechecker|searchshop)|yadirectfetcher)\/([\w\.]+)/i,
|
||||
/(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|cohere-ai|hubspot page fetcher|mastodon|(?:bitly|bufferlinkpreview|discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero|zoom)bot|google-site-verification|iframely|kakaotalk-scrap|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|fordomain|pagechecker|searchshop)|yadirectfetcher|whatsapp)\/([\w\.]+)/i,
|
||||
|
||||
// Bluesky
|
||||
/(bluesky) cardyb\/([\w\.]+)/i,
|
||||
|
||||
// Nova Act - https://github.com/aws/nova-act
|
||||
/agent-(novaact)\/([\w\.]+)/i,
|
||||
|
||||
// Skype
|
||||
/(skypeuripreview) preview\/([\w\.]+)/i,
|
||||
|
||||
// Slackbot - https://api.slack.com/robots
|
||||
/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i,
|
||||
|
||||
// WhatsApp
|
||||
/(whatsapp)\/([\w\.]+)/i
|
||||
/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i
|
||||
],
|
||||
[NAME, VERSION, [TYPE, FETCHER]],
|
||||
|
||||
@@ -389,13 +432,15 @@ const MediaPlayers = Object.freeze({
|
||||
|
||||
const Libraries = Object.freeze({
|
||||
browser : [
|
||||
// Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent
|
||||
[
|
||||
/^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|scrapy)\/([\w\.]+)/i,
|
||||
// Apache-HttpClient/Axios/Bun/Dart/go-http-client/got/GuzzleHttp/hackney/http.rb/Java[-HttpClient]/Jetty/jsdom/libwww-perl/lua-resty-http/Needle/Node.js/node-fetch/ocaml-cohttp/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/rest-client/Scrapy/superagent
|
||||
/^((?:apache|go|java)-http-?client|axios|bun|dart|deno|got|(?:guzzle|lua-resty-|ocaml-co|ok)http|hackney|http\.rb|java|jetty|libwww-perl|needle|node(?:\.js|-fetch|-superagent)|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|rest-client|scrapy)\/([\w\.]+)/i,
|
||||
/(adobeair|aiohttp|jsdom)\/([\w\.]+)/i,
|
||||
/(nutch)-([\w\.-]+)(\(|$)/i,
|
||||
/\((java)\/([\w\.]+)/i
|
||||
], [NAME, VERSION, [TYPE, LIBRARY]]
|
||||
], [NAME, VERSION, [TYPE, LIBRARY]], [
|
||||
/(node-fetch|undici)/i
|
||||
], [NAME, [TYPE, LIBRARY]]
|
||||
]
|
||||
});
|
||||
|
||||
@@ -449,4 +494,4 @@ module.exports = {
|
||||
Libraries,
|
||||
MediaPlayers,
|
||||
Vehicles
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Source: /src/extensions/ua-parser-extensions.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Extensions for UAParser.js v2.0.6
|
||||
/* Extensions for UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
@@ -26,14 +26,27 @@ const INAPP = 'inapp';
|
||||
const MEDIAPLAYER = 'mediaplayer';
|
||||
const LIBRARY = 'library';
|
||||
|
||||
// Helper to normalize specific email client names
|
||||
const normalizeEmailName = function (str) {
|
||||
const map = {
|
||||
'YahooMobile': 'Yahoo Mail',
|
||||
'YahooMail': 'Yahoo Mail',
|
||||
'K-9': 'K-9 Mail',
|
||||
'K-9 Mail': 'K-9 Mail',
|
||||
'Zdesktop': 'Zimbra',
|
||||
'zdesktop': 'Zimbra'
|
||||
};
|
||||
return map[str] || str;
|
||||
};
|
||||
|
||||
//////////////////////
|
||||
// COMMAND LINE APPS
|
||||
/////////////////////
|
||||
|
||||
const CLIs = Object.freeze({
|
||||
browser : [
|
||||
// wget / curl / Lynx / ELinks / HTTPie
|
||||
[/(wget|curl|lynx|elinks|httpie)[\/ ]\(?([\w\.-]+)/i], [NAME, VERSION, [TYPE, CLI]]
|
||||
// wget / curl / Lynx / ELinks / HTTPie / PowerShell
|
||||
[/(wget|curl|lynx|elinks|httpie|powershell)[\/ ]\(?([\w\.-]+)/i], [NAME, VERSION, [TYPE, CLI]]
|
||||
]
|
||||
});
|
||||
|
||||
@@ -233,25 +246,55 @@ const ExtraDevices = Object.freeze({
|
||||
]
|
||||
});
|
||||
|
||||
///////////////
|
||||
//////////////
|
||||
// EMAIL APPS
|
||||
//////////////
|
||||
|
||||
const Emails = Object.freeze({
|
||||
browser : [
|
||||
[
|
||||
// Evolution / Kontact/KMail[2] / [Microsoft/Mac] Outlook / Thunderbird
|
||||
// Airmail / BlueMail / DaumMail / eMClient / Foxmail / NaverMailApp / Polymail
|
||||
// ProtonMail / SparkDesktop / Sparrow / Yahoo! Mail / Zimbra / ZohoMail-Desktop
|
||||
/((?:air|blue|daum|fox|poly|proton)mail|emclient|evolution|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|sparkdesktop|thunderbird|yahoo|zohomail-desktop)(?:m.+ail; |[\/ ])([\w\.]+)/i,
|
||||
// 1. Specific Android Mail Rule
|
||||
[/(android)\/([\w\.-]+email)/i],
|
||||
[NAME, VERSION, [TYPE, EMAIL]],
|
||||
|
||||
// Apple's Mail
|
||||
/(mail)\/([\w\.]+) cf/i
|
||||
], [NAME, VERSION, [TYPE, EMAIL]], [
|
||||
// 2. Standard Email Clients
|
||||
[
|
||||
new RegExp(
|
||||
'(' +
|
||||
// Clients ending in 'mail' (Case 1: Prefix + optional space + [e]mail)
|
||||
// Covers: AirMail, Claws Mail, FairEmail, SamsungEmail, Yahoo Mail, etc.
|
||||
'(?:air|aqua|blue|claws|daum|fair|fox|k-9|mac|nylas|pegasus|poco|poly|proton|samsung|squirrel|yahoo) ?e?mail(?:-desktop| app| bridge)?|' +
|
||||
// Standalone / Specific Names
|
||||
'microsoft outlook|r2mail2|spicebird|turnpike|yahoomobile|' +
|
||||
// Microsoft & Outlook Variants
|
||||
'(?:microsoft )?outlook(?:-express)?|macoutlook|windows-live-mail|' +
|
||||
// Specific Clients
|
||||
'alpine|balsa|barca|canary|emclient|eudora|evolution|geary|gnus|' +
|
||||
'horde::imp|incredimail|kmail2?|kontact|lotus-notes|' +
|
||||
'mail(?:bird|mate|spring)|mutt|navermailapp|newton|nine|postbox|' +
|
||||
'rainloop|roundcube webmail|spar(?:row|kdesktop)|sylpheed|' +
|
||||
'the bat!|thunderbird|trojita|tutanota-desktop|wanderlust|' +
|
||||
'zdesktop|zohomail-desktop' +
|
||||
')' +
|
||||
// Separator
|
||||
'(?:m.+ail; |[\\/ ])' +
|
||||
// Version (Updated to allow hyphens for Turnpike)
|
||||
'([\\w\\.-]+)',
|
||||
'i'
|
||||
)
|
||||
],
|
||||
[
|
||||
[NAME, normalizeEmailName],
|
||||
VERSION,
|
||||
[TYPE, EMAIL]
|
||||
],
|
||||
|
||||
// 3. Apple Mail Context
|
||||
[/(mail)\/([\w\.]+) cf/i],
|
||||
[NAME, VERSION, [TYPE, EMAIL]],
|
||||
|
||||
// Zimbra
|
||||
/zdesktop\/([\w\.]+)/i
|
||||
], [VERSION, [NAME, 'Zimbra'], [TYPE, EMAIL]]
|
||||
// 4. Zimbra Server
|
||||
[/(zimbra)\/([\w\.-]+)/i],
|
||||
[NAME, VERSION, [TYPE, EMAIL]]
|
||||
]
|
||||
});
|
||||
|
||||
@@ -262,7 +305,7 @@ const Emails = Object.freeze({
|
||||
const Fetchers = Object.freeze({
|
||||
browser : [
|
||||
[
|
||||
// Asana / Bitlybot / Better Uptime / BingPreview / Blueno / Cohere-AI / HubSpot Page Fetcher / kakaotalk-scrap / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot
|
||||
// Asana / Bitlybot / Better Uptime / BingPreview / Blueno / Cohere-AI / HubSpot Page Fetcher / kakaotalk-scrap / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot / WhatsApp
|
||||
// AhrefsSiteAudit - https://ahrefs.com/robot/site-audit
|
||||
// Buffer Link Preview Bot - https://scraper.buffer.com/about/bots/link-preview-bot
|
||||
// ChatGPT-User - https://platform.openai.com/docs/plugins/bot
|
||||
@@ -272,19 +315,19 @@ const Fetchers = Object.freeze({
|
||||
// Perplexity-User - https://docs.perplexity.ai/guides/bots
|
||||
// MistralAI-User - https://docs.mistral.ai/robots/
|
||||
// Yandex Bots - https://yandex.com/bots
|
||||
/(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|cohere-ai|hubspot page fetcher|mastodon|(?:bitly|bufferlinkpreview|discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero|zoom)bot|google-site-verification|iframely|kakaotalk-scrap|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|fordomain|pagechecker|searchshop)|yadirectfetcher)\/([\w\.]+)/i,
|
||||
/(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|cohere-ai|hubspot page fetcher|mastodon|(?:bitly|bufferlinkpreview|discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero|zoom)bot|google-site-verification|iframely|kakaotalk-scrap|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|fordomain|pagechecker|searchshop)|yadirectfetcher|whatsapp)\/([\w\.]+)/i,
|
||||
|
||||
// Bluesky
|
||||
/(bluesky) cardyb\/([\w\.]+)/i,
|
||||
|
||||
// Nova Act - https://github.com/aws/nova-act
|
||||
/agent-(novaact)\/([\w\.]+)/i,
|
||||
|
||||
// Skype
|
||||
/(skypeuripreview) preview\/([\w\.]+)/i,
|
||||
|
||||
// Slackbot - https://api.slack.com/robots
|
||||
/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i,
|
||||
|
||||
// WhatsApp
|
||||
/(whatsapp)\/([\w\.]+)/i
|
||||
/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i
|
||||
],
|
||||
[NAME, VERSION, [TYPE, FETCHER]],
|
||||
|
||||
@@ -393,13 +436,15 @@ const MediaPlayers = Object.freeze({
|
||||
|
||||
const Libraries = Object.freeze({
|
||||
browser : [
|
||||
// Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent
|
||||
[
|
||||
/^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|scrapy)\/([\w\.]+)/i,
|
||||
// Apache-HttpClient/Axios/Bun/Dart/go-http-client/got/GuzzleHttp/hackney/http.rb/Java[-HttpClient]/Jetty/jsdom/libwww-perl/lua-resty-http/Needle/Node.js/node-fetch/ocaml-cohttp/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/rest-client/Scrapy/superagent
|
||||
/^((?:apache|go|java)-http-?client|axios|bun|dart|deno|got|(?:guzzle|lua-resty-|ocaml-co|ok)http|hackney|http\.rb|java|jetty|libwww-perl|needle|node(?:\.js|-fetch|-superagent)|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|rest-client|scrapy)\/([\w\.]+)/i,
|
||||
/(adobeair|aiohttp|jsdom)\/([\w\.]+)/i,
|
||||
/(nutch)-([\w\.-]+)(\(|$)/i,
|
||||
/\((java)\/([\w\.]+)/i
|
||||
], [NAME, VERSION, [TYPE, LIBRARY]]
|
||||
], [NAME, VERSION, [TYPE, LIBRARY]], [
|
||||
/(node-fetch|undici)/i
|
||||
], [NAME, [TYPE, LIBRARY]]
|
||||
]
|
||||
});
|
||||
|
||||
@@ -453,4 +498,4 @@ export {
|
||||
Libraries,
|
||||
MediaPlayers,
|
||||
Vehicles
|
||||
};
|
||||
};
|
||||
|
||||
44
src/helpers/ua-parser-helpers.d.ts
vendored
44
src/helpers/ua-parser-helpers.d.ts
vendored
@@ -1,15 +1,41 @@
|
||||
// Type definitions for Helpers submodule of UAParser.js v2.0.5
|
||||
// Type definitions for Helpers submodule of UAParser.js v2.0.8
|
||||
// Project: https://github.com/faisalman/ua-parser-js
|
||||
// Definitions by: Faisal Salman <https://github.com/faisalman>
|
||||
|
||||
import type { IResult } from "../main/ua-parser";
|
||||
|
||||
export function getDeviceVendor(model: string): string | undefined;
|
||||
export function isAppleSilicon(resultOrUA: IResult | string): boolean;
|
||||
export function isAIBot(resultOrUA: IResult | string): boolean;
|
||||
export function isBot(resultOrUA: IResult | string): boolean;
|
||||
export function isChromeFamily(resultOrUA: IResult | string): boolean;
|
||||
export function isElectron(): boolean;
|
||||
export function isFromEU(): boolean;
|
||||
export function getOutlookEdition(name: string, version: string): string;
|
||||
export function isFrozenUA(ua: string): boolean;
|
||||
export function isStandalonePWA(): boolean;
|
||||
|
||||
/**
|
||||
* @deprecated Moved to `device-detection` submodule
|
||||
*/
|
||||
export function getDeviceVendor(model: string): string | undefined;
|
||||
/**
|
||||
* @deprecated Moved to `device-detection` submodule
|
||||
*/
|
||||
export function isAppleSilicon(resultOrUA: IResult | string): boolean;
|
||||
/**
|
||||
* @deprecated Moved to `bot-detection` submodule
|
||||
*/
|
||||
export function isAIBot(resultOrUA: IResult | string): boolean;
|
||||
/**
|
||||
* @deprecated Moved to `bot-detection` submodule
|
||||
*/
|
||||
export function isBot(resultOrUA: IResult | string): boolean;
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
export function isChromeFamily(resultOrUA: IResult | string): boolean;
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
export function isElectron(): boolean;
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
export function isFromEU(): boolean;
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
export function isStandalonePWA(): boolean;
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////
|
||||
/* Helpers for UAParser.js v2.0.6
|
||||
/* Helpers for UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
@@ -7,170 +7,95 @@
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
const { UAParser } = require('../main/ua-parser');
|
||||
const { CPUArch, OSName, EngineName, Extension, BrowserType } = require('../enums/ua-parser-enums');
|
||||
const { Bots, Crawlers } = require('../extensions/ua-parser-extensions');
|
||||
const { isFromEU } = require('detect-europe-js');
|
||||
const { getDeviceVendor: _getDeviceVendor, isAppleSilicon: _isAppleSilicon } = require('../device-detection/device-detection');
|
||||
const { isBot: _isBot, isAICrawler } = require('../bot-detection/bot-detection');
|
||||
const { isChromeFamily: _isChromeFamily, isElectron: _isElectron, isStandalonePWA: _isStandalonePWA } = require('../browser-detection/browser-detection');
|
||||
const { isFromEU: _isFromEU } = require('../browser-detection/browser-detection');
|
||||
const { isFrozenUA } = require('ua-is-frozen');
|
||||
const { isStandalonePWA } = require('is-standalone-pwa');
|
||||
const { Crawler } = Extension.BrowserName;
|
||||
|
||||
const toResult = (value, head, ext) => typeof value === 'string' ? UAParser(value, head, ext) : value;
|
||||
/**
|
||||
* @deprecated Moved to `device-detection` submodule
|
||||
*/
|
||||
const getDeviceVendor = _getDeviceVendor;
|
||||
|
||||
const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor;
|
||||
/**
|
||||
* @deprecated Moved to `device-detection` submodule
|
||||
*/
|
||||
const isAppleSilicon = _isAppleSilicon;
|
||||
|
||||
const isAppleSilicon = (resultOrUA) => {
|
||||
const res = toResult(resultOrUA);
|
||||
if (res.os.is(OSName.MACOS)) {
|
||||
if (res.cpu.is(CPUArch.ARM)) {
|
||||
return true;
|
||||
}
|
||||
if (typeof resultOrUA !== 'string' && typeof window !== 'undefined') {
|
||||
try {
|
||||
const canvas = document.createElement('canvas');
|
||||
const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
|
||||
const debug = webgl.getExtension('WEBGL_debug_renderer_info');
|
||||
const renderer = webgl.getParameter(debug.UNMASKED_RENDERER_WEBGL);
|
||||
if (renderer.match(/apple m\d/i)) {
|
||||
return true;
|
||||
}
|
||||
} catch {
|
||||
return false;
|
||||
/**
|
||||
* @deprecated Moved to `bot-detection` submodule
|
||||
*/
|
||||
const isAIBot = isAICrawler;
|
||||
|
||||
/**
|
||||
* @deprecated Moved to `bot-detection` submodule
|
||||
*/
|
||||
const isBot = _isBot;
|
||||
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
const isChromeFamily = _isChromeFamily;
|
||||
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
const isElectron = () => _isElectron;
|
||||
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
const isFromEU = _isFromEU;
|
||||
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
const isStandalonePWA = _isStandalonePWA;
|
||||
|
||||
/**
|
||||
* Translates a raw Outlook User-Agent name/version into a
|
||||
* Developer-friendly Edition (e.g., "Outlook 2019 (Modern Word)").
|
||||
*/
|
||||
const getOutlookEdition = (name, version) => {
|
||||
if (!name || !version) return name;
|
||||
const cleanName = name.toLowerCase().replace(/microsoft\s+/, '');
|
||||
|
||||
// 1. Handle Mac Separately (Different Rendering Engine)
|
||||
if (cleanName === 'macoutlook') {
|
||||
const major = parseInt(version.split('.')[0], 10);
|
||||
if (major >= 16) return "Outlook for Mac (Modern)";
|
||||
return "Outlook for Mac (Legacy)";
|
||||
}
|
||||
|
||||
// 2. Handle Windows Outlook
|
||||
if (cleanName === 'outlook') {
|
||||
const parts = version.split('.').map(Number);
|
||||
const major = parts[0];
|
||||
const build = parts[2] || 0; // Build number is usually the 3rd part
|
||||
|
||||
// Pre-2016 Versions (Clear Major Version mapping)
|
||||
if (major === 15) return "Outlook 2013";
|
||||
if (major === 14) return "Outlook 2010";
|
||||
if (major === 12) return "Outlook 2007";
|
||||
if (major < 12) return "Outlook (Legacy)";
|
||||
|
||||
// The Version 16.0 Confusion
|
||||
if (major === 16) {
|
||||
// Build < 10000 = MSI (Volume License 2016/2019)
|
||||
// These render poorly (No SVG, older bugs)
|
||||
if (build < 10000) {
|
||||
return "Outlook 2016 (MSI / Volume License)";
|
||||
}
|
||||
// Build >= 10000 = Click-to-Run (Retail 2016 / 2019 / 365)
|
||||
// These render well (SVG support, modern CSS)
|
||||
return "Outlook 365 / 2019+ (Modern)";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const isAIBot = (resultOrUA) => [
|
||||
|
||||
// AI2
|
||||
Crawler.AI2_BOT,
|
||||
|
||||
// Amazon
|
||||
Crawler.AMAZON_BOT,
|
||||
|
||||
// Anthropic
|
||||
Crawler.ANTHROPIC_AI,
|
||||
Crawler.ANTHROPIC_CLAUDE_BOT,
|
||||
Crawler.ANTHROPIC_CLAUDE_SEARCHBOT,
|
||||
Crawler.ANTHROPIC_CLAUDE_WEB,
|
||||
|
||||
// Apple
|
||||
Crawler.APPLE_BOT,
|
||||
Crawler.APPLE_BOT_EXTENDED,
|
||||
|
||||
// Brave
|
||||
Crawler.BRAVE_BOT,
|
||||
|
||||
// ByteDance
|
||||
Crawler.BYTEDANCE_BYTESPIDER,
|
||||
Crawler.BYTEDANCE_TIKTOKSPIDER,
|
||||
|
||||
// Cohere
|
||||
Crawler.COHERE_TRAINING_DATA_CRAWLER,
|
||||
|
||||
// Common Crawl
|
||||
Crawler.COMMON_CRAWL_CCBOT,
|
||||
|
||||
// Coveo
|
||||
Crawler.COVEO_BOT,
|
||||
|
||||
// DataForSeo
|
||||
Crawler.DATAFORSEO_BOT,
|
||||
|
||||
// DeepSeek
|
||||
Crawler.DEEPSEEK_BOT,
|
||||
|
||||
// Diffbot
|
||||
Crawler.DIFFBOT,
|
||||
|
||||
// Google
|
||||
Crawler.GOOGLE_EXTENDED,
|
||||
Crawler.GOOGLE_OTHER,
|
||||
Crawler.GOOGLE_OTHER_IMAGE,
|
||||
Crawler.GOOGLE_OTHER_VIDEO,
|
||||
Crawler.GOOGLE_CLOUDVERTEXBOT,
|
||||
|
||||
// Hive AI
|
||||
Crawler.HIVE_IMAGESIFTBOT,
|
||||
|
||||
// Huawei
|
||||
Crawler.HUAWEI_PETALBOT,
|
||||
Crawler.HUAWEI_PANGUBOT,
|
||||
|
||||
// Hugging Face
|
||||
Crawler.HUGGINGFACE_BOT,
|
||||
|
||||
// Kangaroo
|
||||
Crawler.KANGAROO_BOT,
|
||||
|
||||
// Mendable.ai
|
||||
Crawler.FIRECRAWL_AGENT,
|
||||
|
||||
// Meta
|
||||
Crawler.META_FACEBOOKBOT,
|
||||
Crawler.META_EXTERNALAGENT,
|
||||
|
||||
// OpenAI
|
||||
Crawler.OPENAI_GPTBOT,
|
||||
Crawler.OPENAI_SEARCH_BOT,
|
||||
|
||||
// Perplexity
|
||||
Crawler.PERPLEXITY_BOT,
|
||||
|
||||
// Replicate
|
||||
Crawler.REPLICATE_BOT,
|
||||
|
||||
// Runpod
|
||||
Crawler.RUNPOD_BOT,
|
||||
|
||||
// SB Intuitions
|
||||
Crawler.SB_INTUITIONS_BOT,
|
||||
|
||||
// Semrush
|
||||
Crawler.SEMRUSH_BOT_CONTENTSHAKE,
|
||||
|
||||
// Timpi
|
||||
Crawler.TIMPI_BOT,
|
||||
|
||||
// Together AI
|
||||
Crawler.TOGETHER_BOT,
|
||||
|
||||
// Velen.io
|
||||
Crawler.HUNTER_VELENPUBLICWEBCRAWLER,
|
||||
|
||||
// Vercel
|
||||
Crawler.VERCEL_V0BOT,
|
||||
|
||||
// Webz.io
|
||||
Crawler.WEBZIO_OMGILI,
|
||||
Crawler.WEBZIO_OMGILI_BOT,
|
||||
Crawler.WEBZIO_EXTENDED,
|
||||
|
||||
// X
|
||||
Crawler.XAI_BOT,
|
||||
|
||||
// You.com
|
||||
Crawler.YOU_BOT,
|
||||
|
||||
// Zhipu AI
|
||||
Crawler.ZHIPU_CHATGLM_SPIDER
|
||||
]
|
||||
.map((s) => s.toLowerCase())
|
||||
.includes(String(toResult(resultOrUA, Crawlers).browser.name).toLowerCase());
|
||||
|
||||
const isBot = (resultOrUA) => [
|
||||
BrowserType.CLI,
|
||||
BrowserType.CRAWLER,
|
||||
BrowserType.FETCHER,
|
||||
BrowserType.LIBRARY
|
||||
].includes(toResult(resultOrUA, Bots).browser.type);
|
||||
|
||||
const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(EngineName.BLINK);
|
||||
|
||||
const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js
|
||||
/ electron\//i.test(navigator?.userAgent)); // browser
|
||||
// 3. Fallback for 'Outlook Express' or 'New Outlook' (Browser)
|
||||
return name;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getDeviceVendor,
|
||||
@@ -181,5 +106,6 @@ module.exports = {
|
||||
isElectron,
|
||||
isFromEU,
|
||||
isFrozenUA,
|
||||
isStandalonePWA
|
||||
isStandalonePWA,
|
||||
getOutlookEdition
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
// Source: /src/helpers/ua-parser-helpers.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Helpers for UAParser.js v2.0.6
|
||||
/* Helpers for UAParser.js v2.0.8
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License */
|
||||
@@ -11,170 +11,95 @@
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
import { UAParser } from '../main/ua-parser.mjs';
|
||||
import { CPUArch, OSName, EngineName, Extension, BrowserType } from '../enums/ua-parser-enums.mjs';
|
||||
import { Bots, Crawlers } from '../extensions/ua-parser-extensions.mjs';
|
||||
import { isFromEU } from 'detect-europe-js';
|
||||
import { getDeviceVendor as _getDeviceVendor, isAppleSilicon as _isAppleSilicon } from '../device-detection/device-detection.mjs';
|
||||
import { isBot as _isBot, isAICrawler } from '../bot-detection/bot-detection.mjs';
|
||||
import { isChromeFamily as _isChromeFamily, isElectron as _isElectron, isStandalonePWA as _isStandalonePWA } from '../browser-detection/browser-detection.mjs';
|
||||
import { isFromEU as _isFromEU } from '../browser-detection/browser-detection.mjs';
|
||||
import { isFrozenUA } from 'ua-is-frozen';
|
||||
import { isStandalonePWA } from 'is-standalone-pwa';
|
||||
const { Crawler } = Extension.BrowserName;
|
||||
|
||||
const toResult = (value, head, ext) => typeof value === 'string' ? UAParser(value, head, ext) : value;
|
||||
/**
|
||||
* @deprecated Moved to `device-detection` submodule
|
||||
*/
|
||||
const getDeviceVendor = _getDeviceVendor;
|
||||
|
||||
const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor;
|
||||
/**
|
||||
* @deprecated Moved to `device-detection` submodule
|
||||
*/
|
||||
const isAppleSilicon = _isAppleSilicon;
|
||||
|
||||
const isAppleSilicon = (resultOrUA) => {
|
||||
const res = toResult(resultOrUA);
|
||||
if (res.os.is(OSName.MACOS)) {
|
||||
if (res.cpu.is(CPUArch.ARM)) {
|
||||
return true;
|
||||
}
|
||||
if (typeof resultOrUA !== 'string' && typeof window !== 'undefined') {
|
||||
try {
|
||||
const canvas = document.createElement('canvas');
|
||||
const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
|
||||
const debug = webgl.getExtension('WEBGL_debug_renderer_info');
|
||||
const renderer = webgl.getParameter(debug.UNMASKED_RENDERER_WEBGL);
|
||||
if (renderer.match(/apple m\d/i)) {
|
||||
return true;
|
||||
}
|
||||
} catch {
|
||||
return false;
|
||||
/**
|
||||
* @deprecated Moved to `bot-detection` submodule
|
||||
*/
|
||||
const isAIBot = isAICrawler;
|
||||
|
||||
/**
|
||||
* @deprecated Moved to `bot-detection` submodule
|
||||
*/
|
||||
const isBot = _isBot;
|
||||
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
const isChromeFamily = _isChromeFamily;
|
||||
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
const isElectron = () => _isElectron;
|
||||
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
const isFromEU = _isFromEU;
|
||||
|
||||
/**
|
||||
* @deprecated Moved to `browser-detection` submodule
|
||||
*/
|
||||
const isStandalonePWA = _isStandalonePWA;
|
||||
|
||||
/**
|
||||
* Translates a raw Outlook User-Agent name/version into a
|
||||
* Developer-friendly Edition (e.g., "Outlook 2019 (Modern Word)").
|
||||
*/
|
||||
const getOutlookEdition = (name, version) => {
|
||||
if (!name || !version) return name;
|
||||
const cleanName = name.toLowerCase().replace(/microsoft\s+/, '');
|
||||
|
||||
// 1. Handle Mac Separately (Different Rendering Engine)
|
||||
if (cleanName === 'macoutlook') {
|
||||
const major = parseInt(version.split('.')[0], 10);
|
||||
if (major >= 16) return "Outlook for Mac (Modern)";
|
||||
return "Outlook for Mac (Legacy)";
|
||||
}
|
||||
|
||||
// 2. Handle Windows Outlook
|
||||
if (cleanName === 'outlook') {
|
||||
const parts = version.split('.').map(Number);
|
||||
const major = parts[0];
|
||||
const build = parts[2] || 0; // Build number is usually the 3rd part
|
||||
|
||||
// Pre-2016 Versions (Clear Major Version mapping)
|
||||
if (major === 15) return "Outlook 2013";
|
||||
if (major === 14) return "Outlook 2010";
|
||||
if (major === 12) return "Outlook 2007";
|
||||
if (major < 12) return "Outlook (Legacy)";
|
||||
|
||||
// The Version 16.0 Confusion
|
||||
if (major === 16) {
|
||||
// Build < 10000 = MSI (Volume License 2016/2019)
|
||||
// These render poorly (No SVG, older bugs)
|
||||
if (build < 10000) {
|
||||
return "Outlook 2016 (MSI / Volume License)";
|
||||
}
|
||||
// Build >= 10000 = Click-to-Run (Retail 2016 / 2019 / 365)
|
||||
// These render well (SVG support, modern CSS)
|
||||
return "Outlook 365 / 2019+ (Modern)";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const isAIBot = (resultOrUA) => [
|
||||
|
||||
// AI2
|
||||
Crawler.AI2_BOT,
|
||||
|
||||
// Amazon
|
||||
Crawler.AMAZON_BOT,
|
||||
|
||||
// Anthropic
|
||||
Crawler.ANTHROPIC_AI,
|
||||
Crawler.ANTHROPIC_CLAUDE_BOT,
|
||||
Crawler.ANTHROPIC_CLAUDE_SEARCHBOT,
|
||||
Crawler.ANTHROPIC_CLAUDE_WEB,
|
||||
|
||||
// Apple
|
||||
Crawler.APPLE_BOT,
|
||||
Crawler.APPLE_BOT_EXTENDED,
|
||||
|
||||
// Brave
|
||||
Crawler.BRAVE_BOT,
|
||||
|
||||
// ByteDance
|
||||
Crawler.BYTEDANCE_BYTESPIDER,
|
||||
Crawler.BYTEDANCE_TIKTOKSPIDER,
|
||||
|
||||
// Cohere
|
||||
Crawler.COHERE_TRAINING_DATA_CRAWLER,
|
||||
|
||||
// Common Crawl
|
||||
Crawler.COMMON_CRAWL_CCBOT,
|
||||
|
||||
// Coveo
|
||||
Crawler.COVEO_BOT,
|
||||
|
||||
// DataForSeo
|
||||
Crawler.DATAFORSEO_BOT,
|
||||
|
||||
// DeepSeek
|
||||
Crawler.DEEPSEEK_BOT,
|
||||
|
||||
// Diffbot
|
||||
Crawler.DIFFBOT,
|
||||
|
||||
// Google
|
||||
Crawler.GOOGLE_EXTENDED,
|
||||
Crawler.GOOGLE_OTHER,
|
||||
Crawler.GOOGLE_OTHER_IMAGE,
|
||||
Crawler.GOOGLE_OTHER_VIDEO,
|
||||
Crawler.GOOGLE_CLOUDVERTEXBOT,
|
||||
|
||||
// Hive AI
|
||||
Crawler.HIVE_IMAGESIFTBOT,
|
||||
|
||||
// Huawei
|
||||
Crawler.HUAWEI_PETALBOT,
|
||||
Crawler.HUAWEI_PANGUBOT,
|
||||
|
||||
// Hugging Face
|
||||
Crawler.HUGGINGFACE_BOT,
|
||||
|
||||
// Kangaroo
|
||||
Crawler.KANGAROO_BOT,
|
||||
|
||||
// Mendable.ai
|
||||
Crawler.FIRECRAWL_AGENT,
|
||||
|
||||
// Meta
|
||||
Crawler.META_FACEBOOKBOT,
|
||||
Crawler.META_EXTERNALAGENT,
|
||||
|
||||
// OpenAI
|
||||
Crawler.OPENAI_GPTBOT,
|
||||
Crawler.OPENAI_SEARCH_BOT,
|
||||
|
||||
// Perplexity
|
||||
Crawler.PERPLEXITY_BOT,
|
||||
|
||||
// Replicate
|
||||
Crawler.REPLICATE_BOT,
|
||||
|
||||
// Runpod
|
||||
Crawler.RUNPOD_BOT,
|
||||
|
||||
// SB Intuitions
|
||||
Crawler.SB_INTUITIONS_BOT,
|
||||
|
||||
// Semrush
|
||||
Crawler.SEMRUSH_BOT_CONTENTSHAKE,
|
||||
|
||||
// Timpi
|
||||
Crawler.TIMPI_BOT,
|
||||
|
||||
// Together AI
|
||||
Crawler.TOGETHER_BOT,
|
||||
|
||||
// Velen.io
|
||||
Crawler.HUNTER_VELENPUBLICWEBCRAWLER,
|
||||
|
||||
// Vercel
|
||||
Crawler.VERCEL_V0BOT,
|
||||
|
||||
// Webz.io
|
||||
Crawler.WEBZIO_OMGILI,
|
||||
Crawler.WEBZIO_OMGILI_BOT,
|
||||
Crawler.WEBZIO_EXTENDED,
|
||||
|
||||
// X
|
||||
Crawler.XAI_BOT,
|
||||
|
||||
// You.com
|
||||
Crawler.YOU_BOT,
|
||||
|
||||
// Zhipu AI
|
||||
Crawler.ZHIPU_CHATGLM_SPIDER
|
||||
]
|
||||
.map((s) => s.toLowerCase())
|
||||
.includes(String(toResult(resultOrUA, Crawlers).browser.name).toLowerCase());
|
||||
|
||||
const isBot = (resultOrUA) => [
|
||||
BrowserType.CLI,
|
||||
BrowserType.CRAWLER,
|
||||
BrowserType.FETCHER,
|
||||
BrowserType.LIBRARY
|
||||
].includes(toResult(resultOrUA, Bots).browser.type);
|
||||
|
||||
const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(EngineName.BLINK);
|
||||
|
||||
const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js
|
||||
/ electron\//i.test(navigator?.userAgent)); // browser
|
||||
// 3. Fallback for 'Outlook Express' or 'New Outlook' (Browser)
|
||||
return name;
|
||||
};
|
||||
|
||||
export {
|
||||
getDeviceVendor,
|
||||
@@ -185,5 +110,6 @@ export {
|
||||
isElectron,
|
||||
isFromEU,
|
||||
isFrozenUA,
|
||||
isStandalonePWA
|
||||
isStandalonePWA,
|
||||
getOutlookEdition
|
||||
}
|
||||
2
src/main/ua-parser.d.ts
vendored
2
src/main/ua-parser.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
// Type definitions for UAParser.js v2.0.6
|
||||
// Type definitions for UAParser.js v2.0.8
|
||||
// Project: https://github.com/faisalman/ua-parser-js
|
||||
// Definitions by: Faisal Salman <https://github.com/faisalman>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/* UAParser.js v2.0.6
|
||||
/* UAParser.js v2.0.8
|
||||
Copyright © 2012-2025 Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License *//*
|
||||
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
|
||||
@@ -19,7 +19,7 @@
|
||||
// Constants
|
||||
/////////////
|
||||
|
||||
var LIBVERSION = '2.0.6',
|
||||
var LIBVERSION = '2.0.8',
|
||||
UA_MAX_LENGTH = 500,
|
||||
USER_AGENT = 'user-agent',
|
||||
EMPTY = '',
|
||||
@@ -375,9 +375,10 @@
|
||||
/(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet
|
||||
/(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer
|
||||
|
||||
// Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble/Palemoon
|
||||
/(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:lg |qute)browser|palemoon)\/([-\w\.]+)/i,
|
||||
// Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
|
||||
// Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble/Palemoon/HiBrowser
|
||||
/(atlas|flock|rockmelt|midori|epiphany|silk|skyfire|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:hi|lg |ovi|qute)browser|palemoon)\/v?([-\w\.]+)/i,
|
||||
// Atlas/Rekonq/Puffin/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
|
||||
/(brave)(?: chrome)?\/([\d\.]+)/i, // Brave
|
||||
/(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
|
||||
@@ -404,7 +405,9 @@
|
||||
], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [
|
||||
/\bfocus\/([\w\.]+)/i // Firefox Focus
|
||||
], [VERSION, [NAME, FIREFOX+' Focus']], [
|
||||
/\bopt\/([\w\.]+)/i // Opera Touch
|
||||
/ mms\/([\w\.]+)$/i // Opera Neon
|
||||
], [VERSION, [NAME, OPERA+' Neon']], [
|
||||
/ opt\/([\w\.]+)$/i // Opera Touch
|
||||
], [VERSION, [NAME, OPERA+' Touch']], [
|
||||
/coc_coc\w+\/([\w\.]+)/i // Coc Coc Browser
|
||||
], [VERSION, [NAME, 'Coc Coc']], [
|
||||
@@ -432,7 +435,7 @@
|
||||
/(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla
|
||||
/m?(qqbrowser|2345(?=browser|chrome|explorer))\w*[\/ ]?v?([\w\.]+)/i // QQ/2345
|
||||
], [NAME, VERSION], [
|
||||
/(lbbrowser|rekonq)/i // LieBao Browser/Rekonq
|
||||
/(lbbrowser|rekonq|steam(?= (clie|tenf|gameo)))/i // LieBao Browser/Rekonq/Steam
|
||||
], [NAME], [
|
||||
/ome\/([\w\.]+) \w* ?(iron) saf/i, // Iron
|
||||
/ome\/([\w\.]+).+qihu (360)[es]e/i // 360
|
||||
@@ -453,7 +456,7 @@
|
||||
], [NAME, VERSION, [TYPE, INAPP]], [
|
||||
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
|
||||
], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [
|
||||
/musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok
|
||||
/(?:musical_ly|trill)(?:.+app_?version\/|_)([\w\.]+)/i // TikTok
|
||||
], [VERSION, [NAME, 'TikTok'], [TYPE, INAPP]], [
|
||||
/\[(linkedin)app\]/i // LinkedIn App for iOS & Android
|
||||
], [NAME, [TYPE, INAPP]], [
|
||||
@@ -598,17 +601,17 @@
|
||||
/\b(?:xiao)?((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
|
||||
],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
|
||||
|
||||
/\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
|
||||
/\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
|
||||
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
|
||||
/\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
|
||||
/oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models
|
||||
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i, // Xiaomi Mi
|
||||
// Xiaomi Redmi / POCO / Black Shark / Qin
|
||||
/oid[^\)]+; (redmi[\-_ ]?(?:note|k)?[\w_ ]+|m?[12]\d[01]\d\w{3,6}|poco[\w ]+|(shark )?\w{3}-[ah]0|qin ?[1-3](s\+|ultra| pro)?)( bui|; wv|\))/i,
|
||||
// Xiaomi Mi
|
||||
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i,
|
||||
/ ([\w ]+) miui\/v?\d/i
|
||||
], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
|
||||
|
||||
// OnePlus
|
||||
/droid.+; (cph2[3-6]\d[13579]|((gm|hd)19|(ac|be|in|kb)20|(d[en]|eb|le|mt)21|ne22)[0-2]\d|p[g-k]\w[1m]10)\b/i,
|
||||
/droid.+; (cph2[3-6]\d[13579]|((gm|hd)19|(ac|be|in|kb)20|(d[en]|eb|le|mt)21|ne22)[0-2]\d|p[g-l]\w[1m]10)\b/i,
|
||||
/(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i
|
||||
], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [
|
||||
|
||||
@@ -648,7 +651,7 @@
|
||||
], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [
|
||||
|
||||
// LG
|
||||
/((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
|
||||
/\b(?:lg)?([vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
|
||||
], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [
|
||||
/(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i,
|
||||
/\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch|webos))(\w+)/i,
|
||||
@@ -852,10 +855,10 @@
|
||||
], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [
|
||||
/(ouya)/i, // Ouya
|
||||
/(nintendo) (\w+)/i, // Nintendo
|
||||
/(retroid) (pocket ([^\)]+))/i // Retroid Pocket
|
||||
], [VENDOR, MODEL, [TYPE, CONSOLE]], [
|
||||
/droid.+; (shield)( bui|\))/i // Nvidia Portable
|
||||
], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [
|
||||
/(retroid) (pocket ([^\)]+))/i, // Retroid Pocket
|
||||
/(valve).+(steam deck)/i,
|
||||
/droid.+; ((shield|rgcube|gr0006))( bui|\))/i // Nvidia Portable/Anbernic/Logitech
|
||||
], [[VENDOR, strMapper, { 'Nvidia': 'Shield', 'Anbernic': 'RGCUBE', 'Logitech': 'GR0006' }], MODEL, [TYPE, CONSOLE]], [
|
||||
|
||||
///////////////////
|
||||
// WEARABLES
|
||||
@@ -864,7 +867,7 @@
|
||||
/\b(sm-[lr]\d\d[0156][fnuw]?s?|gear live)\b/i // Samsung Galaxy Watch
|
||||
], [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]], [
|
||||
/((pebble))app/i, // Pebble
|
||||
/(asus|google|lg|oppo) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch
|
||||
/(asus|google|lg|oppo|xiaomi) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch / Xiaomi Watch
|
||||
], [VENDOR, MODEL, [TYPE, WEARABLE]], [
|
||||
/(ow(?:19|20)?we?[1-3]{1,3})/i // Oppo Watch
|
||||
], [MODEL, [VENDOR, OPPO], [TYPE, WEARABLE]], [
|
||||
@@ -939,7 +942,7 @@
|
||||
/(presto)\/([\w\.]+)/i, // Presto
|
||||
/(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna|servo)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna/Servo
|
||||
/ekioh(flow)\/([\w\.]+)/i, // Flow
|
||||
/(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links
|
||||
/(khtml|tasman|links|dillo)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links/Dillo
|
||||
/(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab
|
||||
|
||||
/\b(libweb)/i // LibWeb
|
||||
@@ -968,7 +971,8 @@
|
||||
|
||||
// iOS/macOS
|
||||
/[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
|
||||
/(?:ios;fbsv|ios(?=.+ip(?:ad|hone))|ip(?:ad|hone)(?: |.+i(?:pad)?)os)[\/ ]([\w\.]+)/i,
|
||||
/(?:ios;fbsv|ios(?=.+ip(?:ad|hone)|.+apple ?tv)|ip(?:ad|hone)(?: |.+i(?:pad)?)os|apple ?tv.+ios)[\/ ]([\w\.]+)/i,
|
||||
/\btvos ?([\w\.]+)/i,
|
||||
/cfnetwork\/.+darwin/i
|
||||
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
|
||||
/(mac os x) ?([\w\. ]*)/i,
|
||||
@@ -1000,7 +1004,7 @@
|
||||
], [VERSION, [NAME, BLACKBERRY]], [
|
||||
/(?:symbian ?os|symbos|s60(?=;)|series ?60)[-\/ ]?([\w\.]*)/i // Symbian
|
||||
], [VERSION, [NAME, 'Symbian']], [
|
||||
/mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS
|
||||
/mozilla\/[\d\.]+ \((?:mobile[;\w ]*|tablet|tv|[^\)]*(?:viera|lg(?:l25|-d300)|alcatel ?o.+|y300-f1)); rv:([\w\.]+)\).+gecko\//i // Firefox OS
|
||||
], [VERSION, [NAME, FIREFOX+' OS']], [
|
||||
/\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS
|
||||
/webos(?:[ \/]?|\.tv-20(?=2[2-9]))(\d[\d\.]*)/i
|
||||
@@ -1016,7 +1020,6 @@
|
||||
], [[NAME, "Chrome OS"], VERSION],[
|
||||
|
||||
// Smart TVs
|
||||
/panasonic;(viera)/i, // Panasonic Viera
|
||||
/(netrange)mmh/i, // Netrange
|
||||
/(nettv)\/(\d+\.[\w\.]+)/i, // NetTV
|
||||
|
||||
@@ -1142,25 +1145,25 @@
|
||||
};
|
||||
}
|
||||
|
||||
if (!NAVIGATOR_UADATA) {
|
||||
IData.prototype.then = function (cb) {
|
||||
var that = this;
|
||||
var IDataResolve = function () {
|
||||
for (var prop in that) {
|
||||
if (that.hasOwnProperty(prop)) {
|
||||
this[prop] = that[prop];
|
||||
}
|
||||
IData.prototype.then = function (cb) {
|
||||
var that = this;
|
||||
var IDataResolve = function () {
|
||||
for (var prop in that) {
|
||||
if (that.hasOwnProperty(prop)) {
|
||||
this[prop] = that[prop];
|
||||
}
|
||||
};
|
||||
IDataResolve.prototype = {
|
||||
is : IData.prototype.is,
|
||||
toString : IData.prototype.toString
|
||||
};
|
||||
var resolveData = new IDataResolve();
|
||||
cb(resolveData);
|
||||
return resolveData;
|
||||
}
|
||||
};
|
||||
}
|
||||
IDataResolve.prototype = {
|
||||
is : IData.prototype.is,
|
||||
toString : IData.prototype.toString,
|
||||
withClientHints : IData.prototype.withClientHints,
|
||||
withFeatureCheck : IData.prototype.withFeatureCheck
|
||||
};
|
||||
var resolveData = new IDataResolve();
|
||||
cb(resolveData);
|
||||
return resolveData;
|
||||
};
|
||||
|
||||
return new IData();
|
||||
};
|
||||
@@ -1192,189 +1195,6 @@
|
||||
}
|
||||
|
||||
function UAItem (itemType, ua, rgxMap, uaCH) {
|
||||
|
||||
this.get = function (prop) {
|
||||
if (!prop) return this.data;
|
||||
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
|
||||
};
|
||||
|
||||
this.set = function (prop, val) {
|
||||
this.data[prop] = val;
|
||||
return this;
|
||||
};
|
||||
|
||||
this.setCH = function (ch) {
|
||||
this.uaCH = ch;
|
||||
return this;
|
||||
};
|
||||
|
||||
this.detectFeature = function () {
|
||||
if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
// Brave-specific detection
|
||||
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
|
||||
this.set(NAME, 'Brave');
|
||||
}
|
||||
break;
|
||||
case DEVICE:
|
||||
// Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
|
||||
if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
|
||||
if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
|
||||
this.set(MODEL, 'iPad')
|
||||
.set(TYPE, TABLET);
|
||||
}
|
||||
break;
|
||||
case OS:
|
||||
// Chrome-specific detection: check for 'platform' value of navigator.userAgentData
|
||||
if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
|
||||
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
|
||||
}
|
||||
break;
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var detect = function (itemType) {
|
||||
return data[itemType]
|
||||
.getItem()
|
||||
.detectFeature()
|
||||
.get();
|
||||
};
|
||||
this.set(BROWSER, detect(BROWSER))
|
||||
.set(CPU, detect(CPU))
|
||||
.set(DEVICE, detect(DEVICE))
|
||||
.set(ENGINE, detect(ENGINE))
|
||||
.set(OS, detect(OS));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
this.parseUA = function () {
|
||||
if (this.itemType != RESULT) {
|
||||
rgxMapper.call(this.data, this.ua, this.rgxMap);
|
||||
}
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
this.set(MAJOR, majorize(this.get(VERSION)));
|
||||
break;
|
||||
case OS:
|
||||
if (this.get(NAME) == 'iOS' && this.get(VERSION) == '18.6') {
|
||||
// Based on the assumption that iOS version is tightly coupled with Safari version
|
||||
var realVersion = /\) Version\/([\d\.]+)/.exec(this.ua); // Get Safari version
|
||||
if (realVersion && parseInt(realVersion[1].substring(0,2), 10) >= 26) {
|
||||
this.set(VERSION, realVersion[1]); // Set as iOS version
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
this.parseCH = function () {
|
||||
var uaCH = this.uaCH,
|
||||
rgxMap = this.rgxMap;
|
||||
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
case ENGINE:
|
||||
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
|
||||
if (brands) {
|
||||
for (var i=0; i<brands.length; i++) {
|
||||
var brandName = brands[i].brand || brands[i],
|
||||
brandVersion = brands[i].version;
|
||||
if (this.itemType == BROWSER &&
|
||||
!/not.a.brand/i.test(brandName) &&
|
||||
(!prevName ||
|
||||
(/Chrom/.test(prevName) && brandName != CHROMIUM) ||
|
||||
(prevName == EDGE && /WebView2/.test(brandName))
|
||||
)) {
|
||||
brandName = strMapper(brandName, browserHintsMap);
|
||||
prevName = this.get(NAME);
|
||||
if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) {
|
||||
this.set(NAME, brandName)
|
||||
.set(VERSION, brandVersion)
|
||||
.set(MAJOR, majorize(brandVersion));
|
||||
}
|
||||
prevName = brandName;
|
||||
}
|
||||
if (this.itemType == ENGINE && brandName == CHROMIUM) {
|
||||
this.set(VERSION, brandVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPU:
|
||||
var archName = uaCH[ARCHITECTURE];
|
||||
if (archName) {
|
||||
if (archName && uaCH[BITNESS] == '64') archName += '64';
|
||||
rgxMapper.call(this.data, archName + ';', rgxMap);
|
||||
}
|
||||
break;
|
||||
case DEVICE:
|
||||
if (uaCH[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
if (uaCH[MODEL]) {
|
||||
this.set(MODEL, uaCH[MODEL]);
|
||||
if (!this.get(TYPE) || !this.get(VENDOR)) {
|
||||
var reParse = {};
|
||||
rgxMapper.call(reParse, 'droid 9; ' + uaCH[MODEL] + ')', rgxMap);
|
||||
if (!this.get(TYPE) && !!reParse.type) {
|
||||
this.set(TYPE, reParse.type);
|
||||
}
|
||||
if (!this.get(VENDOR) && !!reParse.vendor) {
|
||||
this.set(VENDOR, reParse.vendor);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uaCH[FORMFACTORS]) {
|
||||
var ff;
|
||||
if (typeof uaCH[FORMFACTORS] !== 'string') {
|
||||
var idx = 0;
|
||||
while (!ff && idx < uaCH[FORMFACTORS].length) {
|
||||
ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap);
|
||||
}
|
||||
} else {
|
||||
ff = strMapper(uaCH[FORMFACTORS], formFactorsMap);
|
||||
}
|
||||
this.set(TYPE, ff);
|
||||
}
|
||||
break;
|
||||
case OS:
|
||||
var osName = uaCH[PLATFORM];
|
||||
if(osName) {
|
||||
var osVersion = uaCH[PLATFORMVER];
|
||||
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
|
||||
this.set(NAME, osName)
|
||||
.set(VERSION, osVersion);
|
||||
}
|
||||
// Xbox-Specific Detection
|
||||
if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') {
|
||||
this.set(NAME, 'Xbox')
|
||||
.set(VERSION, undefined);
|
||||
}
|
||||
break;
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var parse = function (itemType) {
|
||||
return data[itemType]
|
||||
.getItem()
|
||||
.setCH(uaCH)
|
||||
.parseCH()
|
||||
.get();
|
||||
};
|
||||
this.set(BROWSER, parse(BROWSER))
|
||||
.set(CPU, parse(CPU))
|
||||
.set(DEVICE, parse(DEVICE))
|
||||
.set(ENGINE, parse(ENGINE))
|
||||
.set(OS, parse(OS));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
setProps.call(this, [
|
||||
['itemType', itemType],
|
||||
['ua', ua],
|
||||
@@ -1382,10 +1202,191 @@
|
||||
['rgxMap', rgxMap],
|
||||
['data', createIData(this, itemType)]
|
||||
]);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
UAItem.prototype.get = function (prop) {
|
||||
if (!prop) return this.data;
|
||||
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
|
||||
};
|
||||
|
||||
UAItem.prototype.set = function (prop, val) {
|
||||
this.data[prop] = val;
|
||||
return this;
|
||||
};
|
||||
|
||||
UAItem.prototype.setCH = function (ch) {
|
||||
this.uaCH = ch;
|
||||
return this;
|
||||
};
|
||||
|
||||
UAItem.prototype.detectFeature = function () {
|
||||
if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
// Brave-specific detection
|
||||
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
|
||||
this.set(NAME, 'Brave');
|
||||
}
|
||||
break;
|
||||
case DEVICE:
|
||||
// Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
|
||||
if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
|
||||
if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
|
||||
this.set(MODEL, 'iPad')
|
||||
.set(TYPE, TABLET);
|
||||
}
|
||||
break;
|
||||
case OS:
|
||||
// Chrome-specific detection: check for 'platform' value of navigator.userAgentData
|
||||
if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
|
||||
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
|
||||
}
|
||||
break;
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var detect = function (itemType) {
|
||||
return data[itemType]
|
||||
.getItem()
|
||||
.detectFeature()
|
||||
.get();
|
||||
};
|
||||
this.set(BROWSER, detect(BROWSER))
|
||||
.set(CPU, detect(CPU))
|
||||
.set(DEVICE, detect(DEVICE))
|
||||
.set(ENGINE, detect(ENGINE))
|
||||
.set(OS, detect(OS));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
UAItem.prototype.parseUA = function () {
|
||||
if (this.itemType != RESULT) {
|
||||
rgxMapper.call(this.data, this.ua, this.rgxMap);
|
||||
}
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
this.set(MAJOR, majorize(this.get(VERSION)));
|
||||
break;
|
||||
case OS:
|
||||
if (this.get(NAME) == 'iOS' && this.get(VERSION) == '18.6') {
|
||||
// Based on the assumption that iOS version is tightly coupled with Safari version
|
||||
var realVersion = /\) Version\/([\d\.]+)/.exec(this.ua); // Get Safari version
|
||||
if (realVersion && parseInt(realVersion[1].substring(0,2), 10) >= 26) {
|
||||
this.set(VERSION, realVersion[1]); // Set as iOS version
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
UAItem.prototype.parseCH = function () {
|
||||
var uaCH = this.uaCH,
|
||||
rgxMap = this.rgxMap;
|
||||
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
case ENGINE:
|
||||
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
|
||||
if (brands) {
|
||||
for (var i=0; i<brands.length; i++) {
|
||||
var brandName = brands[i].brand || brands[i],
|
||||
brandVersion = brands[i].version;
|
||||
if (this.itemType == BROWSER &&
|
||||
!/not.a.brand/i.test(brandName) &&
|
||||
(!prevName ||
|
||||
(/Chrom/.test(prevName) && brandName != CHROMIUM) ||
|
||||
(prevName == EDGE && /WebView2/.test(brandName))
|
||||
)) {
|
||||
brandName = strMapper(brandName, browserHintsMap);
|
||||
prevName = this.get(NAME);
|
||||
if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) {
|
||||
this.set(NAME, brandName)
|
||||
.set(VERSION, brandVersion)
|
||||
.set(MAJOR, majorize(brandVersion));
|
||||
}
|
||||
prevName = brandName;
|
||||
}
|
||||
if (this.itemType == ENGINE && brandName == CHROMIUM) {
|
||||
this.set(VERSION, brandVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPU:
|
||||
var archName = uaCH[ARCHITECTURE];
|
||||
if (archName) {
|
||||
if (archName && uaCH[BITNESS] == '64') archName += '64';
|
||||
rgxMapper.call(this.data, archName + ';', rgxMap);
|
||||
}
|
||||
break;
|
||||
case DEVICE:
|
||||
if (uaCH[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
if (uaCH[MODEL]) {
|
||||
this.set(MODEL, uaCH[MODEL]);
|
||||
if (!this.get(TYPE) || !this.get(VENDOR)) {
|
||||
var reParse = {};
|
||||
rgxMapper.call(reParse, 'droid 9; ' + uaCH[MODEL] + ')', rgxMap);
|
||||
if (!this.get(TYPE) && !!reParse.type) {
|
||||
this.set(TYPE, reParse.type);
|
||||
}
|
||||
if (!this.get(VENDOR) && !!reParse.vendor) {
|
||||
this.set(VENDOR, reParse.vendor);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uaCH[FORMFACTORS]) {
|
||||
var ff;
|
||||
if (typeof uaCH[FORMFACTORS] !== 'string') {
|
||||
var idx = 0;
|
||||
while (!ff && idx < uaCH[FORMFACTORS].length) {
|
||||
ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap);
|
||||
}
|
||||
} else {
|
||||
ff = strMapper(uaCH[FORMFACTORS], formFactorsMap);
|
||||
}
|
||||
this.set(TYPE, ff);
|
||||
}
|
||||
break;
|
||||
case OS:
|
||||
var osName = uaCH[PLATFORM];
|
||||
if(osName) {
|
||||
var osVersion = uaCH[PLATFORMVER];
|
||||
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
|
||||
this.set(NAME, osName)
|
||||
.set(VERSION, osVersion);
|
||||
}
|
||||
// Xbox-Specific Detection
|
||||
if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') {
|
||||
this.set(NAME, 'Xbox')
|
||||
.set(VERSION, undefined);
|
||||
}
|
||||
break;
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var parse = function (itemType) {
|
||||
return data[itemType]
|
||||
.getItem()
|
||||
.setCH(uaCH)
|
||||
.parseCH()
|
||||
.get();
|
||||
};
|
||||
this.set(BROWSER, parse(BROWSER))
|
||||
.set(CPU, parse(CPU))
|
||||
.set(DEVICE, parse(DEVICE))
|
||||
.set(ENGINE, parse(ENGINE))
|
||||
.set(OS, parse(OS));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
function UAParser (ua, extensions, headers) {
|
||||
|
||||
if (typeof ua === TYPEOF.OBJECT) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Source: /src/main/ua-parser.js
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/* UAParser.js v2.0.6
|
||||
/* UAParser.js v2.0.8
|
||||
Copyright © 2012-2025 Faisal Salman <f@faisalman.com>
|
||||
AGPLv3 License *//*
|
||||
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
|
||||
@@ -21,7 +21,7 @@
|
||||
// Constants
|
||||
/////////////
|
||||
|
||||
var LIBVERSION = '2.0.6',
|
||||
var LIBVERSION = '2.0.8',
|
||||
UA_MAX_LENGTH = 500,
|
||||
USER_AGENT = 'user-agent',
|
||||
EMPTY = '',
|
||||
@@ -377,9 +377,10 @@
|
||||
/(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet
|
||||
/(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer
|
||||
|
||||
// Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble/Palemoon
|
||||
/(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:lg |qute)browser|palemoon)\/([-\w\.]+)/i,
|
||||
// Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
|
||||
// Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble/Palemoon/HiBrowser
|
||||
/(atlas|flock|rockmelt|midori|epiphany|silk|skyfire|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:hi|lg |ovi|qute)browser|palemoon)\/v?([-\w\.]+)/i,
|
||||
// Atlas/Rekonq/Puffin/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon
|
||||
/(brave)(?: chrome)?\/([\d\.]+)/i, // Brave
|
||||
/(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
|
||||
@@ -406,7 +407,9 @@
|
||||
], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [
|
||||
/\bfocus\/([\w\.]+)/i // Firefox Focus
|
||||
], [VERSION, [NAME, FIREFOX+' Focus']], [
|
||||
/\bopt\/([\w\.]+)/i // Opera Touch
|
||||
/ mms\/([\w\.]+)$/i // Opera Neon
|
||||
], [VERSION, [NAME, OPERA+' Neon']], [
|
||||
/ opt\/([\w\.]+)$/i // Opera Touch
|
||||
], [VERSION, [NAME, OPERA+' Touch']], [
|
||||
/coc_coc\w+\/([\w\.]+)/i // Coc Coc Browser
|
||||
], [VERSION, [NAME, 'Coc Coc']], [
|
||||
@@ -434,7 +437,7 @@
|
||||
/(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla
|
||||
/m?(qqbrowser|2345(?=browser|chrome|explorer))\w*[\/ ]?v?([\w\.]+)/i // QQ/2345
|
||||
], [NAME, VERSION], [
|
||||
/(lbbrowser|rekonq)/i // LieBao Browser/Rekonq
|
||||
/(lbbrowser|rekonq|steam(?= (clie|tenf|gameo)))/i // LieBao Browser/Rekonq/Steam
|
||||
], [NAME], [
|
||||
/ome\/([\w\.]+) \w* ?(iron) saf/i, // Iron
|
||||
/ome\/([\w\.]+).+qihu (360)[es]e/i // 360
|
||||
@@ -455,7 +458,7 @@
|
||||
], [NAME, VERSION, [TYPE, INAPP]], [
|
||||
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
|
||||
], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [
|
||||
/musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok
|
||||
/(?:musical_ly|trill)(?:.+app_?version\/|_)([\w\.]+)/i // TikTok
|
||||
], [VERSION, [NAME, 'TikTok'], [TYPE, INAPP]], [
|
||||
/\[(linkedin)app\]/i // LinkedIn App for iOS & Android
|
||||
], [NAME, [TYPE, INAPP]], [
|
||||
@@ -600,17 +603,17 @@
|
||||
/\b(?:xiao)?((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
|
||||
],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
|
||||
|
||||
/\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
|
||||
/\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
|
||||
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
|
||||
/\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
|
||||
/oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models
|
||||
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i, // Xiaomi Mi
|
||||
// Xiaomi Redmi / POCO / Black Shark / Qin
|
||||
/oid[^\)]+; (redmi[\-_ ]?(?:note|k)?[\w_ ]+|m?[12]\d[01]\d\w{3,6}|poco[\w ]+|(shark )?\w{3}-[ah]0|qin ?[1-3](s\+|ultra| pro)?)( bui|; wv|\))/i,
|
||||
// Xiaomi Mi
|
||||
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i,
|
||||
/ ([\w ]+) miui\/v?\d/i
|
||||
], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
|
||||
|
||||
// OnePlus
|
||||
/droid.+; (cph2[3-6]\d[13579]|((gm|hd)19|(ac|be|in|kb)20|(d[en]|eb|le|mt)21|ne22)[0-2]\d|p[g-k]\w[1m]10)\b/i,
|
||||
/droid.+; (cph2[3-6]\d[13579]|((gm|hd)19|(ac|be|in|kb)20|(d[en]|eb|le|mt)21|ne22)[0-2]\d|p[g-l]\w[1m]10)\b/i,
|
||||
/(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i
|
||||
], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [
|
||||
|
||||
@@ -650,7 +653,7 @@
|
||||
], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [
|
||||
|
||||
// LG
|
||||
/((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
|
||||
/\b(?:lg)?([vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
|
||||
], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [
|
||||
/(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i,
|
||||
/\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch|webos))(\w+)/i,
|
||||
@@ -854,10 +857,10 @@
|
||||
], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [
|
||||
/(ouya)/i, // Ouya
|
||||
/(nintendo) (\w+)/i, // Nintendo
|
||||
/(retroid) (pocket ([^\)]+))/i // Retroid Pocket
|
||||
], [VENDOR, MODEL, [TYPE, CONSOLE]], [
|
||||
/droid.+; (shield)( bui|\))/i // Nvidia Portable
|
||||
], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [
|
||||
/(retroid) (pocket ([^\)]+))/i, // Retroid Pocket
|
||||
/(valve).+(steam deck)/i,
|
||||
/droid.+; ((shield|rgcube|gr0006))( bui|\))/i // Nvidia Portable/Anbernic/Logitech
|
||||
], [[VENDOR, strMapper, { 'Nvidia': 'Shield', 'Anbernic': 'RGCUBE', 'Logitech': 'GR0006' }], MODEL, [TYPE, CONSOLE]], [
|
||||
|
||||
///////////////////
|
||||
// WEARABLES
|
||||
@@ -866,7 +869,7 @@
|
||||
/\b(sm-[lr]\d\d[0156][fnuw]?s?|gear live)\b/i // Samsung Galaxy Watch
|
||||
], [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]], [
|
||||
/((pebble))app/i, // Pebble
|
||||
/(asus|google|lg|oppo) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch
|
||||
/(asus|google|lg|oppo|xiaomi) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch / Xiaomi Watch
|
||||
], [VENDOR, MODEL, [TYPE, WEARABLE]], [
|
||||
/(ow(?:19|20)?we?[1-3]{1,3})/i // Oppo Watch
|
||||
], [MODEL, [VENDOR, OPPO], [TYPE, WEARABLE]], [
|
||||
@@ -941,7 +944,7 @@
|
||||
/(presto)\/([\w\.]+)/i, // Presto
|
||||
/(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna|servo)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna/Servo
|
||||
/ekioh(flow)\/([\w\.]+)/i, // Flow
|
||||
/(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links
|
||||
/(khtml|tasman|links|dillo)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links/Dillo
|
||||
/(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab
|
||||
|
||||
/\b(libweb)/i // LibWeb
|
||||
@@ -970,7 +973,8 @@
|
||||
|
||||
// iOS/macOS
|
||||
/[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
|
||||
/(?:ios;fbsv|ios(?=.+ip(?:ad|hone))|ip(?:ad|hone)(?: |.+i(?:pad)?)os)[\/ ]([\w\.]+)/i,
|
||||
/(?:ios;fbsv|ios(?=.+ip(?:ad|hone)|.+apple ?tv)|ip(?:ad|hone)(?: |.+i(?:pad)?)os|apple ?tv.+ios)[\/ ]([\w\.]+)/i,
|
||||
/\btvos ?([\w\.]+)/i,
|
||||
/cfnetwork\/.+darwin/i
|
||||
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
|
||||
/(mac os x) ?([\w\. ]*)/i,
|
||||
@@ -1002,7 +1006,7 @@
|
||||
], [VERSION, [NAME, BLACKBERRY]], [
|
||||
/(?:symbian ?os|symbos|s60(?=;)|series ?60)[-\/ ]?([\w\.]*)/i // Symbian
|
||||
], [VERSION, [NAME, 'Symbian']], [
|
||||
/mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS
|
||||
/mozilla\/[\d\.]+ \((?:mobile[;\w ]*|tablet|tv|[^\)]*(?:viera|lg(?:l25|-d300)|alcatel ?o.+|y300-f1)); rv:([\w\.]+)\).+gecko\//i // Firefox OS
|
||||
], [VERSION, [NAME, FIREFOX+' OS']], [
|
||||
/\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS
|
||||
/webos(?:[ \/]?|\.tv-20(?=2[2-9]))(\d[\d\.]*)/i
|
||||
@@ -1018,7 +1022,6 @@
|
||||
], [[NAME, "Chrome OS"], VERSION],[
|
||||
|
||||
// Smart TVs
|
||||
/panasonic;(viera)/i, // Panasonic Viera
|
||||
/(netrange)mmh/i, // Netrange
|
||||
/(nettv)\/(\d+\.[\w\.]+)/i, // NetTV
|
||||
|
||||
@@ -1144,25 +1147,25 @@
|
||||
};
|
||||
}
|
||||
|
||||
if (!NAVIGATOR_UADATA) {
|
||||
IData.prototype.then = function (cb) {
|
||||
var that = this;
|
||||
var IDataResolve = function () {
|
||||
for (var prop in that) {
|
||||
if (that.hasOwnProperty(prop)) {
|
||||
this[prop] = that[prop];
|
||||
}
|
||||
IData.prototype.then = function (cb) {
|
||||
var that = this;
|
||||
var IDataResolve = function () {
|
||||
for (var prop in that) {
|
||||
if (that.hasOwnProperty(prop)) {
|
||||
this[prop] = that[prop];
|
||||
}
|
||||
};
|
||||
IDataResolve.prototype = {
|
||||
is : IData.prototype.is,
|
||||
toString : IData.prototype.toString
|
||||
};
|
||||
var resolveData = new IDataResolve();
|
||||
cb(resolveData);
|
||||
return resolveData;
|
||||
}
|
||||
};
|
||||
}
|
||||
IDataResolve.prototype = {
|
||||
is : IData.prototype.is,
|
||||
toString : IData.prototype.toString,
|
||||
withClientHints : IData.prototype.withClientHints,
|
||||
withFeatureCheck : IData.prototype.withFeatureCheck
|
||||
};
|
||||
var resolveData = new IDataResolve();
|
||||
cb(resolveData);
|
||||
return resolveData;
|
||||
};
|
||||
|
||||
return new IData();
|
||||
};
|
||||
@@ -1194,189 +1197,6 @@
|
||||
}
|
||||
|
||||
function UAItem (itemType, ua, rgxMap, uaCH) {
|
||||
|
||||
this.get = function (prop) {
|
||||
if (!prop) return this.data;
|
||||
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
|
||||
};
|
||||
|
||||
this.set = function (prop, val) {
|
||||
this.data[prop] = val;
|
||||
return this;
|
||||
};
|
||||
|
||||
this.setCH = function (ch) {
|
||||
this.uaCH = ch;
|
||||
return this;
|
||||
};
|
||||
|
||||
this.detectFeature = function () {
|
||||
if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
// Brave-specific detection
|
||||
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
|
||||
this.set(NAME, 'Brave');
|
||||
}
|
||||
break;
|
||||
case DEVICE:
|
||||
// Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
|
||||
if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
|
||||
if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
|
||||
this.set(MODEL, 'iPad')
|
||||
.set(TYPE, TABLET);
|
||||
}
|
||||
break;
|
||||
case OS:
|
||||
// Chrome-specific detection: check for 'platform' value of navigator.userAgentData
|
||||
if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
|
||||
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
|
||||
}
|
||||
break;
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var detect = function (itemType) {
|
||||
return data[itemType]
|
||||
.getItem()
|
||||
.detectFeature()
|
||||
.get();
|
||||
};
|
||||
this.set(BROWSER, detect(BROWSER))
|
||||
.set(CPU, detect(CPU))
|
||||
.set(DEVICE, detect(DEVICE))
|
||||
.set(ENGINE, detect(ENGINE))
|
||||
.set(OS, detect(OS));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
this.parseUA = function () {
|
||||
if (this.itemType != RESULT) {
|
||||
rgxMapper.call(this.data, this.ua, this.rgxMap);
|
||||
}
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
this.set(MAJOR, majorize(this.get(VERSION)));
|
||||
break;
|
||||
case OS:
|
||||
if (this.get(NAME) == 'iOS' && this.get(VERSION) == '18.6') {
|
||||
// Based on the assumption that iOS version is tightly coupled with Safari version
|
||||
var realVersion = /\) Version\/([\d\.]+)/.exec(this.ua); // Get Safari version
|
||||
if (realVersion && parseInt(realVersion[1].substring(0,2), 10) >= 26) {
|
||||
this.set(VERSION, realVersion[1]); // Set as iOS version
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
this.parseCH = function () {
|
||||
var uaCH = this.uaCH,
|
||||
rgxMap = this.rgxMap;
|
||||
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
case ENGINE:
|
||||
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
|
||||
if (brands) {
|
||||
for (var i=0; i<brands.length; i++) {
|
||||
var brandName = brands[i].brand || brands[i],
|
||||
brandVersion = brands[i].version;
|
||||
if (this.itemType == BROWSER &&
|
||||
!/not.a.brand/i.test(brandName) &&
|
||||
(!prevName ||
|
||||
(/Chrom/.test(prevName) && brandName != CHROMIUM) ||
|
||||
(prevName == EDGE && /WebView2/.test(brandName))
|
||||
)) {
|
||||
brandName = strMapper(brandName, browserHintsMap);
|
||||
prevName = this.get(NAME);
|
||||
if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) {
|
||||
this.set(NAME, brandName)
|
||||
.set(VERSION, brandVersion)
|
||||
.set(MAJOR, majorize(brandVersion));
|
||||
}
|
||||
prevName = brandName;
|
||||
}
|
||||
if (this.itemType == ENGINE && brandName == CHROMIUM) {
|
||||
this.set(VERSION, brandVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPU:
|
||||
var archName = uaCH[ARCHITECTURE];
|
||||
if (archName) {
|
||||
if (archName && uaCH[BITNESS] == '64') archName += '64';
|
||||
rgxMapper.call(this.data, archName + ';', rgxMap);
|
||||
}
|
||||
break;
|
||||
case DEVICE:
|
||||
if (uaCH[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
if (uaCH[MODEL]) {
|
||||
this.set(MODEL, uaCH[MODEL]);
|
||||
if (!this.get(TYPE) || !this.get(VENDOR)) {
|
||||
var reParse = {};
|
||||
rgxMapper.call(reParse, 'droid 9; ' + uaCH[MODEL] + ')', rgxMap);
|
||||
if (!this.get(TYPE) && !!reParse.type) {
|
||||
this.set(TYPE, reParse.type);
|
||||
}
|
||||
if (!this.get(VENDOR) && !!reParse.vendor) {
|
||||
this.set(VENDOR, reParse.vendor);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uaCH[FORMFACTORS]) {
|
||||
var ff;
|
||||
if (typeof uaCH[FORMFACTORS] !== 'string') {
|
||||
var idx = 0;
|
||||
while (!ff && idx < uaCH[FORMFACTORS].length) {
|
||||
ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap);
|
||||
}
|
||||
} else {
|
||||
ff = strMapper(uaCH[FORMFACTORS], formFactorsMap);
|
||||
}
|
||||
this.set(TYPE, ff);
|
||||
}
|
||||
break;
|
||||
case OS:
|
||||
var osName = uaCH[PLATFORM];
|
||||
if(osName) {
|
||||
var osVersion = uaCH[PLATFORMVER];
|
||||
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
|
||||
this.set(NAME, osName)
|
||||
.set(VERSION, osVersion);
|
||||
}
|
||||
// Xbox-Specific Detection
|
||||
if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') {
|
||||
this.set(NAME, 'Xbox')
|
||||
.set(VERSION, undefined);
|
||||
}
|
||||
break;
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var parse = function (itemType) {
|
||||
return data[itemType]
|
||||
.getItem()
|
||||
.setCH(uaCH)
|
||||
.parseCH()
|
||||
.get();
|
||||
};
|
||||
this.set(BROWSER, parse(BROWSER))
|
||||
.set(CPU, parse(CPU))
|
||||
.set(DEVICE, parse(DEVICE))
|
||||
.set(ENGINE, parse(ENGINE))
|
||||
.set(OS, parse(OS));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
setProps.call(this, [
|
||||
['itemType', itemType],
|
||||
['ua', ua],
|
||||
@@ -1384,10 +1204,191 @@
|
||||
['rgxMap', rgxMap],
|
||||
['data', createIData(this, itemType)]
|
||||
]);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
UAItem.prototype.get = function (prop) {
|
||||
if (!prop) return this.data;
|
||||
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
|
||||
};
|
||||
|
||||
UAItem.prototype.set = function (prop, val) {
|
||||
this.data[prop] = val;
|
||||
return this;
|
||||
};
|
||||
|
||||
UAItem.prototype.setCH = function (ch) {
|
||||
this.uaCH = ch;
|
||||
return this;
|
||||
};
|
||||
|
||||
UAItem.prototype.detectFeature = function () {
|
||||
if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
// Brave-specific detection
|
||||
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
|
||||
this.set(NAME, 'Brave');
|
||||
}
|
||||
break;
|
||||
case DEVICE:
|
||||
// Chrome-specific detection: check for 'mobile' value of navigator.userAgentData
|
||||
if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
|
||||
if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
|
||||
this.set(MODEL, 'iPad')
|
||||
.set(TYPE, TABLET);
|
||||
}
|
||||
break;
|
||||
case OS:
|
||||
// Chrome-specific detection: check for 'platform' value of navigator.userAgentData
|
||||
if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
|
||||
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
|
||||
}
|
||||
break;
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var detect = function (itemType) {
|
||||
return data[itemType]
|
||||
.getItem()
|
||||
.detectFeature()
|
||||
.get();
|
||||
};
|
||||
this.set(BROWSER, detect(BROWSER))
|
||||
.set(CPU, detect(CPU))
|
||||
.set(DEVICE, detect(DEVICE))
|
||||
.set(ENGINE, detect(ENGINE))
|
||||
.set(OS, detect(OS));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
UAItem.prototype.parseUA = function () {
|
||||
if (this.itemType != RESULT) {
|
||||
rgxMapper.call(this.data, this.ua, this.rgxMap);
|
||||
}
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
this.set(MAJOR, majorize(this.get(VERSION)));
|
||||
break;
|
||||
case OS:
|
||||
if (this.get(NAME) == 'iOS' && this.get(VERSION) == '18.6') {
|
||||
// Based on the assumption that iOS version is tightly coupled with Safari version
|
||||
var realVersion = /\) Version\/([\d\.]+)/.exec(this.ua); // Get Safari version
|
||||
if (realVersion && parseInt(realVersion[1].substring(0,2), 10) >= 26) {
|
||||
this.set(VERSION, realVersion[1]); // Set as iOS version
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
UAItem.prototype.parseCH = function () {
|
||||
var uaCH = this.uaCH,
|
||||
rgxMap = this.rgxMap;
|
||||
|
||||
switch (this.itemType) {
|
||||
case BROWSER:
|
||||
case ENGINE:
|
||||
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName;
|
||||
if (brands) {
|
||||
for (var i=0; i<brands.length; i++) {
|
||||
var brandName = brands[i].brand || brands[i],
|
||||
brandVersion = brands[i].version;
|
||||
if (this.itemType == BROWSER &&
|
||||
!/not.a.brand/i.test(brandName) &&
|
||||
(!prevName ||
|
||||
(/Chrom/.test(prevName) && brandName != CHROMIUM) ||
|
||||
(prevName == EDGE && /WebView2/.test(brandName))
|
||||
)) {
|
||||
brandName = strMapper(brandName, browserHintsMap);
|
||||
prevName = this.get(NAME);
|
||||
if (!(prevName && !/Chrom/.test(prevName) && /Chrom/.test(brandName))) {
|
||||
this.set(NAME, brandName)
|
||||
.set(VERSION, brandVersion)
|
||||
.set(MAJOR, majorize(brandVersion));
|
||||
}
|
||||
prevName = brandName;
|
||||
}
|
||||
if (this.itemType == ENGINE && brandName == CHROMIUM) {
|
||||
this.set(VERSION, brandVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPU:
|
||||
var archName = uaCH[ARCHITECTURE];
|
||||
if (archName) {
|
||||
if (archName && uaCH[BITNESS] == '64') archName += '64';
|
||||
rgxMapper.call(this.data, archName + ';', rgxMap);
|
||||
}
|
||||
break;
|
||||
case DEVICE:
|
||||
if (uaCH[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
if (uaCH[MODEL]) {
|
||||
this.set(MODEL, uaCH[MODEL]);
|
||||
if (!this.get(TYPE) || !this.get(VENDOR)) {
|
||||
var reParse = {};
|
||||
rgxMapper.call(reParse, 'droid 9; ' + uaCH[MODEL] + ')', rgxMap);
|
||||
if (!this.get(TYPE) && !!reParse.type) {
|
||||
this.set(TYPE, reParse.type);
|
||||
}
|
||||
if (!this.get(VENDOR) && !!reParse.vendor) {
|
||||
this.set(VENDOR, reParse.vendor);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (uaCH[FORMFACTORS]) {
|
||||
var ff;
|
||||
if (typeof uaCH[FORMFACTORS] !== 'string') {
|
||||
var idx = 0;
|
||||
while (!ff && idx < uaCH[FORMFACTORS].length) {
|
||||
ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap);
|
||||
}
|
||||
} else {
|
||||
ff = strMapper(uaCH[FORMFACTORS], formFactorsMap);
|
||||
}
|
||||
this.set(TYPE, ff);
|
||||
}
|
||||
break;
|
||||
case OS:
|
||||
var osName = uaCH[PLATFORM];
|
||||
if(osName) {
|
||||
var osVersion = uaCH[PLATFORMVER];
|
||||
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
|
||||
this.set(NAME, osName)
|
||||
.set(VERSION, osVersion);
|
||||
}
|
||||
// Xbox-Specific Detection
|
||||
if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') {
|
||||
this.set(NAME, 'Xbox')
|
||||
.set(VERSION, undefined);
|
||||
}
|
||||
break;
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var parse = function (itemType) {
|
||||
return data[itemType]
|
||||
.getItem()
|
||||
.setCH(uaCH)
|
||||
.parseCH()
|
||||
.get();
|
||||
};
|
||||
this.set(BROWSER, parse(BROWSER))
|
||||
.set(CPU, parse(CPU))
|
||||
.set(DEVICE, parse(DEVICE))
|
||||
.set(ENGINE, parse(ENGINE))
|
||||
.set(OS, parse(OS));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
function UAParser (ua, extensions, headers) {
|
||||
|
||||
if (typeof ua === TYPEOF.OBJECT) {
|
||||
|
||||
@@ -171,6 +171,16 @@
|
||||
"major" : "0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Atlas",
|
||||
"ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Atlas/1.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Atlas",
|
||||
"version" : "1.0",
|
||||
"major" : "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Avant",
|
||||
"ua" : "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB5; Avant Browser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
|
||||
@@ -856,6 +866,16 @@
|
||||
"major" : "40"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Hi Browser",
|
||||
"ua" : "Mozilla/5.0 (Linux; Android 14; en; TECNO BG6m Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 HiBrowser/v2.25.6.3;lang=es;nation=DO;locale=es_DO UWS/ Mobile Safari/537.36",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "HiBrowser",
|
||||
"version" : "2.25.6.3",
|
||||
"major" : "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "HuaweiBrowser",
|
||||
"ua" : "Mozilla/5.0 (Linux; Android 6.0.1; LYA-AL00;HMSCore/4.0.0 GMS/10.4 ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.64 HuaweiBrowser/10.0.3.102 Mobile Safari/537.36",
|
||||
@@ -1598,6 +1618,16 @@
|
||||
"major" : "60"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Opera Neon",
|
||||
"ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.21 Safari/537.36 MMS/1.0.2531.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Opera Neon",
|
||||
"version" : "1.0.2531.0",
|
||||
"major" : "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Opera Tablet",
|
||||
"ua" : "Opera/9.80 (Windows NT 6.1; Opera Tablet/15165; U; en) Presto/2.8.149 Version/11.1",
|
||||
@@ -1949,6 +1979,36 @@
|
||||
"major" : "20"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Steam Client",
|
||||
"ua" : "Mozilla/5.0 (X11; Linux x86_64; Valve Steam Client/default/1705108172) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Steam",
|
||||
"version" : "undefined",
|
||||
"major" : "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Steam Big Picture",
|
||||
"ua" : "Mozilla/5.0 (Linux; U; X11; en-US; Valve Steam Tenfoot/1660688177; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Steam",
|
||||
"version" : "undefined",
|
||||
"major" : "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Steam Overlay",
|
||||
"ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Valve Steam GameOverlay/default/1741737356) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.183 Safari/537.36",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Steam",
|
||||
"version" : "undefined",
|
||||
"major" : "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Swiftfox",
|
||||
"ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0 (Swiftfox)",
|
||||
@@ -2458,6 +2518,26 @@
|
||||
"major" : "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Brave",
|
||||
"ua" : "Mozilla/5.0 (Linux; Android 11; SM-T870) AppleWebKit/537.36 (KHTML, like Gecko) Brave Chrome/88.0.4324.96 Safari/537.36",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Brave",
|
||||
"version" : "88.0.4324.96",
|
||||
"major" : "88"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Brave",
|
||||
"ua" : "Mozilla/5.0 (Linux; Android 11; Samsung Galaxy F62 SM-E625F Build/SME625F512091028;) AppleWebKit/537.36 (KHTML, like Gecko) Brave/1.75.110 Mobile Safari/537.36",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Brave",
|
||||
"version" : "1.75.110",
|
||||
"major" : "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Brave Browser",
|
||||
"ua" : "Brave/4.5.16 CFNetwork/893.13.1 Darwin/17.3.0 (x86_64)",
|
||||
@@ -2702,6 +2782,16 @@
|
||||
"major" : "28",
|
||||
"type" : "inapp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "TikTok",
|
||||
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_6_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Safari/604.1 trill_43.0.0 BytedanceWebview/d8a21c6",
|
||||
"expect" : {
|
||||
"name" : "TikTok",
|
||||
"version": "43.0.0",
|
||||
"major" : "43",
|
||||
"type" : "inapp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Chrome Mobile",
|
||||
|
||||
11
test/data/ua/device/anbernic.json
Normal file
11
test/data/ua/device/anbernic.json
Normal file
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"desc": "Anbernic",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 13; RGCUBE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Anbernic",
|
||||
"model": "RGCUBE",
|
||||
"type": "console"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -188,6 +188,15 @@
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "LG G Pad F2",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 7.1.1; LG-LK460 Build/NMF26X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "LG",
|
||||
"model": "LK460",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "LG Watch Urbane",
|
||||
"ua": "Mozilla/5.0 Linux; Android 7.1.1; LG Watch Urbane Build/NWD1.180306.004 AppleWebKit/537.36 KHTML, like Gecko Chrome/19.77.34.5 Mobile Safari/537.36",
|
||||
|
||||
11
test/data/ua/device/logitech.json
Normal file
11
test/data/ua/device/logitech.json
Normal file
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"desc": "Logitech",
|
||||
"ua": "Dalvik/2.1.0 (Linux; U; Android 11; GR0006 Build/SQ02K.00.0018)",
|
||||
"expect": {
|
||||
"vendor": "Logitech",
|
||||
"model": "GR0006",
|
||||
"type": "console"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -170,6 +170,15 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "OnePlus 15",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 16; PLK110 Build/BP2A.250605.015; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.135 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "OnePlus",
|
||||
"model": "PLK110",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "OnePlus Ace",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 15; zh-cn; PGKM10 Build/AP3A.240617.008) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.168 Mobile Safari/537.36 HeyTapBrowser/40.9.6.2",
|
||||
|
||||
11
test/data/ua/device/valve.json
Normal file
11
test/data/ua/device/valve.json
Normal file
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"desc": "Valve",
|
||||
"ua": "Mozilla/5.0 (X11; Linux x86_64; Valve Steam Client/Steam Deck [Steam Deck Beta]/default/0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.183 Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Valve",
|
||||
"model": "Steam Deck",
|
||||
"type": "console"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -71,6 +71,51 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "XiaoMi Black Shark 3",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 11; zh-CN; SHARK KLE-A0 Build/KLEN2211210CN00MR6) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/17.3.8.1369 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "SHARK KLE-A0",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "XiaoMi Black Shark 4",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 11; en-US; SHARK PRS-H0 Build/PROS2203060OS00MP5) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.4.0.1306 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "SHARK PRS-H0",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "XiaoMi Black Shark 4 Pro",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 11; es-us; SHARK KSR-H0 Build/KASE2208050OS00MP4) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.127 Mobile Safari/537.36 XiaoMi/MiuiBrowser/13.22.1-gn",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "SHARK KSR-H0",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "XiaoMi Black Shark 5",
|
||||
"ua": "Mozilla/5.0 (Linux; arm_64; Android 12; SHARK PAR-H0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 YaBrowser/23.5.0.256.00 SA/3 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "SHARK PAR-H0",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "XiaoMi Civi 5 Pro",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 15; zh-CN; 25067PYE3C Build/AQ3A.250129.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/18.0.4.1430 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "25067PYE3C",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Hongmi Note 1W",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-CN; HM NOTE 1W Build/JDQ39) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/9.7.9.439 U3/0.8.0 Mobile Safari/533.1",
|
||||
@@ -413,6 +458,42 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Qin 1s+",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 4.4.4; Qin 1s+ Build/KTU84P; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Mobile Safari/537.36 OPR/46.0.2254.145391",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "Qin 1s+",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Qin 2",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 9; Qin 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "Qin 2",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Qin 2 Pro",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 9.0; QIN2 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4215.0 Mobile Safari/537.36 EdgA/86.0.597.0",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "QIN2 Pro",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Qin 3 Ultra",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 12; QIN3ULTRA Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/128.0.6613.120 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "QIN3ULTRA",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Redmi 4A",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 6.0; Redmi 4A Build/MMB29M; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36",
|
||||
@@ -431,6 +512,15 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Redmi 15",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 15; 25062RN2DA Build/AQ3A.250226.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/142.0.7444.212 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/541.0.0.49.79;IABMV/1;]",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "25062RN2DA",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Redmi K30 5G",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 10; Redmi K30 5G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36",
|
||||
@@ -601,5 +691,14 @@
|
||||
"model": "MIBOX3",
|
||||
"type": "smarttv"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Watch 2 Pro",
|
||||
"ua": "Dalvik/2.1.0 (Linux; U; Android 13; Xiaomi Watch 2 Pro Build/TWR7.231031.001.XM064S)",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "Watch 2 Pro",
|
||||
"type": "wearable"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -17,6 +17,15 @@
|
||||
"version" : "57.0.2987.146"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Dillo",
|
||||
"ua" : "Dillo/3.1.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Dillo",
|
||||
"version" : "3.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "EdgeHTML",
|
||||
"ua" : "Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36 Edge/12.0",
|
||||
|
||||
@@ -59,6 +59,26 @@
|
||||
"type" : "cli"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "PowerShell",
|
||||
"ua" : "Mozilla/5.0 (Windows NT 10.0; Microsoft Windows 10.0.15063; en-US) PowerShell/6.0.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "PowerShell",
|
||||
"version" : "6.0.0",
|
||||
"type" : "cli"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "PowerShell",
|
||||
"ua" : "Mozilla/5.0 (Windows NT; Windows NT 10.0; de-DE) WindowsPowerShell/5.1.19041.5737",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "PowerShell",
|
||||
"version" : "5.1.19041.5737",
|
||||
"type" : "cli"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "wget",
|
||||
"ua" : "Wget/1.21.1",
|
||||
|
||||
@@ -1,232 +1,587 @@
|
||||
[
|
||||
{
|
||||
"desc" : "Airmail",
|
||||
"ua" : "Airmail 1.0 rv:148 (Macintosh; Mac OS X 10.8.3; en_BE)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Airmail",
|
||||
"version" : "1.0",
|
||||
"type" : "email"
|
||||
"desc": "Airmail",
|
||||
"ua": "Airmail 1.0 rv:148 (Macintosh; Mac OS X 10.8.3; en_BE)",
|
||||
"expect": {
|
||||
"name": "Airmail",
|
||||
"version": "1.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Apple Mail",
|
||||
"ua" : "Mail/3826.500.181.1.5 CFNetwork/3826.500.111.1.1 Darwin/24.4.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Mail",
|
||||
"version" : "3826.500.181.1.5",
|
||||
"type" : "email"
|
||||
"desc": "Alpine",
|
||||
"ua": "Alpine/2.21 (Linux; x86_64)",
|
||||
"expect": {
|
||||
"name": "Alpine",
|
||||
"version": "2.21",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "BlueMail",
|
||||
"ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) BlueMail/0.10.31 Chrome/61.0.3163.100 Electron/2.0.18 Safari/537.36",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "BlueMail",
|
||||
"version" : "0.10.31",
|
||||
"type" : "email"
|
||||
"desc": "Android Mail (AOSP)",
|
||||
"ua": "Android/9-email",
|
||||
"expect": {
|
||||
"name": "Android",
|
||||
"version": "9-email",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "BlueMail",
|
||||
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16A405 BlueMail iOS",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "BlueMail",
|
||||
"version" : "iOS",
|
||||
"type" : "email"
|
||||
"desc": "Apple Mail",
|
||||
"ua": "Mail/3826.500.181.1.5 CFNetwork/3826.500.111.1.1 Darwin/24.4.0",
|
||||
"expect": {
|
||||
"name": "Mail",
|
||||
"version": "3826.500.181.1.5",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "DaumMail",
|
||||
"ua" : "DaumMobileApp (LG-KU5400; U; Android 2.3.7|10; ko-kr) DaumMail/1.0.8 ",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "DaumMail",
|
||||
"version" : "1.0.8",
|
||||
"type" : "email"
|
||||
"desc": "Aqua Mail",
|
||||
"ua": "AquaMail/1.16.0-1081 (build: 101600003)",
|
||||
"expect": {
|
||||
"name": "AquaMail",
|
||||
"version": "1.16.0-1081",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Evolution",
|
||||
"ua" : "Evolution/3.52.3",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Evolution",
|
||||
"version" : "3.52.3",
|
||||
"type" : "email"
|
||||
"desc": "Balsa",
|
||||
"ua": "Balsa/2.6.4",
|
||||
"expect": {
|
||||
"name": "Balsa",
|
||||
"version": "2.6.4",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "eM Client",
|
||||
"ua" : "eMClient/9.2.2157.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "eMClient",
|
||||
"version" : "9.2.2157.0",
|
||||
"type" : "email"
|
||||
"desc": "Barca",
|
||||
"ua": "Barca/2.8 (Windows NT 5.1)",
|
||||
"expect": {
|
||||
"name": "Barca",
|
||||
"version": "2.8",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Foxmail",
|
||||
"ua" : "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36 foxmail/7.2.15.80",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "foxmail",
|
||||
"version" : "7.2.15.80",
|
||||
"type" : "email"
|
||||
"desc": "BlueMail",
|
||||
"ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) BlueMail/0.10.31 Chrome/61.0.3163.100 Electron/2.0.18 Safari/537.36",
|
||||
"expect": {
|
||||
"name": "BlueMail",
|
||||
"version": "0.10.31",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "KMail",
|
||||
"ua" : "KMail/4.14.10 (FreeBSD/12.0-CURRENT; KDE/4.14.10; amd64; ; )",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "KMail",
|
||||
"version" : "4.14.10",
|
||||
"type" : "email"
|
||||
"desc": "BlueMail (iOS)",
|
||||
"ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16A405 BlueMail iOS",
|
||||
"expect": {
|
||||
"name": "BlueMail",
|
||||
"version": "iOS",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "KMail2",
|
||||
"ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) kmail2/5.7.3 Safari/534.34",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "kmail2",
|
||||
"version" : "5.7.3",
|
||||
"type" : "email"
|
||||
"desc": "Canary Mail",
|
||||
"ua": "Canary/413 (Macintosh; OS X 14.2.1)",
|
||||
"expect": {
|
||||
"name": "Canary",
|
||||
"version": "413",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Kontact",
|
||||
"ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) kontact/4.13.3 Safari/534.34",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "kontact",
|
||||
"version" : "4.13.3",
|
||||
"type" : "email"
|
||||
"desc": "Claws Mail",
|
||||
"ua": "Claws Mail/3.17.5 (x86_64-pc-linux-gnu)",
|
||||
"expect": {
|
||||
"name": "Claws Mail",
|
||||
"version": "3.17.5",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Microsoft Outlook",
|
||||
"ua" : "Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.5431; Pro)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Microsoft Outlook",
|
||||
"version" : "16.0.5431",
|
||||
"type" : "email"
|
||||
"desc": "DaumMail",
|
||||
"ua": "DaumMobileApp (LG-KU5400; U; Android 2.3.7|10; ko-kr) DaumMail/1.0.8 ",
|
||||
"expect": {
|
||||
"name": "DaumMail",
|
||||
"version": "1.0.8",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Microsoft Outlook for Mac",
|
||||
"ua" : "MacOutlook/14.7.1.161129 (Intel Mac OS X 10.9.6)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "MacOutlook",
|
||||
"version" : "14.7.1.161129",
|
||||
"type" : "email"
|
||||
"desc": "eM Client",
|
||||
"ua": "eMClient/9.2.2157.0",
|
||||
"expect": {
|
||||
"name": "eMClient",
|
||||
"version": "9.2.2157.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "NaverMailApp",
|
||||
"ua" : "NaverMailApp/2.1.23 (Android 10; SM-N960N)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "NaverMailApp",
|
||||
"version" : "2.1.23",
|
||||
"type" : "email"
|
||||
"desc": "Eudora OSE",
|
||||
"ua": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.19) Gecko/20110420 Eudora/3.0.4",
|
||||
"expect": {
|
||||
"name": "Eudora",
|
||||
"version": "3.0.4",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Polymail",
|
||||
"ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Polymail/2.3.12 Chrome/114.0.5735.134 Electron/25.2.0 Safari/537.36",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Polymail",
|
||||
"version" : "2.3.12",
|
||||
"type" : "email"
|
||||
"desc": "Evolution",
|
||||
"ua": "Evolution/3.52.3",
|
||||
"expect": {
|
||||
"name": "Evolution",
|
||||
"version": "3.52.3",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "ProtonMail",
|
||||
"ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ProtonMail/1.4.0 Chrome/122.0.6261.156 Electron/29.4.6 Safari/537.36",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "ProtonMail",
|
||||
"version" : "1.4.0",
|
||||
"type" : "email"
|
||||
"desc": "FairEmail",
|
||||
"ua": "FairEmail/1.2066 (Android 13; Pixel 7)",
|
||||
"expect": {
|
||||
"name": "FairEmail",
|
||||
"version": "1.2066",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "SparkDesktop",
|
||||
"ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) SparkDesktop/3.0.30 Chrome/102.0.5005.63 Electron/19.0.4 Safari/537.36",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "SparkDesktop",
|
||||
"version" : "3.0.30",
|
||||
"type" : "email"
|
||||
"desc": "Foxmail",
|
||||
"ua": "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36 foxmail/7.2.15.80",
|
||||
"expect": {
|
||||
"name": "foxmail",
|
||||
"version": "7.2.15.80",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Sparrow",
|
||||
"ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5.6 (KHTML, like Gecko) Sparrow/1043.1",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Sparrow",
|
||||
"version" : "1043.1",
|
||||
"type" : "email"
|
||||
"desc": "Geary",
|
||||
"ua": "Geary/40.0 (Linux; x86_64)",
|
||||
"expect": {
|
||||
"name": "Geary",
|
||||
"version": "40.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Thunderbird",
|
||||
"ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Thunderbird",
|
||||
"version" : "78.13.0",
|
||||
"type" : "email"
|
||||
"desc": "Gnus",
|
||||
"ua": "Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux)",
|
||||
"expect": {
|
||||
"name": "Gnus",
|
||||
"version": "5.13",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Yahoo! Mail",
|
||||
"ua" : "YahooMobile/1.0 (mail; 3.0.5.1311380); (Linux; U; Android 4.0.3; htc_runnymede Build/ICE_CREAM_SANDWICH_MR1);",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Yahoo",
|
||||
"version" : "3.0.5.1311380",
|
||||
"type" : "email"
|
||||
"desc": "Horde IMP",
|
||||
"ua": "Horde::IMP/6.2.27 (Linux)",
|
||||
"expect": {
|
||||
"name": "Horde::IMP",
|
||||
"version": "6.2.27",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Yahoo! Mail",
|
||||
"ua" : "YahooMobileMail/1.0 (Android Mail; 1.3.10) (supersonic;HTC;PC36100;2.3.5/GRJ90) ",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Yahoo",
|
||||
"version" : "1.3.10",
|
||||
"type" : "email"
|
||||
"desc": "IBM Notes",
|
||||
"ua": "Lotus-Notes/8.5.3",
|
||||
"expect": {
|
||||
"name": "Lotus-Notes",
|
||||
"version": "8.5.3",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Zimbra",
|
||||
"ua" : "Mozilla/5.0 (Windows; U; Windows NT 6.2; it; rv:1.9.2.19pre) Gecko/20110902 Prism zdesktop/7.2.8",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Zimbra",
|
||||
"version" : "7.2.8",
|
||||
"type" : "email"
|
||||
"desc": "IncrediMail",
|
||||
"ua": "IncrediMail/6.29.5126",
|
||||
"expect": {
|
||||
"name": "IncrediMail",
|
||||
"version": "6.29.5126",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "K-9 Mail",
|
||||
"ua": "K-9 Mail/5.600",
|
||||
"expect": {
|
||||
"name": "K-9 Mail",
|
||||
"version": "5.600",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "ZohoMail",
|
||||
"ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ZohoMail-Desktop/1.6.3 Chrome/124.0.6367.207 Electron/30.0.6 Safari/537.36",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "ZohoMail-Desktop",
|
||||
"version" : "1.6.3",
|
||||
"type" : "email"
|
||||
"desc": "KMail",
|
||||
"ua": "KMail/4.14.10 (FreeBSD/12.0-CURRENT; KDE/4.14.10; amd64; ; )",
|
||||
"expect": {
|
||||
"name": "KMail",
|
||||
"version": "4.14.10",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "KMail2",
|
||||
"ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) kmail2/5.7.3 Safari/534.34",
|
||||
"expect": {
|
||||
"name": "kmail2",
|
||||
"version": "5.7.3",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Kontact",
|
||||
"ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) kontact/4.13.3 Safari/534.34",
|
||||
"expect": {
|
||||
"name": "kontact",
|
||||
"version": "4.13.3",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Mailbird",
|
||||
"ua": "Mailbird/2.9.0.0",
|
||||
"expect": {
|
||||
"name": "Mailbird",
|
||||
"version": "2.9.0.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "MailMate",
|
||||
"ua": "MailMate/5663 (Macintosh; Intel Mac OS X 10.15.7)",
|
||||
"expect": {
|
||||
"name": "MailMate",
|
||||
"version": "5663",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Mailspring",
|
||||
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Mailspring/1.7.8 Chrome/69.0.3497.128 Electron/4.2.12 Safari/537.36",
|
||||
"expect": {
|
||||
"name": "Mailspring",
|
||||
"version": "1.7.8",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Microsoft Outlook",
|
||||
"ua": "Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.5431; Pro)",
|
||||
"expect": {
|
||||
"name": "Microsoft Outlook",
|
||||
"version": "16.0.5431",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Microsoft Outlook for Mac",
|
||||
"ua": "MacOutlook/14.7.1.161129 (Intel Mac OS X 10.9.6)",
|
||||
"expect": {
|
||||
"name": "MacOutlook",
|
||||
"version": "14.7.1.161129",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Mutt",
|
||||
"ua": "Mutt/1.5.21 (2010-09-15)",
|
||||
"expect": {
|
||||
"name": "Mutt",
|
||||
"version": "1.5.21",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "NaverMailApp",
|
||||
"ua": "NaverMailApp/2.1.23 (Android 10; SM-N960N)",
|
||||
"expect": {
|
||||
"name": "NaverMailApp",
|
||||
"version": "2.1.23",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Newton Mail",
|
||||
"ua": "Newton/10.0.52 (Android 10; K)",
|
||||
"expect": {
|
||||
"name": "Newton",
|
||||
"version": "10.0.52",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Nine",
|
||||
"ua": "Nine/4.5.3a",
|
||||
"expect": {
|
||||
"name": "Nine",
|
||||
"version": "4.5.3a",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Nylas Mail",
|
||||
"ua": "NylasMail/2.0.32 (Macintosh; OS X 10.12.6)",
|
||||
"expect": {
|
||||
"name": "NylasMail",
|
||||
"version": "2.0.32",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Outlook Express",
|
||||
"ua": "Outlook-Express/6.0 (MSIE 6.0; Windows NT 5.1; SV1)",
|
||||
"expect": {
|
||||
"name": "Outlook-Express",
|
||||
"version": "6.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Pegasus Mail",
|
||||
"ua": "Pegasus Mail/4.70",
|
||||
"expect": {
|
||||
"name": "Pegasus Mail",
|
||||
"version": "4.70",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "PocoMail",
|
||||
"ua": "PocoMail 4.8 (4320) - Licensed Version",
|
||||
"expect": {
|
||||
"name": "PocoMail",
|
||||
"version": "4.8",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Polymail",
|
||||
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Polymail/2.3.12 Chrome/114.0.5735.134 Electron/25.2.0 Safari/537.36",
|
||||
"expect": {
|
||||
"name": "Polymail",
|
||||
"version": "2.3.12",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Postbox",
|
||||
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:60.0) Gecko/20100101 Postbox/7.0.26",
|
||||
"expect": {
|
||||
"name": "Postbox",
|
||||
"version": "7.0.26",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "ProtonMail",
|
||||
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ProtonMail/1.4.0 Chrome/122.0.6261.156 Electron/29.4.6 Safari/537.36",
|
||||
"expect": {
|
||||
"name": "ProtonMail",
|
||||
"version": "1.4.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "ProtonMail Bridge",
|
||||
"ua": "ProtonMail Bridge/3.8.2 (Macintosh; Intel Mac OS X 10_15_7)",
|
||||
"expect": {
|
||||
"name": "ProtonMail Bridge",
|
||||
"version": "3.8.2",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "R2Mail2",
|
||||
"ua": "R2Mail2/2.50 (Android)",
|
||||
"expect": {
|
||||
"name": "R2Mail2",
|
||||
"version": "2.50",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "RainLoop",
|
||||
"ua": "RainLoop/1.17.0",
|
||||
"expect": {
|
||||
"name": "RainLoop",
|
||||
"version": "1.17.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Roundcube Webmail",
|
||||
"ua": "Roundcube Webmail/1.6.0",
|
||||
"expect": {
|
||||
"name": "Roundcube Webmail",
|
||||
"version": "1.6.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Samsung Email",
|
||||
"ua": "SamsungEmail/6.1.80.12",
|
||||
"expect": {
|
||||
"name": "SamsungEmail",
|
||||
"version": "6.1.80.12",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "SparkDesktop",
|
||||
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) SparkDesktop/3.0.30 Chrome/102.0.5005.63 Electron/19.0.4 Safari/537.36",
|
||||
"expect": {
|
||||
"name": "SparkDesktop",
|
||||
"version": "3.0.30",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Sparrow",
|
||||
"ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5.6 (KHTML, like Gecko) Sparrow/1043.1",
|
||||
"expect": {
|
||||
"name": "Sparrow",
|
||||
"version": "1043.1",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Spicebird",
|
||||
"ua": "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.9pre) Gecko/20100216 Spicebird/0.8",
|
||||
"expect": {
|
||||
"name": "Spicebird",
|
||||
"version": "0.8",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "SquirrelMail",
|
||||
"ua": "SquirrelMail/1.4.23 [SVN]",
|
||||
"expect": {
|
||||
"name": "SquirrelMail",
|
||||
"version": "1.4.23",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Sylpheed",
|
||||
"ua": "Sylpheed/3.7.0 (MinGW32; Windows NT 6.1; x86_64)",
|
||||
"expect": {
|
||||
"name": "Sylpheed",
|
||||
"version": "3.7.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "The Bat!",
|
||||
"ua": "The Bat! 9.2.1",
|
||||
"expect": {
|
||||
"name": "The Bat!",
|
||||
"version": "9.2.1",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Thunderbird",
|
||||
"ua": "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0",
|
||||
"expect": {
|
||||
"name": "Thunderbird",
|
||||
"version": "78.13.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Trojita",
|
||||
"ua": "Trojita/0.7",
|
||||
"expect": {
|
||||
"name": "Trojita",
|
||||
"version": "0.7",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Turnpike",
|
||||
"ua": "Turnpike/6.07-U",
|
||||
"expect": {
|
||||
"name": "Turnpike",
|
||||
"version": "6.07-U",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Tuta Desktop (Tutanota)",
|
||||
"ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) tutanota-desktop/3.110.1 Chrome/110.0.5481.179 Electron/23.1.3 Safari/537.36",
|
||||
"expect": {
|
||||
"name": "tutanota-desktop",
|
||||
"version": "3.110.1",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Wanderlust",
|
||||
"ua": "Wanderlust/2.15.9 (Almost Unreal) SEMI/1.14.6 (Maruoka) FLIM/1.14.9 (Gojō) APEL/10.8 Emacs/23.1 (i486-pc-linux-gnu) MULE/6.0 (HANACHIRUSATO)",
|
||||
"expect": {
|
||||
"name": "Wanderlust",
|
||||
"version": "2.15.9",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Windows Live Mail",
|
||||
"ua": "Windows-Live-Mail/15.4.3508.1109",
|
||||
"expect": {
|
||||
"name": "Windows-Live-Mail",
|
||||
"version": "15.4.3508.1109",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Yahoo! Mail (Android)",
|
||||
"ua": "YahooMobile/1.0 (mail; 3.0.5.1311380); (Linux; U; Android 4.0.3; htc_runnymede Build/ICE_CREAM_SANDWICH_MR1);",
|
||||
"expect": {
|
||||
"name": "Yahoo Mail",
|
||||
"version": "1.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Yahoo! Mail (Legacy)",
|
||||
"ua": "YahooMobileMail/1.0 (Android Mail; 1.3.10) (supersonic;HTC;PC36100;2.3.5/GRJ90) ",
|
||||
"expect": {
|
||||
"name": "Yahoo Mail",
|
||||
"version": "1.3.10",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Yahoo! Mail (iOS)",
|
||||
"ua": "YahooMail/1.0 (iOS; 1.0.0; en_US)",
|
||||
"expect": {
|
||||
"name": "Yahoo Mail",
|
||||
"version": "1.0",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Zimbra",
|
||||
"ua": "Mozilla/5.0 (Windows; U; Windows NT 6.2; it; rv:1.9.2.19pre) Gecko/20110902 Prism zdesktop/7.2.8",
|
||||
"expect": {
|
||||
"name": "Zimbra",
|
||||
"version": "7.2.8",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Zimbra (Server)",
|
||||
"ua": "Zimbra/9.0.0_GA_4138",
|
||||
"expect": {
|
||||
"name": "Zimbra",
|
||||
"version": "9.0.0_GA_4138",
|
||||
"type": "email"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "ZohoMail",
|
||||
"ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ZohoMail-Desktop/1.6.3 Chrome/124.0.6367.207 Electron/30.0.6 Safari/537.36",
|
||||
"expect": {
|
||||
"name": "ZohoMail-Desktop",
|
||||
"version": "1.6.3",
|
||||
"type": "email"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -289,6 +289,16 @@
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "NovaAct",
|
||||
"ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Agent-NovaAct/0.9",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "NovaAct",
|
||||
"version" : "0.9",
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Perplexity-User",
|
||||
"ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Perplexity-User/1.0; +https://perplexity.ai/perplexity-user)",
|
||||
|
||||
@@ -39,6 +39,36 @@
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Bun",
|
||||
"ua" : "Bun/1.0.6",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Bun",
|
||||
"version" : "1.0.6",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Dart",
|
||||
"ua" : "Dart/3.5 (dart:io)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Dart",
|
||||
"version" : "3.5",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Deno",
|
||||
"ua" : "Deno/2.1.7",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Deno",
|
||||
"version" : "2.1.7",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "go-http-client",
|
||||
"ua" : "go-http-client/1.1",
|
||||
@@ -69,6 +99,26 @@
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "hackney",
|
||||
"ua" : "hackney/1.20.1",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "hackney",
|
||||
"version" : "1.20.1",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "http.rb",
|
||||
"ua" : "http.rb/4.2.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "http.rb",
|
||||
"version" : "4.2.0",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Java",
|
||||
"ua" : "Java/1.6.0_14",
|
||||
@@ -89,6 +139,16 @@
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Jetty",
|
||||
"ua" : "Jetty/11.0.13",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Jetty",
|
||||
"version" : "11.0.13",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "jsdom",
|
||||
"ua" : "Mozilla/5.0 (unknown OS) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/11.12.0",
|
||||
@@ -130,22 +190,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Nutch",
|
||||
"ua" : "AliyunSecBot/Nutch-1.21-SNAPSHOT",
|
||||
"desc" : "Node.js",
|
||||
"ua" : "Node.js/22",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Nutch",
|
||||
"version" : "1.21-SNAPSHOT",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "OkHttp",
|
||||
"ua" : "okhttp/3.2.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "okhttp",
|
||||
"version" : "3.2.0",
|
||||
"name" : "Node.js",
|
||||
"version" : "22",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
@@ -159,6 +209,36 @@
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Nutch",
|
||||
"ua" : "AliyunSecBot/Nutch-1.21-SNAPSHOT",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Nutch",
|
||||
"version" : "1.21-SNAPSHOT",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "ocaml-cohttp",
|
||||
"ua" : "ocaml-cohttp/1.2.00",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "ocaml-cohttp",
|
||||
"version" : "1.2.00",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "OkHttp",
|
||||
"ua" : "okhttp/3.2.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "okhttp",
|
||||
"version" : "3.2.0",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "PHP-SOAP",
|
||||
"ua" : "PHP-SOAP/7.4.33",
|
||||
@@ -219,6 +299,16 @@
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "rest-client",
|
||||
"ua" : "rest-client/2.1.0 (linux-gnu x86_64) ruby/2.7.2p137",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "rest-client",
|
||||
"version" : "2.1.0",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Scrapy",
|
||||
"ua" : "Scrapy/1.5.0 (+https://scrapy.org)",
|
||||
@@ -238,5 +328,15 @@
|
||||
"version" : "5.0.2",
|
||||
"type" : "library"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "undici",
|
||||
"ua" : "undici",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "undici",
|
||||
"version" : "undefined",
|
||||
"type" : "library"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -25,5 +25,59 @@
|
||||
"name" : "Firefox OS",
|
||||
"version" : "44.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Firefox OS on Alcatel One Touch Fire",
|
||||
"ua" : "Mozilla/5.0 (Mobile; ALCATELOneTouch4012X; rv:18.1) Gecko/18.1 Firefox/18.1",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Firefox OS",
|
||||
"version" : "18.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Firefox OS on Huawei Y300II",
|
||||
"ua" : "Mozilla/5.0 (Mobile; HUAWEIY300-F1; rv:18.1) Gecko/18.1 Firefox/18.1",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Firefox OS",
|
||||
"version" : "18.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Firefox OS on LG Fireweb",
|
||||
"ua" : "Mozilla/5.0 (Mobile; LG-D300; rv:18.1) Gecko/18.1 Firefox/18.1",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Firefox OS",
|
||||
"version" : "18.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Firefox OS on LG FX0",
|
||||
"ua" : "Mozilla/5.0 (Mobile; LGL25; rv:32.0) Gecko/32.0 Firefox/32.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Firefox OS",
|
||||
"version" : "32.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Firefox OS on Panasonic Viera TV",
|
||||
"ua" : "Mozilla/5.0 (Linux; Viera; rv:34.0) Gecko/20100101 Firefox/34.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Firefox OS",
|
||||
"version" : "34.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Firefox OS on ZTE Open",
|
||||
"ua" : "Mozilla/5.0 (Mobile; ZTEOPEN; rv:18.1) Gecko/18.1 Firefox/18.1",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Firefox OS",
|
||||
"version" : "18.1"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -7,5 +7,14 @@
|
||||
"name" : "FreeBSD",
|
||||
"version" : "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "FreeBSD",
|
||||
"ua" : "Mozilla/5.0 (X11; FreeBSD; U; Viera; pl-PL) AppleWebKit/537.11 (KHTML, like Gecko) Viera/3.3.3 Chrome/23.0.1271.97 Safari/537.11",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "FreeBSD",
|
||||
"version" : "undefined"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -142,5 +142,41 @@
|
||||
"name" : "iOS",
|
||||
"version" : "11.2.5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iOS",
|
||||
"ua": "iPlayTV/3.3.9 (Apple TV; iOS 16.1; Scale/1.00)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "16.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iOS",
|
||||
"ua": "itunesstored/1.0 iOS/8.4.4 AppleTV/7.8 model/AppleTV3,2 build/12H937 (3; dt:12)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "8.4.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "tvOS",
|
||||
"ua": "iMPlayer/1.6.1 (tvOS 26.0.1)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "26.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "tvOS",
|
||||
"ua": "otg/1.5.1 (AppleTv Apple TV 4; tvOS16.2; appletv.client) libcurl/7.58.0 OpenSSL/1.0.2o zlib/1.2.11 clib/1.8.56",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "16.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -152,3 +152,45 @@ test.describe('request.headers can be passed in form of a Headers object', () =>
|
||||
expect(uap.ua).toBe('myBrowser/1.0');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Chaining withFeatureCheck() & withClientHints() in client-side development', () => {
|
||||
|
||||
test('Chain', async ({ page, browserName }) => {
|
||||
await page.addInitScript((browserName) => {
|
||||
Object.defineProperty(navigator, 'userAgent', {
|
||||
value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15'
|
||||
});
|
||||
Object.defineProperty(navigator, 'standalone', {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(navigator, 'maxTouchPoints', {
|
||||
value: 3
|
||||
});
|
||||
if (browserName == 'chromium') {
|
||||
Object.defineProperty(navigator, 'userAgentData', {
|
||||
value: {
|
||||
brands: [],
|
||||
platform: '',
|
||||
mobile: false,
|
||||
getHighEntropyValues: () => {
|
||||
return Promise.resolve({
|
||||
formFactors: 'VR'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, browserName);
|
||||
await page.goto(localHtml);
|
||||
// @ts-ignore
|
||||
const fc2ch = await page.evaluate(async () => await UAParser().withFeatureCheck().then(res => res.withClientHints()));
|
||||
const ch2fc = await page.evaluate(async () => await UAParser().withClientHints().then(res => res.withFeatureCheck()));
|
||||
if (browserName == 'chromium') {
|
||||
expect(fc2ch).toHaveProperty('device.type', 'xr'); // overwrite by client hints
|
||||
expect(ch2fc).toHaveProperty('device.type', 'tablet'); // overwrite by feature check
|
||||
} else {
|
||||
expect(fc2ch).toHaveProperty('device.type', 'tablet'); // no client hints found
|
||||
expect(ch2fc).toHaveProperty('device.type', 'tablet');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,85 +0,0 @@
|
||||
const assert = require('assert');
|
||||
const { UAParser } = require('../../src/main/ua-parser');
|
||||
const { getDeviceVendor, isAppleSilicon, isAIBot, isBot, isChromeFamily } = require('../../src/helpers/ua-parser-helpers');
|
||||
const { Bots, Emails } = require('../../src/extensions/ua-parser-extensions');
|
||||
const { DeviceVendor } = require('../../src/enums/ua-parser-enums');
|
||||
|
||||
describe('getDeviceVendor', () => {
|
||||
it('Can guess the device vendor from a model name', () => {
|
||||
|
||||
const modelSM = 'SM-A605G';
|
||||
const modelRedmi = 'Redmi Note 8';
|
||||
const modelNexus = 'Nexus 6P';
|
||||
const modelAquos = 'AQUOS-TVX19B';
|
||||
|
||||
assert.equal(getDeviceVendor(modelSM), DeviceVendor.SAMSUNG);
|
||||
assert.equal(getDeviceVendor(modelRedmi), DeviceVendor.XIAOMI);
|
||||
assert.equal(getDeviceVendor(modelNexus), DeviceVendor.HUAWEI);
|
||||
assert.equal(getDeviceVendor(modelAquos), DeviceVendor.SHARP);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAppleSilicon', () => {
|
||||
it('Can detect Apple Silicon device', () => {
|
||||
|
||||
// non-real ua
|
||||
const macARM = 'Mozilla/5.0 (Macintosh; ARM; Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0';
|
||||
const macIntel = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0';
|
||||
|
||||
assert.equal(isAppleSilicon(UAParser(macIntel)), false);
|
||||
assert.equal(isAppleSilicon(macIntel), false);
|
||||
assert.equal(isAppleSilicon(UAParser(macARM)), true);
|
||||
assert.equal(isAppleSilicon(macARM), true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAIBot', () => {
|
||||
it('Can detect AI Bots', () => {
|
||||
|
||||
const claudeBot = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)';
|
||||
const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0';
|
||||
const searchGPT = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot';
|
||||
const semrushAI = 'Mozilla/5.0 (compatible; SemrushBot-OCOB/1; +https://www.semrush.com/bot/)';
|
||||
|
||||
assert.equal(isAIBot(UAParser(claudeBot, Bots)), true);
|
||||
assert.equal(isAIBot(claudeBot), true);
|
||||
assert.equal(isAIBot(firefox), false);
|
||||
assert.equal(isAIBot(searchGPT), true);
|
||||
assert.equal(isAIBot(semrushAI), true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBot', () => {
|
||||
it('Can detect Bots', () => {
|
||||
|
||||
// non-real ua
|
||||
const ahrefsBot = 'Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)';
|
||||
const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0';
|
||||
const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)';
|
||||
const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0';
|
||||
|
||||
const botParser = new UAParser(firefox, { Bots, Emails });
|
||||
assert.equal(isBot(botParser.getResult()), false);
|
||||
assert.equal(isBot(botParser.setUA(ahrefsBot).getResult()), true);
|
||||
assert.equal(isBot(botParser.setUA(scrapy).getResult()), true);
|
||||
assert.equal(isBot(botParser.setUA(thunderbird).getResult()), false);
|
||||
|
||||
assert.equal(isBot(ahrefsBot), true);
|
||||
assert.equal(isBot(firefox), false);
|
||||
assert.equal(isBot(scrapy), true);
|
||||
assert.equal(isBot(thunderbird), false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isChromeFamily', () => {
|
||||
it('Can detect Chromium-based browser', () => {
|
||||
|
||||
const edge = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.2151.58';
|
||||
const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0';
|
||||
|
||||
assert.equal(isChromeFamily(UAParser(edge)), true);
|
||||
assert.equal(isChromeFamily(UAParser(firefox)), false);
|
||||
assert.equal(isChromeFamily(edge), true);
|
||||
assert.equal(isChromeFamily(firefox), false);
|
||||
});
|
||||
});
|
||||
42
test/unit/submodules/bot-detection.spec.js
Normal file
42
test/unit/submodules/bot-detection.spec.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const assert = require('assert');
|
||||
const { UAParser } = require('../../../src/main/ua-parser');
|
||||
const { isAICrawler, isBot } = require('../../../src/bot-detection/bot-detection');
|
||||
const { Bots, Emails } = require('../../../src/extensions/ua-parser-extensions');
|
||||
|
||||
describe('isAICrawler()', () => {
|
||||
it('Can detect AI Crawlers', () => {
|
||||
|
||||
// AI Crawlers
|
||||
const claudeBot = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)';
|
||||
const searchGPT = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot';
|
||||
const semrushAI = 'Mozilla/5.0 (compatible; SemrushBot-OCOB/1; +https://www.semrush.com/bot/)';
|
||||
|
||||
assert.equal(isAICrawler(claudeBot), true);
|
||||
assert.equal(isAICrawler(searchGPT), true);
|
||||
assert.equal(isAICrawler(semrushAI), true);
|
||||
|
||||
// Non-AI Crawlers
|
||||
const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0';
|
||||
|
||||
assert.equal(isAICrawler(firefox), false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isBot()', () => {
|
||||
it('Can detect general Bots', () => {
|
||||
|
||||
// Bots
|
||||
const ahrefsBot = 'Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)';
|
||||
const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)';
|
||||
|
||||
assert.equal(isBot(ahrefsBot), true);
|
||||
assert.equal(isBot(scrapy), true);
|
||||
|
||||
// Non-bots
|
||||
const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0';
|
||||
const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0';
|
||||
|
||||
assert.equal(isBot(firefox), false);
|
||||
assert.equal(isBot(thunderbird), false);
|
||||
});
|
||||
});
|
||||
16
test/unit/submodules/browser-detection.spec.js
Normal file
16
test/unit/submodules/browser-detection.spec.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const assert = require('assert');
|
||||
const { UAParser } = require('../../../src/main/ua-parser');
|
||||
const { isChromeFamily } = require('../../../src/browser-detection/browser-detection');
|
||||
|
||||
describe('isChromeFamily()', () => {
|
||||
it('Can detect Chromium-based browser', () => {
|
||||
|
||||
const edge = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.2151.58';
|
||||
const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0';
|
||||
|
||||
assert.equal(isChromeFamily(UAParser(edge)), true);
|
||||
assert.equal(isChromeFamily(edge), true);
|
||||
assert.equal(isChromeFamily(UAParser(firefox)), false);
|
||||
assert.equal(isChromeFamily(firefox), false);
|
||||
});
|
||||
});
|
||||
32
test/unit/submodules/device-detection.spec.js
Normal file
32
test/unit/submodules/device-detection.spec.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const assert = require('assert');
|
||||
const { UAParser } = require('../../../src/main/ua-parser');
|
||||
const { getDeviceVendor, isAppleSilicon } = require('../../../src/device-detection/device-detection');
|
||||
const { DeviceVendor } = require('../../../src/enums/ua-parser-enums');
|
||||
|
||||
describe('getDeviceVendor()', () => {
|
||||
it('Can guess the device vendor from a model name', () => {
|
||||
|
||||
const sm = 'SM-A605G';
|
||||
const redmi = 'Redmi Note 8';
|
||||
const nexus = 'Nexus 6P';
|
||||
const aquos = 'AQUOS-TVX19B';
|
||||
|
||||
assert.equal(getDeviceVendor(sm), DeviceVendor.SAMSUNG);
|
||||
assert.equal(getDeviceVendor(redmi), DeviceVendor.XIAOMI);
|
||||
assert.equal(getDeviceVendor(nexus), DeviceVendor.HUAWEI);
|
||||
assert.equal(getDeviceVendor(aquos), DeviceVendor.SHARP);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAppleSilicon()', () => {
|
||||
it('Can detect Apple Silicon device', () => {
|
||||
|
||||
const macARM = 'Mozilla/5.0 (Macintosh; ARM; Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0';
|
||||
const macIntel = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0';
|
||||
|
||||
assert.equal(isAppleSilicon(UAParser(macIntel)), false);
|
||||
assert.equal(isAppleSilicon(macIntel), false);
|
||||
assert.equal(isAppleSilicon(UAParser(macARM)), true);
|
||||
assert.equal(isAppleSilicon(macARM), true);
|
||||
});
|
||||
});
|
||||
@@ -3,9 +3,9 @@ const assert = require('assert');
|
||||
const parseJS = require('@babel/parser').parse;
|
||||
const traverse = require('@babel/traverse').default;
|
||||
const safe = require('safe-regex');
|
||||
const { UAParser } = require('../../src/main/ua-parser');
|
||||
const { Bots, CLIs, Crawlers, Emails, Fetchers, InApps, Libraries, Vehicles } = require('../../src/extensions/ua-parser-extensions');
|
||||
const { BrowserType, OSName, Extension } = require('../../src/enums/ua-parser-enums');
|
||||
const { UAParser } = require('../../../src/main/ua-parser');
|
||||
const { Bots, CLIs, Crawlers, Emails, Fetchers, InApps, Libraries, Vehicles } = require('../../../src/extensions/ua-parser-extensions');
|
||||
const { BrowserType, OSName, Extension } = require('../../../src/enums/ua-parser-enums');
|
||||
const { CLI, Crawler, Email, Fetcher, Library } = Extension.BrowserName;
|
||||
|
||||
describe('Extensions', () => {
|
||||
@@ -19,7 +19,7 @@ describe('Extensions', () => {
|
||||
['Vehicles', 'vehicle', Vehicles]
|
||||
]
|
||||
.forEach(([desc, path, ext]) => {
|
||||
const tests = require(`../data/ua/extension/${path}.json`);
|
||||
const tests = require(`../../data/ua/extension/${path}.json`);
|
||||
describe(desc, () => {
|
||||
tests.forEach((test) => {
|
||||
it(`Can detect ${test.desc}: "${test.ua}"`, () => {
|
||||
@@ -38,18 +38,34 @@ describe('Extensions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Existing test cases
|
||||
const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)';
|
||||
const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0';
|
||||
const axios = 'axios/1.3.5';
|
||||
const jsdom = 'Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3';
|
||||
const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)';
|
||||
|
||||
// New test cases for updated Regex logic
|
||||
const macOutlook = 'MacOutlook/16.61.22041701 (Intel Mac OS X 10.15.7)';
|
||||
const yahooMobile = 'YahooMobile/1.0 (mail; 3.0.5.1311380)';
|
||||
|
||||
assert.equal(UAParser(scrapy, Bots).browser.name, Library.SCRAPY);
|
||||
|
||||
const emailParser = new UAParser(Emails);
|
||||
|
||||
// Verify Standard Outlook
|
||||
assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: Email.MICROSOFT_OUTLOOK, version: "16.0.9126", major: "16", type: BrowserType.EMAIL});
|
||||
|
||||
// Verify Thunderbird
|
||||
assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: Email.THUNDERBIRD, version: "78.13.0", major: "78", type: BrowserType.EMAIL});
|
||||
|
||||
// Verify New MacOutlook Logic (Distinguishing it from Windows Outlook)
|
||||
assert.deepEqual(emailParser.setUA(macOutlook).getBrowser(), {name: Email.MICROSOFT_OUTLOOK_MAC, version: "16.61.22041701", major: "16", type: BrowserType.EMAIL});
|
||||
|
||||
// Verify Yahoo Mobile Logic (Tightened Regex)
|
||||
// Note: We expect 'Yahoo Mail' (Email.YAHOO_MAIL) because of the normalization helper.
|
||||
assert.deepEqual(emailParser.setUA(yahooMobile).getBrowser(), {name: Email.YAHOO_MAIL, version: "1.0", major: "1", type: BrowserType.EMAIL});
|
||||
|
||||
const libraryParser = new UAParser(Libraries);
|
||||
assert.deepEqual(libraryParser.setUA(axios).getBrowser(), {name: Library.AXIOS, version: "1.3.5", major: "1", type: BrowserType.LIBRARY});
|
||||
assert.deepEqual(libraryParser.setUA(jsdom).getBrowser(), {name: Library.JSDOM, version: "20.0.3", major: "20", type: BrowserType.LIBRARY});
|
||||
39
test/unit/submodules/helpers.spec.js
Normal file
39
test/unit/submodules/helpers.spec.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const assert = require('assert');
|
||||
const { isFrozenUA, getOutlookEdition } = require('../../../src/helpers/ua-parser-helpers');
|
||||
|
||||
describe('isFrozenUA()', () => {
|
||||
it('matches supplied user-agent string with known frozen user-agent pattern', () => {
|
||||
|
||||
const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36";
|
||||
const frozenMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36";
|
||||
|
||||
assert.equal(isFrozenUA(regularMobileUA), false);
|
||||
assert.equal(isFrozenUA(frozenMobileUA), true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOutlookEdition()', () => {
|
||||
it('identifies Windows versions correctly', () => {
|
||||
// MSI Version (Older engine)
|
||||
assert.equal(getOutlookEdition('Microsoft Outlook', '16.0.4266.1001'), 'Outlook 2016 (MSI / Volume License)');
|
||||
// Click-to-Run (Modern engine)
|
||||
assert.equal(getOutlookEdition('Microsoft Outlook', '16.0.14326.20000'), 'Outlook 365 / 2019+ (Modern)');
|
||||
// Legacy Major Version
|
||||
assert.equal(getOutlookEdition('Microsoft Outlook', '15.0.4569.1506'), 'Outlook 2013');
|
||||
});
|
||||
|
||||
it('identifies Mac versions correctly', () => {
|
||||
assert.equal(getOutlookEdition('MacOutlook', '16.61'), 'Outlook for Mac (Modern)');
|
||||
assert.equal(getOutlookEdition('MacOutlook', '15.4'), 'Outlook for Mac (Legacy)');
|
||||
});
|
||||
|
||||
it('returns original name for unknown inputs', () => {
|
||||
assert.equal(getOutlookEdition('Thunderbird', '91.0'), 'Thunderbird');
|
||||
});
|
||||
|
||||
it('handles New Outlook (OneOutlook) correctly', () => {
|
||||
// New Outlook usually sends a browser UA, but if it sends "Outlook" without version info matches,
|
||||
// it shouldn't trigger the Legacy/MSI logic.
|
||||
assert.equal(getOutlookEdition('Microsoft Outlook', 'SomeRandomString'), 'Microsoft Outlook');
|
||||
});
|
||||
});
|
||||
@@ -413,4 +413,20 @@ describe('Identify vendor & type of device from given model name', () => {
|
||||
assert.strictEqual(device.type, test.expect.type);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Chaining withClientHints() & withFeatureCheck() in server-side development', () => {
|
||||
const headers = {
|
||||
'user-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15',
|
||||
'sec-ch-ua-form-factors' : '"VR"'
|
||||
};
|
||||
const device = new UAParser(headers).getDevice();
|
||||
it('Chain order: withFeatureCheck().withClientHints()', () => {
|
||||
const fc2ch = device.withFeatureCheck().withClientHints();
|
||||
assert.strictEqual(fc2ch.type, "xr");
|
||||
});
|
||||
it('Chain order: withClientHints().withFeatureCheck()', () => {
|
||||
const ch2fc = device.withClientHints().withFeatureCheck();
|
||||
assert.strictEqual(ch2fc.type, "xr");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user