mirror of
https://github.com/faisalman/ua-parser-js.git
synced 2025-11-15 14:42:16 +03:00
Merge tag '2.0.6' into pro-personal
This commit is contained in:
22
CHANGELOG.md
22
CHANGELOG.md
@@ -23,8 +23,12 @@
|
||||
- **Support for Custom/Predefined Extensions:**
|
||||
- Pass custom regexes or predefined extensions as a list to `UAParser()`
|
||||
|
||||
- **Support for CLI Parsing:**
|
||||
- Parse a user-agent directly from the command line using `npx ua-parser-js "[User-Agent]"`
|
||||
- **Support for CLI Processing:**
|
||||
- Directly parse user-agent strings from the command line:
|
||||
`npx ua-parser-js "<User-Agent>"`
|
||||
- Process batch data from files:
|
||||
`npx ua-parser-js --input-file=log.txt >> result.json` or
|
||||
`npx ua-parser-js --input-file=log.txt --output-file=result.json`
|
||||
|
||||
- **Enhanced Detection with Client Hints:**
|
||||
- `withClientHints()`: Improves detection accuracy by leveraging client hints
|
||||
@@ -65,6 +69,20 @@
|
||||
|
||||
---
|
||||
|
||||
## Version 2.0.6
|
||||
- Add new CLI feature: processing batch user-agent data from file and output as JSON
|
||||
- Fix `setUA()`: trim leading space from user-agent string input
|
||||
- Replace `undici` dependency with node's internal `Headers`
|
||||
- Add new browser: Bing, Qwant
|
||||
- Add new device vendor: Hisense, Wiko
|
||||
- Improve browser detection: Mozilla, Pale Moon
|
||||
- Improve CPU detection: 68k
|
||||
- Improve device detection: Apple, BlackBerry, Huawei, Nokia, Xiaomi
|
||||
- Improve OS detection: iOS 26
|
||||
- `extensions` submodule:
|
||||
- Add new fetcher: Discordbot, KeybaseBot, Slackbot, Slackbot-LinkExpanding, Slack-ImgProxy, Twitterbot
|
||||
- Add new crawler: Qwantbot-news, SurdotlyBot, SwiftBot
|
||||
|
||||
## Version 2.0.5
|
||||
|
||||
- Add new browser: Zalo
|
||||
|
||||
@@ -22,8 +22,12 @@ https://docs.uaparser.dev
|
||||
import { UAParser } from '@ua-parser-js/pro-personal';
|
||||
```
|
||||
|
||||
```js
|
||||
import { isFrozenUA } from '@ua-parser-js/pro-personal/helpers';
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
UAParser.js PRO Personal
|
||||
|
||||
Copyright (c) 2023-2024 Faisal Salman <<f@faisalman.com>>
|
||||
Copyright (c) 2023-2025 Faisal Salman <<f@faisalman.com>>
|
||||
|
||||
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
16
package-lock.json
generated
16
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@ua-parser-js/pro-personal",
|
||||
"version": "2.0.5",
|
||||
"version": "2.0.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ua-parser-js/pro-personal",
|
||||
"version": "2.0.5",
|
||||
"version": "2.0.6",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -25,8 +25,7 @@
|
||||
"dependencies": {
|
||||
"detect-europe-js": "^0.1.2",
|
||||
"is-standalone-pwa": "^0.1.1",
|
||||
"ua-is-frozen": "^0.1.2",
|
||||
"undici": "^7.12.0"
|
||||
"ua-is-frozen": "^0.1.2"
|
||||
},
|
||||
"bin": {
|
||||
"pro-personal": "script/cli.js"
|
||||
@@ -2693,15 +2692,6 @@
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.12.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz",
|
||||
"integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/validate-npm-package-license": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
|
||||
|
||||
15
package.json
15
package.json
@@ -1,25 +1,25 @@
|
||||
{
|
||||
"title": "UAParser.js PRO Personal",
|
||||
"name": "@ua-parser-js/pro-personal",
|
||||
"version": "2.0.5",
|
||||
"version": "2.0.6",
|
||||
"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": [
|
||||
"user-agent",
|
||||
"client-hints",
|
||||
"parser",
|
||||
"browser",
|
||||
"engine",
|
||||
"os",
|
||||
"device",
|
||||
"cpu",
|
||||
"jquery-plugin",
|
||||
"ecosystem:jquery",
|
||||
"ua-parser-js",
|
||||
"browser-detection",
|
||||
"device-detection",
|
||||
"os-detection",
|
||||
"bot-detection"
|
||||
"bot-detection",
|
||||
"ai-detection",
|
||||
"app-detection",
|
||||
"crawler-detection"
|
||||
],
|
||||
"homepage": "https://uaparser.dev",
|
||||
"contributors": [
|
||||
@@ -220,14 +220,13 @@
|
||||
"test:eslint": "eslint src && eslint script",
|
||||
"test:jshint": "jshint src/main",
|
||||
"test:lockfile-lint": "npx lockfile-lint -p package-lock.json",
|
||||
"test:mocha": "mocha test/unit",
|
||||
"test:mocha": "mocha --recursive test/unit",
|
||||
"test:playwright": "npx playwright install && playwright test test/e2e --browser all"
|
||||
},
|
||||
"dependencies": {
|
||||
"detect-europe-js": "^0.1.2",
|
||||
"is-standalone-pwa": "^0.1.1",
|
||||
"ua-is-frozen": "^0.1.2",
|
||||
"undici": "^7.12.0"
|
||||
"ua-is-frozen": "^0.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/parser": "7.15.8",
|
||||
|
||||
@@ -1,4 +1,94 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const UAParser = require('ua-parser-js');
|
||||
console.log(JSON.stringify(process.argv.slice(2).map(ua => UAParser(ua)), null, 4));
|
||||
try {
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { performance } = require('node:perf_hooks');
|
||||
const readline = require('node:readline');
|
||||
const { parseArgs } = require('node:util');
|
||||
const UAParser = require('../src/main/ua-parser');
|
||||
const { Bots, Emails, ExtraDevices, InApps, Vehicles } = require('../src/extensions/ua-parser-extensions');
|
||||
|
||||
if (!process.argv[2].startsWith('-')) {
|
||||
|
||||
const results = process.argv.slice(2).map(ua => UAParser(ua));
|
||||
console.log(JSON.stringify(results, null, 4));
|
||||
process.exit(0);
|
||||
|
||||
} else if (['-h', '--help'].includes(process.argv[2])) {
|
||||
|
||||
console.log('Usage: npx ua-parser-js <string>');
|
||||
console.log(' or npx ua-parser-js --input-file <filepath> [--output-file <filepath>]');
|
||||
console.log('-i, --input-file');
|
||||
console.log('-o, --output-file');
|
||||
process.exit(0);
|
||||
|
||||
} else {
|
||||
|
||||
const startPerf = performance.now();
|
||||
const {
|
||||
values: {
|
||||
'input-file': inputFile,
|
||||
'output-file': outputFile
|
||||
},
|
||||
} = parseArgs({
|
||||
options: {
|
||||
'input-file': { type: 'string', short: 'i' },
|
||||
'output-file': { type: 'string', short: 'o' }
|
||||
}
|
||||
});
|
||||
|
||||
if (!inputFile) {
|
||||
console.error('Input file must be present');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const inputPath = path.resolve(__dirname, inputFile);
|
||||
const outputPath = outputFile ? path.resolve(__dirname, outputFile) : null;
|
||||
|
||||
if (!fs.existsSync(inputPath)) {
|
||||
console.error(`Input file not found: ${inputPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const inputStream = fs.createReadStream(inputPath, 'utf8');
|
||||
const rl = readline.createInterface({
|
||||
input: inputStream,
|
||||
crlfDelay: Infinity
|
||||
});
|
||||
|
||||
const outputStream = outputPath ? fs.createWriteStream(outputPath, { encoding : 'utf8' }) : process.stdout;
|
||||
|
||||
const uap = new UAParser([Bots, Emails, ExtraDevices, InApps, Vehicles]);
|
||||
let lineNumber = 0;
|
||||
|
||||
outputStream.write('[\n');
|
||||
|
||||
rl.on('line', line => {
|
||||
const result = uap.setUA(line).getResult();
|
||||
const json = JSON.stringify(result, null, 4);
|
||||
if (lineNumber > 0) outputStream.write(',\n');
|
||||
outputStream.write(json);
|
||||
lineNumber++;
|
||||
});
|
||||
|
||||
rl.on('close', () => {
|
||||
outputStream.write('\n]');
|
||||
if (outputPath) {
|
||||
outputStream.end(() => {
|
||||
const finishPerf = performance.now();
|
||||
console.log(`Done!`);
|
||||
console.log(`Number of lines found: ${lineNumber}`);
|
||||
console.log(`Task finished in: ${(finishPerf - startPerf).toFixed(3)}ms`);
|
||||
console.log(`Output written to: ${outputPath}`);
|
||||
process.exit(0);
|
||||
});
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
15
src/enums/ua-parser-enums.d.ts
vendored
15
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.5
|
||||
/* Enums for UAParser.js v2.0.6
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
UAParser.js PRO Personal License */
|
||||
@@ -24,6 +24,7 @@ export const BrowserName: Readonly<{
|
||||
AVG: 'AVG Secure Browser',
|
||||
BAIDU: 'Baidu Browser',
|
||||
BASILISK: 'Basilisk',
|
||||
BING: 'Bing',
|
||||
BLAZER: 'Blazer',
|
||||
BOLT: 'Bolt',
|
||||
BOWSER: 'Bowser',
|
||||
@@ -129,6 +130,7 @@ export const BrowserName: Readonly<{
|
||||
QUARK: 'Quark',
|
||||
QUPZILLA: 'QupZilla',
|
||||
QUTEBROWSER: 'qutebrowser',
|
||||
QWANT: 'Qwant',
|
||||
REKONQ: 'rekonq',
|
||||
ROCKMELT: 'Rockmelt',
|
||||
SAFARI: 'Safari',
|
||||
@@ -243,6 +245,7 @@ export const DeviceVendor: Readonly<{
|
||||
GEEKSPHONE: 'GeeksPhone',
|
||||
GENERIC: 'Generic',
|
||||
GOOGLE: 'Google',
|
||||
HISENSE: 'Hisense',
|
||||
HMD: 'HMD',
|
||||
HP: 'HP',
|
||||
HTC: 'HTC',
|
||||
@@ -291,6 +294,7 @@ export const DeviceVendor: Readonly<{
|
||||
VIVO: 'Vivo',
|
||||
VIZIO: 'Vizio',
|
||||
VODAFONE: 'Vodafone',
|
||||
WIKO: 'Wiko',
|
||||
XBOX: 'Xbox',
|
||||
XIAOMI: 'Xiaomi',
|
||||
ZEBRA: 'Zebra',
|
||||
@@ -489,6 +493,7 @@ export const Extension: Readonly<{
|
||||
DUCKDUCKGO_BOT: 'DuckDuckBot',
|
||||
DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot',
|
||||
ELASTIC: 'Elastic',
|
||||
ELASTIC_SWIFTYPE_BOT: 'Swiftbot',
|
||||
EXALEAD_EXABOT: 'Exabot',
|
||||
FIRECRAWL_AGENT: 'FirecrawlAgent',
|
||||
FREESPOKE: 'Freespoke',
|
||||
@@ -538,6 +543,7 @@ export const Extension: Readonly<{
|
||||
PERPLEXITY_BOT: 'PerplexityBot',
|
||||
QIHOO_360_SPIDER: '360Spider',
|
||||
QWANT_BOT: 'Qwantbot',
|
||||
QWANT_BOT_NEWS: 'Qwantbot-news',
|
||||
REPLICATE_BOT: 'Replicate-Bot',
|
||||
RUNPOD_BOT: 'RunPod-Bot',
|
||||
SB_INTUITIONS_BOT: 'SBIntuitionsBot',
|
||||
@@ -551,6 +557,7 @@ export const Extension: Readonly<{
|
||||
SOGOU_PIC_SPIDER: 'Sogou Pic Spider',
|
||||
SOGOU_WEB_SPIDER: 'Sogou web spider',
|
||||
STARTPAGE: 'Startpage',
|
||||
SURLY_BOT: 'SurdotlyBot',
|
||||
TIMPI_BOT: 'Timpibot',
|
||||
TOGETHER_BOT: 'Together-Bot',
|
||||
TURNITIN_BOT: 'TurnitinBot',
|
||||
@@ -631,6 +638,7 @@ export const Extension: Readonly<{
|
||||
BLUESKY: 'Bluesky',
|
||||
BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot',
|
||||
COHERE_AI: 'Cohere-AI',
|
||||
DISCORD_BOT: 'Discordbot',
|
||||
DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot',
|
||||
GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse',
|
||||
GOOGLE_FEEDFETCHER: 'FeedFetcher-Google',
|
||||
@@ -643,6 +651,7 @@ export const Extension: Readonly<{
|
||||
HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher',
|
||||
IFRAMELY: 'Iframely',
|
||||
KAKAOTALK_SCRAP: 'kakaotalk-scrap',
|
||||
KEYBASE_BOT: 'KeybaseBot',
|
||||
META_EXTERNALFETCHER: 'meta-externalfetcher',
|
||||
META_WHATSAPP: 'WhatsApp',
|
||||
MICROSOFT_BINGPREVIEW: 'BingPreview',
|
||||
@@ -654,6 +663,9 @@ export const Extension: Readonly<{
|
||||
PERPLEXITY_USER: 'Perplexity-User',
|
||||
PINTEREST_BOT: 'Pinterestbot',
|
||||
SEMRUSH_SITEAUDITBOT: 'SiteAuditBot',
|
||||
SLACK_BOT: 'Slackbot',
|
||||
SLACK_BOT_LINKEXPANDING: 'Slackbot-LinkExpanding',
|
||||
SLACK_IMGPROXY: 'Slack-ImgProxy',
|
||||
SNAP_URL_PREVIEW: 'Snap URL Preview',
|
||||
SKYPE_URIPREVIEW: 'SkypeUriPreview',
|
||||
TELEGRAM_BOT: 'TelegramBot',
|
||||
@@ -663,6 +675,7 @@ export const Extension: Readonly<{
|
||||
VERCEL_BOT: 'Vercelbot',
|
||||
VERCEL_FLAGS: 'vercelflags',
|
||||
VERCEL_TRACING: 'verceltracing',
|
||||
X_TWITTERBOT: 'Twitterbot',
|
||||
YANDEX_CALENDAR: 'YandexCalendar',
|
||||
YANDEX_DIRECT: 'YandexDirect',
|
||||
YANDEX_DIRECTDYN: 'YandexDirectDyn',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////
|
||||
/* Enums for UAParser.js v2.0.5
|
||||
/* Enums for UAParser.js v2.0.6
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
UAParser.js PRO Personal License */
|
||||
@@ -20,6 +20,7 @@ const BrowserName = Object.freeze({
|
||||
AVG: 'AVG Secure Browser',
|
||||
BAIDU: 'Baidu Browser',
|
||||
BASILISK: 'Basilisk',
|
||||
BING: 'Bing',
|
||||
BLAZER: 'Blazer',
|
||||
BOLT: 'Bolt',
|
||||
BOWSER: 'Bowser',
|
||||
@@ -125,6 +126,7 @@ const BrowserName = Object.freeze({
|
||||
QUARK: 'Quark',
|
||||
QUPZILLA: 'QupZilla',
|
||||
QUTEBROWSER: 'qutebrowser',
|
||||
QWANT: 'Qwant',
|
||||
REKONQ: 'rekonq',
|
||||
ROCKMELT: 'Rockmelt',
|
||||
SAFARI: 'Safari',
|
||||
@@ -239,6 +241,7 @@ const DeviceVendor = Object.freeze({
|
||||
GEEKSPHONE: 'GeeksPhone',
|
||||
GENERIC: 'Generic',
|
||||
GOOGLE: 'Google',
|
||||
HISENSE: 'Hisense',
|
||||
HMD: 'HMD',
|
||||
HP: 'HP',
|
||||
HTC: 'HTC',
|
||||
@@ -287,6 +290,7 @@ const DeviceVendor = Object.freeze({
|
||||
VIVO: 'Vivo',
|
||||
VIZIO: 'Vizio',
|
||||
VODAFONE: 'Vodafone',
|
||||
WIKO: 'Wiko',
|
||||
XBOX: 'Xbox',
|
||||
XIAOMI: 'Xiaomi',
|
||||
ZEBRA: 'Zebra',
|
||||
@@ -485,6 +489,7 @@ const Extension = Object.freeze({
|
||||
DUCKDUCKGO_BOT: 'DuckDuckBot',
|
||||
DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot',
|
||||
ELASTIC: 'Elastic',
|
||||
ELASTIC_SWIFTYPE_BOT: 'Swiftbot',
|
||||
EXALEAD_EXABOT: 'Exabot',
|
||||
FIRECRAWL_AGENT: 'FirecrawlAgent',
|
||||
FREESPOKE: 'Freespoke',
|
||||
@@ -534,6 +539,7 @@ const Extension = Object.freeze({
|
||||
PERPLEXITY_BOT: 'PerplexityBot',
|
||||
QIHOO_360_SPIDER: '360Spider',
|
||||
QWANT_BOT: 'Qwantbot',
|
||||
QWANT_BOT_NEWS: 'Qwantbot-news',
|
||||
REPLICATE_BOT: 'Replicate-Bot',
|
||||
RUNPOD_BOT: 'RunPod-Bot',
|
||||
SB_INTUITIONS_BOT: 'SBIntuitionsBot',
|
||||
@@ -547,6 +553,7 @@ const Extension = Object.freeze({
|
||||
SOGOU_PIC_SPIDER: 'Sogou Pic Spider',
|
||||
SOGOU_WEB_SPIDER: 'Sogou web spider',
|
||||
STARTPAGE: 'Startpage',
|
||||
SURLY_BOT: 'SurdotlyBot',
|
||||
TIMPI_BOT: 'Timpibot',
|
||||
TOGETHER_BOT: 'Together-Bot',
|
||||
TURNITIN_BOT: 'TurnitinBot',
|
||||
@@ -627,6 +634,7 @@ const Extension = Object.freeze({
|
||||
BLUESKY: 'Bluesky',
|
||||
BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot',
|
||||
COHERE_AI: 'Cohere-AI',
|
||||
DISCORD_BOT: 'Discordbot',
|
||||
DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot',
|
||||
GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse',
|
||||
GOOGLE_FEEDFETCHER: 'FeedFetcher-Google',
|
||||
@@ -639,6 +647,7 @@ const Extension = Object.freeze({
|
||||
HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher',
|
||||
IFRAMELY: 'Iframely',
|
||||
KAKAOTALK_SCRAP: 'kakaotalk-scrap',
|
||||
KEYBASE_BOT: 'KeybaseBot',
|
||||
META_EXTERNALFETCHER: 'meta-externalfetcher',
|
||||
META_WHATSAPP: 'WhatsApp',
|
||||
MICROSOFT_BINGPREVIEW: 'BingPreview',
|
||||
@@ -650,6 +659,9 @@ const Extension = Object.freeze({
|
||||
PERPLEXITY_USER: 'Perplexity-User',
|
||||
PINTEREST_BOT: 'Pinterestbot',
|
||||
SEMRUSH_SITEAUDITBOT: 'SiteAuditBot',
|
||||
SLACK_BOT: 'Slackbot',
|
||||
SLACK_BOT_LINKEXPANDING: 'Slackbot-LinkExpanding',
|
||||
SLACK_IMGPROXY: 'Slack-ImgProxy',
|
||||
SNAP_URL_PREVIEW: 'Snap URL Preview',
|
||||
SKYPE_URIPREVIEW: 'SkypeUriPreview',
|
||||
TELEGRAM_BOT: 'TelegramBot',
|
||||
@@ -659,6 +671,7 @@ const Extension = Object.freeze({
|
||||
VERCEL_BOT: 'Vercelbot',
|
||||
VERCEL_FLAGS: 'vercelflags',
|
||||
VERCEL_TRACING: 'verceltracing',
|
||||
X_TWITTERBOT: 'Twitterbot',
|
||||
YANDEX_CALENDAR: 'YandexCalendar',
|
||||
YANDEX_DIRECT: 'YandexDirect',
|
||||
YANDEX_DIRECTDYN: 'YandexDirectDyn',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Source: /src/enums/ua-parser-enums.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Enums for UAParser.js v2.0.5
|
||||
/* Enums for UAParser.js v2.0.6
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
UAParser.js PRO Personal License */
|
||||
@@ -24,6 +24,7 @@ const BrowserName = Object.freeze({
|
||||
AVG: 'AVG Secure Browser',
|
||||
BAIDU: 'Baidu Browser',
|
||||
BASILISK: 'Basilisk',
|
||||
BING: 'Bing',
|
||||
BLAZER: 'Blazer',
|
||||
BOLT: 'Bolt',
|
||||
BOWSER: 'Bowser',
|
||||
@@ -129,6 +130,7 @@ const BrowserName = Object.freeze({
|
||||
QUARK: 'Quark',
|
||||
QUPZILLA: 'QupZilla',
|
||||
QUTEBROWSER: 'qutebrowser',
|
||||
QWANT: 'Qwant',
|
||||
REKONQ: 'rekonq',
|
||||
ROCKMELT: 'Rockmelt',
|
||||
SAFARI: 'Safari',
|
||||
@@ -243,6 +245,7 @@ const DeviceVendor = Object.freeze({
|
||||
GEEKSPHONE: 'GeeksPhone',
|
||||
GENERIC: 'Generic',
|
||||
GOOGLE: 'Google',
|
||||
HISENSE: 'Hisense',
|
||||
HMD: 'HMD',
|
||||
HP: 'HP',
|
||||
HTC: 'HTC',
|
||||
@@ -291,6 +294,7 @@ const DeviceVendor = Object.freeze({
|
||||
VIVO: 'Vivo',
|
||||
VIZIO: 'Vizio',
|
||||
VODAFONE: 'Vodafone',
|
||||
WIKO: 'Wiko',
|
||||
XBOX: 'Xbox',
|
||||
XIAOMI: 'Xiaomi',
|
||||
ZEBRA: 'Zebra',
|
||||
@@ -489,6 +493,7 @@ const Extension = Object.freeze({
|
||||
DUCKDUCKGO_BOT: 'DuckDuckBot',
|
||||
DUCKDUCKGO_FAVICONS_BOT: 'DuckDuckGo-Favicons-Bot',
|
||||
ELASTIC: 'Elastic',
|
||||
ELASTIC_SWIFTYPE_BOT: 'Swiftbot',
|
||||
EXALEAD_EXABOT: 'Exabot',
|
||||
FIRECRAWL_AGENT: 'FirecrawlAgent',
|
||||
FREESPOKE: 'Freespoke',
|
||||
@@ -538,6 +543,7 @@ const Extension = Object.freeze({
|
||||
PERPLEXITY_BOT: 'PerplexityBot',
|
||||
QIHOO_360_SPIDER: '360Spider',
|
||||
QWANT_BOT: 'Qwantbot',
|
||||
QWANT_BOT_NEWS: 'Qwantbot-news',
|
||||
REPLICATE_BOT: 'Replicate-Bot',
|
||||
RUNPOD_BOT: 'RunPod-Bot',
|
||||
SB_INTUITIONS_BOT: 'SBIntuitionsBot',
|
||||
@@ -551,6 +557,7 @@ const Extension = Object.freeze({
|
||||
SOGOU_PIC_SPIDER: 'Sogou Pic Spider',
|
||||
SOGOU_WEB_SPIDER: 'Sogou web spider',
|
||||
STARTPAGE: 'Startpage',
|
||||
SURLY_BOT: 'SurdotlyBot',
|
||||
TIMPI_BOT: 'Timpibot',
|
||||
TOGETHER_BOT: 'Together-Bot',
|
||||
TURNITIN_BOT: 'TurnitinBot',
|
||||
@@ -631,6 +638,7 @@ const Extension = Object.freeze({
|
||||
BLUESKY: 'Bluesky',
|
||||
BUFFER_LINKPREVIEWBOT: 'BufferLinkPreviewBot',
|
||||
COHERE_AI: 'Cohere-AI',
|
||||
DISCORD_BOT: 'Discordbot',
|
||||
DUCKDUCKGO_ASSISTBOT: 'DuckAssistBot',
|
||||
GOOGLE_CHROME_LIGHTHOUSE: 'Chrome-Lighthouse',
|
||||
GOOGLE_FEEDFETCHER: 'FeedFetcher-Google',
|
||||
@@ -643,6 +651,7 @@ const Extension = Object.freeze({
|
||||
HUBSPOT_PAGE_FETCHER: 'HubSpot Page Fetcher',
|
||||
IFRAMELY: 'Iframely',
|
||||
KAKAOTALK_SCRAP: 'kakaotalk-scrap',
|
||||
KEYBASE_BOT: 'KeybaseBot',
|
||||
META_EXTERNALFETCHER: 'meta-externalfetcher',
|
||||
META_WHATSAPP: 'WhatsApp',
|
||||
MICROSOFT_BINGPREVIEW: 'BingPreview',
|
||||
@@ -654,6 +663,9 @@ const Extension = Object.freeze({
|
||||
PERPLEXITY_USER: 'Perplexity-User',
|
||||
PINTEREST_BOT: 'Pinterestbot',
|
||||
SEMRUSH_SITEAUDITBOT: 'SiteAuditBot',
|
||||
SLACK_BOT: 'Slackbot',
|
||||
SLACK_BOT_LINKEXPANDING: 'Slackbot-LinkExpanding',
|
||||
SLACK_IMGPROXY: 'Slack-ImgProxy',
|
||||
SNAP_URL_PREVIEW: 'Snap URL Preview',
|
||||
SKYPE_URIPREVIEW: 'SkypeUriPreview',
|
||||
TELEGRAM_BOT: 'TelegramBot',
|
||||
@@ -663,6 +675,7 @@ const Extension = Object.freeze({
|
||||
VERCEL_BOT: 'Vercelbot',
|
||||
VERCEL_FLAGS: 'vercelflags',
|
||||
VERCEL_TRACING: 'verceltracing',
|
||||
X_TWITTERBOT: 'Twitterbot',
|
||||
YANDEX_CALENDAR: 'YandexCalendar',
|
||||
YANDEX_DIRECT: 'YandexDirect',
|
||||
YANDEX_DIRECTDYN: 'YandexDirectDyn',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////
|
||||
/* Extensions for UAParser.js v2.0.5
|
||||
/* Extensions for UAParser.js v2.0.6
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
UAParser.js PRO Personal License */
|
||||
@@ -63,8 +63,10 @@ const Crawlers = Object.freeze({
|
||||
// PerplexityBot - https://perplexity.ai/perplexitybot
|
||||
// SBIntuitionsBot - https://www.sbintuitions.co.jp/bot/
|
||||
// SeznamBot - http://napoveda.seznam.cz/seznambot-intro
|
||||
// SurdotlyBot - http://sur.ly/bot.html
|
||||
// Swiftbot - https://swiftype.com/swiftbot
|
||||
// YepBot - https://yep.com/yepbot/
|
||||
/((?:adidx|ahrefs|amazon|bing|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|sbintuitions|semrush|seznam|yep)bot)\/([\w\.-]+)/i,
|
||||
/((?:adidx|ahrefs|amazon|bing|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|sbintuitions|semrush|seznam|surdotly|swift|yep)bot)\/([\w\.-]+)/i,
|
||||
|
||||
// Algolia Crawler
|
||||
/(algolia crawler(?: renderscript)?)\/?([\w\.]*)/i,
|
||||
@@ -98,7 +100,7 @@ const Crawlers = Object.freeze({
|
||||
/(oncrawl) mobile\/([\w\.]+)/i,
|
||||
|
||||
// Qwantbot - https://help.qwant.com/bot
|
||||
/(qwantbot)[-\w]*\/?([\w\.]*)/i,
|
||||
/(qwantbot(?:-news)?)[-\w]*\/?([\w\.]*)/i,
|
||||
|
||||
// SemrushBot - http://www.semrush.com/bot.html
|
||||
/((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i,
|
||||
@@ -283,8 +285,8 @@ const Fetchers = Object.freeze({
|
||||
[NAME, VERSION, [TYPE, FETCHER]],
|
||||
|
||||
[
|
||||
// Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / Snapchat / Vercelbot / Yandex Bots
|
||||
/((?:better uptime |telegram|vercel)bot|chrome-lighthouse|feedfetcher-google|gemini-deep-research|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|vercel(flags|tracing|-(favicon|screenshot)-bot)|yandex(?:sitelinks|userproxy))/i
|
||||
// Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / KeybaseBot / Snapchat / Vercelbot / Yandex Bots
|
||||
/((?:better uptime |keybase|telegram|vercel)bot|chrome-lighthouse|feedfetcher-google|gemini-deep-research|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|vercel(flags|tracing|-(favicon|screenshot)-bot)|yandex(?:sitelinks|userproxy))/i
|
||||
],
|
||||
[NAME, [TYPE, FETCHER]],
|
||||
],
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Source: /src/extensions/ua-parser-extensions.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Extensions for UAParser.js v2.0.5
|
||||
/* Extensions for UAParser.js v2.0.6
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
UAParser.js PRO Personal License */
|
||||
@@ -67,8 +67,10 @@ const Crawlers = Object.freeze({
|
||||
// PerplexityBot - https://perplexity.ai/perplexitybot
|
||||
// SBIntuitionsBot - https://www.sbintuitions.co.jp/bot/
|
||||
// SeznamBot - http://napoveda.seznam.cz/seznambot-intro
|
||||
// SurdotlyBot - http://sur.ly/bot.html
|
||||
// Swiftbot - https://swiftype.com/swiftbot
|
||||
// YepBot - https://yep.com/yepbot/
|
||||
/((?:adidx|ahrefs|amazon|bing|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|sbintuitions|semrush|seznam|yep)bot)\/([\w\.-]+)/i,
|
||||
/((?:adidx|ahrefs|amazon|bing|brave|cc|contx|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kagi|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|sbintuitions|semrush|seznam|surdotly|swift|yep)bot)\/([\w\.-]+)/i,
|
||||
|
||||
// Algolia Crawler
|
||||
/(algolia crawler(?: renderscript)?)\/?([\w\.]*)/i,
|
||||
@@ -102,7 +104,7 @@ const Crawlers = Object.freeze({
|
||||
/(oncrawl) mobile\/([\w\.]+)/i,
|
||||
|
||||
// Qwantbot - https://help.qwant.com/bot
|
||||
/(qwantbot)[-\w]*\/?([\w\.]*)/i,
|
||||
/(qwantbot(?:-news)?)[-\w]*\/?([\w\.]*)/i,
|
||||
|
||||
// SemrushBot - http://www.semrush.com/bot.html
|
||||
/((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i,
|
||||
@@ -287,8 +289,8 @@ const Fetchers = Object.freeze({
|
||||
[NAME, VERSION, [TYPE, FETCHER]],
|
||||
|
||||
[
|
||||
// Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / Snapchat / Vercelbot / Yandex Bots
|
||||
/((?:better uptime |telegram|vercel)bot|chrome-lighthouse|feedfetcher-google|gemini-deep-research|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|vercel(flags|tracing|-(favicon|screenshot)-bot)|yandex(?:sitelinks|userproxy))/i
|
||||
// Google Bots / Chrome-Lighthouse / Gemini-Deep-Research / KeybaseBot / Snapchat / Vercelbot / Yandex Bots
|
||||
/((?:better uptime |keybase|telegram|vercel)bot|chrome-lighthouse|feedfetcher-google|gemini-deep-research|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|vercel(flags|tracing|-(favicon|screenshot)-bot)|yandex(?:sitelinks|userproxy))/i
|
||||
],
|
||||
[NAME, [TYPE, FETCHER]],
|
||||
],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////
|
||||
/* Helpers for UAParser.js v2.0.5
|
||||
/* Helpers for UAParser.js v2.0.6
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
UAParser.js PRO Personal License */
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Source: /src/helpers/ua-parser-helpers.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Helpers for UAParser.js v2.0.5
|
||||
/* Helpers for UAParser.js v2.0.6
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
UAParser.js PRO Personal License */
|
||||
|
||||
3
src/main/ua-parser.d.ts
vendored
3
src/main/ua-parser.d.ts
vendored
@@ -1,8 +1,7 @@
|
||||
// Type definitions for UAParser.js v2.0.5
|
||||
// Type definitions for UAParser.js v2.0.6
|
||||
// Project: https://github.com/faisalman/ua-parser-js
|
||||
// Definitions by: Faisal Salman <https://github.com/faisalman>
|
||||
|
||||
import type { Headers } from "undici";
|
||||
import { BrowserType, CPUArch, DeviceType, EngineName } from "../enums/ua-parser-enums";
|
||||
|
||||
declare namespace UAParser {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/* UAParser.js v2.0.5
|
||||
/* UAParser.js v2.0.6
|
||||
Copyright © 2012-2025 Faisal Salman <f@faisalman.com>
|
||||
UAParser.js PRO Personal License *//*
|
||||
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
|
||||
@@ -19,26 +19,26 @@
|
||||
// Constants
|
||||
/////////////
|
||||
|
||||
var LIBVERSION = '2.0.5',
|
||||
var LIBVERSION = '2.0.6',
|
||||
UA_MAX_LENGTH = 500,
|
||||
USER_AGENT = 'user-agent',
|
||||
EMPTY = '',
|
||||
UNKNOWN = '?',
|
||||
|
||||
// typeof
|
||||
FUNC_TYPE = 'function',
|
||||
UNDEF_TYPE = 'undefined',
|
||||
OBJ_TYPE = 'object',
|
||||
STR_TYPE = 'string',
|
||||
TYPEOF = {
|
||||
FUNCTION : 'function',
|
||||
OBJECT : 'object',
|
||||
STRING : 'string',
|
||||
UNDEFINED : 'undefined'
|
||||
},
|
||||
|
||||
// properties
|
||||
UA_BROWSER = 'browser',
|
||||
UA_CPU = 'cpu',
|
||||
UA_DEVICE = 'device',
|
||||
UA_ENGINE = 'engine',
|
||||
UA_OS = 'os',
|
||||
UA_RESULT = 'result',
|
||||
|
||||
BROWSER = 'browser',
|
||||
CPU = 'cpu',
|
||||
DEVICE = 'device',
|
||||
ENGINE = 'engine',
|
||||
OS = 'os',
|
||||
RESULT = 'result',
|
||||
|
||||
NAME = 'name',
|
||||
TYPE = 'type',
|
||||
VENDOR = 'vendor',
|
||||
@@ -66,16 +66,16 @@
|
||||
PLATFORM = 'platform',
|
||||
PLATFORMVER = 'platformVersion',
|
||||
BITNESS = 'bitness',
|
||||
CH_HEADER = 'sec-ch-ua',
|
||||
CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list',
|
||||
CH_HEADER_ARCH = CH_HEADER + '-arch',
|
||||
CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS,
|
||||
CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors',
|
||||
CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE,
|
||||
CH_HEADER_MODEL = CH_HEADER + '-' + MODEL,
|
||||
CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM,
|
||||
CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version',
|
||||
CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS],
|
||||
CH = 'sec-ch-ua',
|
||||
CH_FULL_VER_LIST= CH + '-full-version-list',
|
||||
CH_ARCH = CH + '-arch',
|
||||
CH_BITNESS = CH + '-' + BITNESS,
|
||||
CH_FORM_FACTORS = CH + '-form-factors',
|
||||
CH_MOBILE = CH + '-' + MOBILE,
|
||||
CH_MODEL = CH + '-' + MODEL,
|
||||
CH_PLATFORM = CH + '-' + PLATFORM,
|
||||
CH_PLATFORM_VER = CH_PLATFORM + '-version',
|
||||
CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS],
|
||||
|
||||
// device vendors
|
||||
AMAZON = 'Amazon',
|
||||
@@ -114,7 +114,7 @@
|
||||
// os
|
||||
WINDOWS = 'Windows';
|
||||
|
||||
var isWindow = typeof window !== UNDEF_TYPE,
|
||||
var isWindow = typeof window !== TYPEOF.UNDEFINED,
|
||||
NAVIGATOR = (isWindow && window.navigator) ?
|
||||
window.navigator :
|
||||
undefined,
|
||||
@@ -150,7 +150,7 @@
|
||||
return enums;
|
||||
},
|
||||
has = function (str1, str2) {
|
||||
if (typeof str1 === OBJ_TYPE && str1.length > 0) {
|
||||
if (typeof str1 === TYPEOF.OBJECT && str1.length > 0) {
|
||||
for (var i in str1) {
|
||||
if (lowerize(str2) == lowerize(str1[i])) return true;
|
||||
}
|
||||
@@ -164,7 +164,7 @@
|
||||
}
|
||||
},
|
||||
isString = function (val) {
|
||||
return typeof val === STR_TYPE;
|
||||
return typeof val === TYPEOF.STRING;
|
||||
},
|
||||
itemListToArray = function (header) {
|
||||
if (!header) return undefined;
|
||||
@@ -191,7 +191,7 @@
|
||||
if (!arr.hasOwnProperty(i)) continue;
|
||||
|
||||
var propName = arr[i];
|
||||
if (typeof propName == OBJ_TYPE && propName.length == 2) {
|
||||
if (typeof propName == TYPEOF.OBJECT && propName.length == 2) {
|
||||
this[propName[0]] = propName[1];
|
||||
} else {
|
||||
this[propName] = undefined;
|
||||
@@ -206,10 +206,8 @@
|
||||
return strip(/\\?\"/g, str);
|
||||
},
|
||||
trim = function (str, len) {
|
||||
if (isString(str)) {
|
||||
str = strip(/^\s\s*/, str);
|
||||
return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
|
||||
}
|
||||
str = strip(/^\s\s*/, String(str));
|
||||
return typeof len === TYPEOF.UNDEFINED ? str : str.substring(0, len);
|
||||
};
|
||||
|
||||
///////////////
|
||||
@@ -240,9 +238,9 @@
|
||||
match = matches[++k];
|
||||
q = props[p];
|
||||
// check if given property is actually array
|
||||
if (typeof q === OBJ_TYPE && q.length > 0) {
|
||||
if (typeof q === TYPEOF.OBJECT && q.length > 0) {
|
||||
if (q.length === 2) {
|
||||
if (typeof q[1] == FUNC_TYPE) {
|
||||
if (typeof q[1] == TYPEOF.FUNCTION) {
|
||||
// assign modified match
|
||||
this[q[0]] = q[1].call(this, match);
|
||||
} else {
|
||||
@@ -251,7 +249,7 @@
|
||||
}
|
||||
} else if (q.length >= 3) {
|
||||
// Check whether q[1] FUNCTION or REGEX
|
||||
if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {
|
||||
if (typeof q[1] === TYPEOF.FUNCTION && !(q[1].exec && q[1].test)) {
|
||||
if (q.length > 3) {
|
||||
this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined;
|
||||
} else {
|
||||
@@ -283,7 +281,7 @@
|
||||
|
||||
for (var i in map) {
|
||||
// check if current value is array
|
||||
if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {
|
||||
if (typeof map[i] === TYPEOF.OBJECT && map[i].length > 0) {
|
||||
for (var j = 0; j < map[i].length; j++) {
|
||||
if (has(map[i][j], str)) {
|
||||
return (i === UNKNOWN) ? undefined : i;
|
||||
@@ -377,10 +375,11 @@
|
||||
/(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
|
||||
/(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:lg |qute)browser)\/([-\w\.]+)/i,
|
||||
// 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
|
||||
/(heytap|ovi|115|surf)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf
|
||||
/(heytap|ovi|115|surf|qwant)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf
|
||||
/(qwant)(?:ios|mobile)\/([\d\.]+)/i, // Qwant
|
||||
/(ecosia|weibo)(?:__| \w+@)([\d\.]+)/i // Ecosia/Weibo
|
||||
], [NAME, VERSION], [
|
||||
/quark(?:pc)?\/([-\w\.]+)/i // Quark
|
||||
@@ -449,6 +448,7 @@
|
||||
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
|
||||
/(alipay)client\/([\w\.]+)/i, // Alipay
|
||||
/(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter
|
||||
/(bing)(?:web|sapphire)\/([\w\.]+)/i, // Bing
|
||||
/(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna
|
||||
], [NAME, VERSION, [TYPE, INAPP]], [
|
||||
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
|
||||
@@ -506,10 +506,10 @@
|
||||
/(swiftfox)/i, // Swiftfox
|
||||
/(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i,
|
||||
// IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
|
||||
/(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i,
|
||||
/(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|basilisk|waterfox)\/([-\w\.]+)$/i,
|
||||
// Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
|
||||
/(firefox)\/([\w\.]+)/i, // Other Firefox-based
|
||||
/(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla
|
||||
/(mozilla)\/([\w\.]+(?= .+rv\:.+gecko\/\d+)|[0-4][\w\.]+(?!.+compatible))/i, // Mozilla
|
||||
|
||||
// Other
|
||||
/(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
|
||||
@@ -547,6 +547,8 @@
|
||||
/((ppc|powerpc)(64)?)( mac|;|\))/i, // PowerPC
|
||||
/(?:osf1|[freopnt]{3,4}bsd) (alpha)/i // Alpha
|
||||
], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [
|
||||
/mc680.0/i
|
||||
], [[ARCHITECTURE, '68k']], [
|
||||
/winnt.+\[axp/i
|
||||
], [[ARCHITECTURE, 'alpha']]
|
||||
],
|
||||
@@ -566,11 +568,10 @@
|
||||
], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [
|
||||
|
||||
// Apple
|
||||
/(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone
|
||||
/(?:\/|\()(ip(?:hone|od)[\w, ]*)[\/\);]/i // iPod/iPhone
|
||||
], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [
|
||||
/\((ipad);[-\w\),; ]+apple/i, // iPad
|
||||
/applecoremedia\/[\w\.]+ \((ipad)/i,
|
||||
/\b(ipad)\d\d?,\d\d?[;\]].+ios/i
|
||||
/\b(?:ios|apple\w+)\/.+[\(\/](ipad)/i, // iPad
|
||||
/\b(ipad)[\d,]*[;\] ].+(mac |i(pad)?)os/i
|
||||
], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [
|
||||
/(macintosh);/i
|
||||
], [MODEL, [VENDOR, APPLE]], [
|
||||
@@ -588,13 +589,13 @@
|
||||
// Huawei
|
||||
/\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i
|
||||
], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [
|
||||
/(?:huawei)([-\w ]+)[;\)]/i,
|
||||
/\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i
|
||||
/(?:huawei) ?([-\w ]+)[;\)]/i,
|
||||
/\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][\dc][adnt]?)\b(?!.+d\/s)/i
|
||||
], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
|
||||
|
||||
// Xiaomi
|
||||
/oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i,
|
||||
/\b((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
|
||||
/\b(?:xiao)?((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
|
||||
],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
|
||||
|
||||
/\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
|
||||
@@ -602,7 +603,7 @@
|
||||
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
|
||||
/\b(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 lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i, // Xiaomi Mi
|
||||
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i, // Xiaomi Mi
|
||||
/ ([\w ]+) miui\/v?\d/i
|
||||
], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
|
||||
|
||||
@@ -658,7 +659,7 @@
|
||||
/(nokia) (t[12][01])/i
|
||||
], [VENDOR, MODEL, [TYPE, TABLET]], [
|
||||
/(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i,
|
||||
/nokia[-_ ]?(([-\w\. ]*))/i
|
||||
/nokia[-_ ]?(([-\w\. ]*?))( bui|\)|;|\/)/i
|
||||
], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [
|
||||
|
||||
// Google
|
||||
@@ -689,7 +690,7 @@
|
||||
/(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook
|
||||
], [MODEL, VENDOR, [TYPE, TABLET]], [
|
||||
/\b((?:bb[a-f]|st[hv])100-\d)/i,
|
||||
/\(bb10; (\w+)/i // BlackBerry 10
|
||||
/(?:blackberry|\(bb10;) (\w+)/i
|
||||
], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [
|
||||
|
||||
// Asus
|
||||
@@ -769,10 +770,12 @@
|
||||
|
||||
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i,
|
||||
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan
|
||||
/; (blu|hmd|imo|infinix|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL
|
||||
// BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL/Wiko
|
||||
/; (blu|hmd|imo|infinix|lava|oneplus|tcl|wiko)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i,
|
||||
/(hp) ([\w ]+\w)/i, // HP iPAQ
|
||||
/(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia
|
||||
/(oppo) ?([\w ]+) bui/i, // OPPO
|
||||
/(hisense) ([ehv][\w ]+)\)/i, // Hisense
|
||||
/droid[^;]+; (philips)[_ ]([sv-x][\d]{3,4}[xz]?)/i // Philips
|
||||
], [VENDOR, MODEL, [TYPE, MOBILE]], [
|
||||
|
||||
@@ -912,7 +915,7 @@
|
||||
], [MODEL, [TYPE, SMARTTV]], [
|
||||
/\b((4k|android|smart|opera)[- ]?tv|tv; rv:|large screen[\w ]+safari)\b/i
|
||||
], [[TYPE, SMARTTV]], [
|
||||
/droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+?(mobile|vr|\d) safari/i
|
||||
/droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew|; hmsc).+?(mobile|vr|\d) safari/i
|
||||
], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [
|
||||
/\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet
|
||||
], [[TYPE, TABLET]], [
|
||||
@@ -965,7 +968,7 @@
|
||||
|
||||
// iOS/macOS
|
||||
/[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
|
||||
/(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
|
||||
/(?:ios;fbsv|ios(?=.+ip(?:ad|hone))|ip(?:ad|hone)(?: |.+i(?:pad)?)os)[\/ ]([\w\.]+)/i,
|
||||
/cfnetwork\/.+darwin/i
|
||||
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
|
||||
/(mac os x) ?([\w\. ]*)/i,
|
||||
@@ -1050,27 +1053,27 @@
|
||||
var defaultProps = (function () {
|
||||
var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
|
||||
setProps.call(props.init, [
|
||||
[UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]],
|
||||
[UA_CPU, [ARCHITECTURE]],
|
||||
[UA_DEVICE, [TYPE, MODEL, VENDOR]],
|
||||
[UA_ENGINE, [NAME, VERSION]],
|
||||
[UA_OS, [NAME, VERSION]]
|
||||
[BROWSER, [NAME, VERSION, MAJOR, TYPE]],
|
||||
[CPU, [ARCHITECTURE]],
|
||||
[DEVICE, [TYPE, MODEL, VENDOR]],
|
||||
[ENGINE, [NAME, VERSION]],
|
||||
[OS, [NAME, VERSION]]
|
||||
]);
|
||||
setProps.call(props.isIgnore, [
|
||||
[UA_BROWSER, [VERSION, MAJOR]],
|
||||
[UA_ENGINE, [VERSION]],
|
||||
[UA_OS, [VERSION]]
|
||||
[BROWSER, [VERSION, MAJOR]],
|
||||
[ENGINE, [VERSION]],
|
||||
[OS, [VERSION]]
|
||||
]);
|
||||
setProps.call(props.isIgnoreRgx, [
|
||||
[UA_BROWSER, / ?browser$/i],
|
||||
[UA_OS, / ?os$/i]
|
||||
[BROWSER, / ?browser$/i],
|
||||
[OS, / ?os$/i]
|
||||
]);
|
||||
setProps.call(props.toString, [
|
||||
[UA_BROWSER, [NAME, VERSION]],
|
||||
[UA_CPU, [ARCHITECTURE]],
|
||||
[UA_DEVICE, [VENDOR, MODEL]],
|
||||
[UA_ENGINE, [NAME, VERSION]],
|
||||
[UA_OS, [NAME, VERSION]]
|
||||
[BROWSER, [NAME, VERSION]],
|
||||
[CPU, [ARCHITECTURE]],
|
||||
[DEVICE, [VENDOR, MODEL]],
|
||||
[ENGINE, [NAME, VERSION]],
|
||||
[OS, [NAME, VERSION]]
|
||||
]);
|
||||
return props;
|
||||
})();
|
||||
@@ -1114,14 +1117,14 @@
|
||||
return item.detectFeature().get();
|
||||
};
|
||||
|
||||
if (itemType != UA_RESULT) {
|
||||
if (itemType != RESULT) {
|
||||
IData.prototype.is = function (strToCheck) {
|
||||
var is = false;
|
||||
for (var i in this) {
|
||||
if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) {
|
||||
is = true;
|
||||
if (strToCheck != UNDEF_TYPE) break;
|
||||
} else if (strToCheck == UNDEF_TYPE && is) {
|
||||
if (strToCheck != TYPEOF.UNDEFINED) break;
|
||||
} else if (strToCheck == TYPEOF.UNDEFINED && is) {
|
||||
is = !is;
|
||||
break;
|
||||
}
|
||||
@@ -1131,11 +1134,11 @@
|
||||
IData.prototype.toString = function () {
|
||||
var str = EMPTY;
|
||||
for (var i in toString_props) {
|
||||
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
|
||||
if (typeof(this[toString_props[i]]) !== TYPEOF.UNDEFINED) {
|
||||
str += (str ? ' ' : EMPTY) + this[toString_props[i]];
|
||||
}
|
||||
}
|
||||
return str || UNDEF_TYPE;
|
||||
return str || TYPEOF.UNDEFINED;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1171,19 +1174,19 @@
|
||||
setProps.call(this, CH_ALL_VALUES);
|
||||
if (isHttpUACH) {
|
||||
setProps.call(this, [
|
||||
[BRANDS, itemListToArray(uach[CH_HEADER])],
|
||||
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
|
||||
[MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
|
||||
[MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
|
||||
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
|
||||
[PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
|
||||
[ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
|
||||
[FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])],
|
||||
[BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
|
||||
[BRANDS, itemListToArray(uach[CH])],
|
||||
[FULLVERLIST, itemListToArray(uach[CH_FULL_VER_LIST])],
|
||||
[MOBILE, /\?1/.test(uach[CH_MOBILE])],
|
||||
[MODEL, stripQuotes(uach[CH_MODEL])],
|
||||
[PLATFORM, stripQuotes(uach[CH_PLATFORM])],
|
||||
[PLATFORMVER, stripQuotes(uach[CH_PLATFORM_VER])],
|
||||
[ARCHITECTURE, stripQuotes(uach[CH_ARCH])],
|
||||
[FORMFACTORS, itemListToArray(uach[CH_FORM_FACTORS])],
|
||||
[BITNESS, stripQuotes(uach[CH_BITNESS])]
|
||||
]);
|
||||
} else {
|
||||
for (var prop in uach) {
|
||||
if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop];
|
||||
if(this.hasOwnProperty(prop) && typeof uach[prop] !== TYPEOF.UNDEFINED) this[prop] = uach[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1208,30 +1211,30 @@
|
||||
this.detectFeature = function () {
|
||||
if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
|
||||
switch (this.itemType) {
|
||||
case UA_BROWSER:
|
||||
case BROWSER:
|
||||
// Brave-specific detection
|
||||
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
|
||||
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
|
||||
this.set(NAME, 'Brave');
|
||||
}
|
||||
break;
|
||||
case UA_DEVICE:
|
||||
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 !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
|
||||
if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
|
||||
this.set(MODEL, 'iPad')
|
||||
.set(TYPE, TABLET);
|
||||
}
|
||||
break;
|
||||
case UA_OS:
|
||||
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 UA_RESULT:
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var detect = function (itemType) {
|
||||
return data[itemType]
|
||||
@@ -1239,22 +1242,33 @@
|
||||
.detectFeature()
|
||||
.get();
|
||||
};
|
||||
this.set(UA_BROWSER, detect(UA_BROWSER))
|
||||
.set(UA_CPU, detect(UA_CPU))
|
||||
.set(UA_DEVICE, detect(UA_DEVICE))
|
||||
.set(UA_ENGINE, detect(UA_ENGINE))
|
||||
.set(UA_OS, detect(UA_OS));
|
||||
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 != UA_RESULT) {
|
||||
if (this.itemType != RESULT) {
|
||||
rgxMapper.call(this.data, this.ua, this.rgxMap);
|
||||
}
|
||||
if (this.itemType == UA_BROWSER) {
|
||||
this.set(MAJOR, majorize(this.get(VERSION)));
|
||||
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;
|
||||
};
|
||||
@@ -1264,14 +1278,14 @@
|
||||
rgxMap = this.rgxMap;
|
||||
|
||||
switch (this.itemType) {
|
||||
case UA_BROWSER:
|
||||
case UA_ENGINE:
|
||||
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 == UA_BROWSER &&
|
||||
if (this.itemType == BROWSER &&
|
||||
!/not.a.brand/i.test(brandName) &&
|
||||
(!prevName ||
|
||||
(/Chrom/.test(prevName) && brandName != CHROMIUM) ||
|
||||
@@ -1286,20 +1300,20 @@
|
||||
}
|
||||
prevName = brandName;
|
||||
}
|
||||
if (this.itemType == UA_ENGINE && brandName == CHROMIUM) {
|
||||
if (this.itemType == ENGINE && brandName == CHROMIUM) {
|
||||
this.set(VERSION, brandVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UA_CPU:
|
||||
case CPU:
|
||||
var archName = uaCH[ARCHITECTURE];
|
||||
if (archName) {
|
||||
if (archName && uaCH[BITNESS] == '64') archName += '64';
|
||||
rgxMapper.call(this.data, archName + ';', rgxMap);
|
||||
}
|
||||
break;
|
||||
case UA_DEVICE:
|
||||
case DEVICE:
|
||||
if (uaCH[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
@@ -1329,7 +1343,7 @@
|
||||
this.set(TYPE, ff);
|
||||
}
|
||||
break;
|
||||
case UA_OS:
|
||||
case OS:
|
||||
var osName = uaCH[PLATFORM];
|
||||
if(osName) {
|
||||
var osVersion = uaCH[PLATFORMVER];
|
||||
@@ -1343,7 +1357,7 @@
|
||||
.set(VERSION, undefined);
|
||||
}
|
||||
break;
|
||||
case UA_RESULT:
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var parse = function (itemType) {
|
||||
return data[itemType]
|
||||
@@ -1352,11 +1366,11 @@
|
||||
.parseCH()
|
||||
.get();
|
||||
};
|
||||
this.set(UA_BROWSER, parse(UA_BROWSER))
|
||||
.set(UA_CPU, parse(UA_CPU))
|
||||
.set(UA_DEVICE, parse(UA_DEVICE))
|
||||
.set(UA_ENGINE, parse(UA_ENGINE))
|
||||
.set(UA_OS, parse(UA_OS));
|
||||
this.set(BROWSER, parse(BROWSER))
|
||||
.set(CPU, parse(CPU))
|
||||
.set(DEVICE, parse(DEVICE))
|
||||
.set(ENGINE, parse(ENGINE))
|
||||
.set(OS, parse(OS));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
@@ -1374,9 +1388,9 @@
|
||||
|
||||
function UAParser (ua, extensions, headers) {
|
||||
|
||||
if (typeof ua === OBJ_TYPE) {
|
||||
if (typeof ua === TYPEOF.OBJECT) {
|
||||
if (isExtensions(ua, true)) {
|
||||
if (typeof extensions === OBJ_TYPE) {
|
||||
if (typeof extensions === TYPEOF.OBJECT) {
|
||||
headers = extensions; // case UAParser(extensions, headers)
|
||||
}
|
||||
extensions = ua; // case UAParser(extensions)
|
||||
@@ -1385,13 +1399,13 @@
|
||||
extensions = undefined;
|
||||
}
|
||||
ua = undefined;
|
||||
} else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) {
|
||||
} else if (typeof ua === TYPEOF.STRING && !isExtensions(extensions, true)) {
|
||||
headers = extensions; // case UAParser(ua, headers)
|
||||
extensions = undefined;
|
||||
}
|
||||
|
||||
if (headers) {
|
||||
if (typeof headers.append === FUNC_TYPE) {
|
||||
if (typeof headers.append === TYPEOF.FUNCTION) {
|
||||
// Convert Headers object into a plain object
|
||||
var kv = {};
|
||||
headers.forEach(function (v, k) { kv[String(k).toLowerCase()] = v; });
|
||||
@@ -1412,7 +1426,7 @@
|
||||
return new UAParser(ua, extensions, headers).getResult();
|
||||
}
|
||||
|
||||
var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string
|
||||
var userAgent = typeof ua === TYPEOF.STRING ? ua : // Passed user-agent string
|
||||
(headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers
|
||||
((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent
|
||||
EMPTY)), // empty string
|
||||
@@ -1423,15 +1437,15 @@
|
||||
defaultRegexes,
|
||||
|
||||
createItemFunc = function (itemType) {
|
||||
if (itemType == UA_RESULT) {
|
||||
if (itemType == RESULT) {
|
||||
return function () {
|
||||
return new UAItem(itemType, userAgent, regexMap, httpUACH)
|
||||
.set('ua', userAgent)
|
||||
.set(UA_BROWSER, this.getBrowser())
|
||||
.set(UA_CPU, this.getCPU())
|
||||
.set(UA_DEVICE, this.getDevice())
|
||||
.set(UA_ENGINE, this.getEngine())
|
||||
.set(UA_OS, this.getOS())
|
||||
.set(BROWSER, this.getBrowser())
|
||||
.set(CPU, this.getCPU())
|
||||
.set(DEVICE, this.getDevice())
|
||||
.set(ENGINE, this.getEngine())
|
||||
.set(OS, this.getOS())
|
||||
.get();
|
||||
};
|
||||
} else {
|
||||
@@ -1445,16 +1459,15 @@
|
||||
|
||||
// public methods
|
||||
setProps.call(this, [
|
||||
['getBrowser', createItemFunc(UA_BROWSER)],
|
||||
['getCPU', createItemFunc(UA_CPU)],
|
||||
['getDevice', createItemFunc(UA_DEVICE)],
|
||||
['getEngine', createItemFunc(UA_ENGINE)],
|
||||
['getOS', createItemFunc(UA_OS)],
|
||||
['getResult', createItemFunc(UA_RESULT)],
|
||||
['getBrowser', createItemFunc(BROWSER)],
|
||||
['getCPU', createItemFunc(CPU)],
|
||||
['getDevice', createItemFunc(DEVICE)],
|
||||
['getEngine', createItemFunc(ENGINE)],
|
||||
['getOS', createItemFunc(OS)],
|
||||
['getResult', createItemFunc(RESULT)],
|
||||
['getUA', function () { return userAgent; }],
|
||||
['setUA', function (ua) {
|
||||
if (isString(ua))
|
||||
userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
|
||||
if (isString(ua)) userAgent = trim(ua, UA_MAX_LENGTH);
|
||||
return this;
|
||||
}]
|
||||
])
|
||||
@@ -1474,15 +1487,15 @@
|
||||
//////////
|
||||
|
||||
// check js environment
|
||||
if (typeof exports !== UNDEF_TYPE) {
|
||||
if (typeof exports !== TYPEOF.UNDEFINED) {
|
||||
// nodejs env
|
||||
if (typeof module !== UNDEF_TYPE && module.exports) {
|
||||
if (typeof module !== TYPEOF.UNDEFINED && module.exports) {
|
||||
exports = module.exports = UAParser;
|
||||
}
|
||||
exports.UAParser = UAParser;
|
||||
} else {
|
||||
// requirejs env (optional)
|
||||
if (typeof define === FUNC_TYPE && define.amd) {
|
||||
if (typeof define === TYPEOF.FUNCTION && define.amd) {
|
||||
define(function () {
|
||||
return UAParser;
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// Source: /src/main/ua-parser.js
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/* UAParser.js v2.0.5
|
||||
/* UAParser.js v2.0.6
|
||||
Copyright © 2012-2025 Faisal Salman <f@faisalman.com>
|
||||
UAParser.js PRO Personal License *//*
|
||||
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
|
||||
@@ -21,26 +21,26 @@
|
||||
// Constants
|
||||
/////////////
|
||||
|
||||
var LIBVERSION = '2.0.5',
|
||||
var LIBVERSION = '2.0.6',
|
||||
UA_MAX_LENGTH = 500,
|
||||
USER_AGENT = 'user-agent',
|
||||
EMPTY = '',
|
||||
UNKNOWN = '?',
|
||||
|
||||
// typeof
|
||||
FUNC_TYPE = 'function',
|
||||
UNDEF_TYPE = 'undefined',
|
||||
OBJ_TYPE = 'object',
|
||||
STR_TYPE = 'string',
|
||||
TYPEOF = {
|
||||
FUNCTION : 'function',
|
||||
OBJECT : 'object',
|
||||
STRING : 'string',
|
||||
UNDEFINED : 'undefined'
|
||||
},
|
||||
|
||||
// properties
|
||||
UA_BROWSER = 'browser',
|
||||
UA_CPU = 'cpu',
|
||||
UA_DEVICE = 'device',
|
||||
UA_ENGINE = 'engine',
|
||||
UA_OS = 'os',
|
||||
UA_RESULT = 'result',
|
||||
|
||||
BROWSER = 'browser',
|
||||
CPU = 'cpu',
|
||||
DEVICE = 'device',
|
||||
ENGINE = 'engine',
|
||||
OS = 'os',
|
||||
RESULT = 'result',
|
||||
|
||||
NAME = 'name',
|
||||
TYPE = 'type',
|
||||
VENDOR = 'vendor',
|
||||
@@ -68,16 +68,16 @@
|
||||
PLATFORM = 'platform',
|
||||
PLATFORMVER = 'platformVersion',
|
||||
BITNESS = 'bitness',
|
||||
CH_HEADER = 'sec-ch-ua',
|
||||
CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list',
|
||||
CH_HEADER_ARCH = CH_HEADER + '-arch',
|
||||
CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS,
|
||||
CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors',
|
||||
CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE,
|
||||
CH_HEADER_MODEL = CH_HEADER + '-' + MODEL,
|
||||
CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM,
|
||||
CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version',
|
||||
CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS],
|
||||
CH = 'sec-ch-ua',
|
||||
CH_FULL_VER_LIST= CH + '-full-version-list',
|
||||
CH_ARCH = CH + '-arch',
|
||||
CH_BITNESS = CH + '-' + BITNESS,
|
||||
CH_FORM_FACTORS = CH + '-form-factors',
|
||||
CH_MOBILE = CH + '-' + MOBILE,
|
||||
CH_MODEL = CH + '-' + MODEL,
|
||||
CH_PLATFORM = CH + '-' + PLATFORM,
|
||||
CH_PLATFORM_VER = CH_PLATFORM + '-version',
|
||||
CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS],
|
||||
|
||||
// device vendors
|
||||
AMAZON = 'Amazon',
|
||||
@@ -116,7 +116,7 @@
|
||||
// os
|
||||
WINDOWS = 'Windows';
|
||||
|
||||
var isWindow = typeof window !== UNDEF_TYPE,
|
||||
var isWindow = typeof window !== TYPEOF.UNDEFINED,
|
||||
NAVIGATOR = (isWindow && window.navigator) ?
|
||||
window.navigator :
|
||||
undefined,
|
||||
@@ -152,7 +152,7 @@
|
||||
return enums;
|
||||
},
|
||||
has = function (str1, str2) {
|
||||
if (typeof str1 === OBJ_TYPE && str1.length > 0) {
|
||||
if (typeof str1 === TYPEOF.OBJECT && str1.length > 0) {
|
||||
for (var i in str1) {
|
||||
if (lowerize(str2) == lowerize(str1[i])) return true;
|
||||
}
|
||||
@@ -166,7 +166,7 @@
|
||||
}
|
||||
},
|
||||
isString = function (val) {
|
||||
return typeof val === STR_TYPE;
|
||||
return typeof val === TYPEOF.STRING;
|
||||
},
|
||||
itemListToArray = function (header) {
|
||||
if (!header) return undefined;
|
||||
@@ -193,7 +193,7 @@
|
||||
if (!arr.hasOwnProperty(i)) continue;
|
||||
|
||||
var propName = arr[i];
|
||||
if (typeof propName == OBJ_TYPE && propName.length == 2) {
|
||||
if (typeof propName == TYPEOF.OBJECT && propName.length == 2) {
|
||||
this[propName[0]] = propName[1];
|
||||
} else {
|
||||
this[propName] = undefined;
|
||||
@@ -208,10 +208,8 @@
|
||||
return strip(/\\?\"/g, str);
|
||||
},
|
||||
trim = function (str, len) {
|
||||
if (isString(str)) {
|
||||
str = strip(/^\s\s*/, str);
|
||||
return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
|
||||
}
|
||||
str = strip(/^\s\s*/, String(str));
|
||||
return typeof len === TYPEOF.UNDEFINED ? str : str.substring(0, len);
|
||||
};
|
||||
|
||||
///////////////
|
||||
@@ -242,9 +240,9 @@
|
||||
match = matches[++k];
|
||||
q = props[p];
|
||||
// check if given property is actually array
|
||||
if (typeof q === OBJ_TYPE && q.length > 0) {
|
||||
if (typeof q === TYPEOF.OBJECT && q.length > 0) {
|
||||
if (q.length === 2) {
|
||||
if (typeof q[1] == FUNC_TYPE) {
|
||||
if (typeof q[1] == TYPEOF.FUNCTION) {
|
||||
// assign modified match
|
||||
this[q[0]] = q[1].call(this, match);
|
||||
} else {
|
||||
@@ -253,7 +251,7 @@
|
||||
}
|
||||
} else if (q.length >= 3) {
|
||||
// Check whether q[1] FUNCTION or REGEX
|
||||
if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {
|
||||
if (typeof q[1] === TYPEOF.FUNCTION && !(q[1].exec && q[1].test)) {
|
||||
if (q.length > 3) {
|
||||
this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined;
|
||||
} else {
|
||||
@@ -285,7 +283,7 @@
|
||||
|
||||
for (var i in map) {
|
||||
// check if current value is array
|
||||
if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {
|
||||
if (typeof map[i] === TYPEOF.OBJECT && map[i].length > 0) {
|
||||
for (var j = 0; j < map[i].length; j++) {
|
||||
if (has(map[i][j], str)) {
|
||||
return (i === UNKNOWN) ? undefined : i;
|
||||
@@ -379,10 +377,11 @@
|
||||
/(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
|
||||
/(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:lg |qute)browser)\/([-\w\.]+)/i,
|
||||
// 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
|
||||
/(heytap|ovi|115|surf)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf
|
||||
/(heytap|ovi|115|surf|qwant)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf
|
||||
/(qwant)(?:ios|mobile)\/([\d\.]+)/i, // Qwant
|
||||
/(ecosia|weibo)(?:__| \w+@)([\d\.]+)/i // Ecosia/Weibo
|
||||
], [NAME, VERSION], [
|
||||
/quark(?:pc)?\/([-\w\.]+)/i // Quark
|
||||
@@ -451,6 +450,7 @@
|
||||
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
|
||||
/(alipay)client\/([\w\.]+)/i, // Alipay
|
||||
/(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter
|
||||
/(bing)(?:web|sapphire)\/([\w\.]+)/i, // Bing
|
||||
/(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna
|
||||
], [NAME, VERSION, [TYPE, INAPP]], [
|
||||
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
|
||||
@@ -508,10 +508,10 @@
|
||||
/(swiftfox)/i, // Swiftfox
|
||||
/(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i,
|
||||
// IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
|
||||
/(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i,
|
||||
/(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|basilisk|waterfox)\/([-\w\.]+)$/i,
|
||||
// Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
|
||||
/(firefox)\/([\w\.]+)/i, // Other Firefox-based
|
||||
/(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla
|
||||
/(mozilla)\/([\w\.]+(?= .+rv\:.+gecko\/\d+)|[0-4][\w\.]+(?!.+compatible))/i, // Mozilla
|
||||
|
||||
// Other
|
||||
/(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
|
||||
@@ -549,6 +549,8 @@
|
||||
/((ppc|powerpc)(64)?)( mac|;|\))/i, // PowerPC
|
||||
/(?:osf1|[freopnt]{3,4}bsd) (alpha)/i // Alpha
|
||||
], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [
|
||||
/mc680.0/i
|
||||
], [[ARCHITECTURE, '68k']], [
|
||||
/winnt.+\[axp/i
|
||||
], [[ARCHITECTURE, 'alpha']]
|
||||
],
|
||||
@@ -568,11 +570,10 @@
|
||||
], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [
|
||||
|
||||
// Apple
|
||||
/(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone
|
||||
/(?:\/|\()(ip(?:hone|od)[\w, ]*)[\/\);]/i // iPod/iPhone
|
||||
], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [
|
||||
/\((ipad);[-\w\),; ]+apple/i, // iPad
|
||||
/applecoremedia\/[\w\.]+ \((ipad)/i,
|
||||
/\b(ipad)\d\d?,\d\d?[;\]].+ios/i
|
||||
/\b(?:ios|apple\w+)\/.+[\(\/](ipad)/i, // iPad
|
||||
/\b(ipad)[\d,]*[;\] ].+(mac |i(pad)?)os/i
|
||||
], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [
|
||||
/(macintosh);/i
|
||||
], [MODEL, [VENDOR, APPLE]], [
|
||||
@@ -590,13 +591,13 @@
|
||||
// Huawei
|
||||
/\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i
|
||||
], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [
|
||||
/(?:huawei)([-\w ]+)[;\)]/i,
|
||||
/\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i
|
||||
/(?:huawei) ?([-\w ]+)[;\)]/i,
|
||||
/\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][\dc][adnt]?)\b(?!.+d\/s)/i
|
||||
], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
|
||||
|
||||
// Xiaomi
|
||||
/oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i,
|
||||
/\b((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
|
||||
/\b(?:xiao)?((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets
|
||||
],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
|
||||
|
||||
/\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
|
||||
@@ -604,7 +605,7 @@
|
||||
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
|
||||
/\b(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 lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i, // Xiaomi Mi
|
||||
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i, // Xiaomi Mi
|
||||
/ ([\w ]+) miui\/v?\d/i
|
||||
], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
|
||||
|
||||
@@ -660,7 +661,7 @@
|
||||
/(nokia) (t[12][01])/i
|
||||
], [VENDOR, MODEL, [TYPE, TABLET]], [
|
||||
/(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i,
|
||||
/nokia[-_ ]?(([-\w\. ]*))/i
|
||||
/nokia[-_ ]?(([-\w\. ]*?))( bui|\)|;|\/)/i
|
||||
], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [
|
||||
|
||||
// Google
|
||||
@@ -691,7 +692,7 @@
|
||||
/(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook
|
||||
], [MODEL, VENDOR, [TYPE, TABLET]], [
|
||||
/\b((?:bb[a-f]|st[hv])100-\d)/i,
|
||||
/\(bb10; (\w+)/i // BlackBerry 10
|
||||
/(?:blackberry|\(bb10;) (\w+)/i
|
||||
], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [
|
||||
|
||||
// Asus
|
||||
@@ -771,10 +772,12 @@
|
||||
|
||||
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i,
|
||||
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan
|
||||
/; (blu|hmd|imo|infinix|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL
|
||||
// BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL/Wiko
|
||||
/; (blu|hmd|imo|infinix|lava|oneplus|tcl|wiko)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i,
|
||||
/(hp) ([\w ]+\w)/i, // HP iPAQ
|
||||
/(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia
|
||||
/(oppo) ?([\w ]+) bui/i, // OPPO
|
||||
/(hisense) ([ehv][\w ]+)\)/i, // Hisense
|
||||
/droid[^;]+; (philips)[_ ]([sv-x][\d]{3,4}[xz]?)/i // Philips
|
||||
], [VENDOR, MODEL, [TYPE, MOBILE]], [
|
||||
|
||||
@@ -914,7 +917,7 @@
|
||||
], [MODEL, [TYPE, SMARTTV]], [
|
||||
/\b((4k|android|smart|opera)[- ]?tv|tv; rv:|large screen[\w ]+safari)\b/i
|
||||
], [[TYPE, SMARTTV]], [
|
||||
/droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+?(mobile|vr|\d) safari/i
|
||||
/droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew|; hmsc).+?(mobile|vr|\d) safari/i
|
||||
], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [
|
||||
/\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet
|
||||
], [[TYPE, TABLET]], [
|
||||
@@ -967,7 +970,7 @@
|
||||
|
||||
// iOS/macOS
|
||||
/[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
|
||||
/(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
|
||||
/(?:ios;fbsv|ios(?=.+ip(?:ad|hone))|ip(?:ad|hone)(?: |.+i(?:pad)?)os)[\/ ]([\w\.]+)/i,
|
||||
/cfnetwork\/.+darwin/i
|
||||
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
|
||||
/(mac os x) ?([\w\. ]*)/i,
|
||||
@@ -1052,27 +1055,27 @@
|
||||
var defaultProps = (function () {
|
||||
var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
|
||||
setProps.call(props.init, [
|
||||
[UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]],
|
||||
[UA_CPU, [ARCHITECTURE]],
|
||||
[UA_DEVICE, [TYPE, MODEL, VENDOR]],
|
||||
[UA_ENGINE, [NAME, VERSION]],
|
||||
[UA_OS, [NAME, VERSION]]
|
||||
[BROWSER, [NAME, VERSION, MAJOR, TYPE]],
|
||||
[CPU, [ARCHITECTURE]],
|
||||
[DEVICE, [TYPE, MODEL, VENDOR]],
|
||||
[ENGINE, [NAME, VERSION]],
|
||||
[OS, [NAME, VERSION]]
|
||||
]);
|
||||
setProps.call(props.isIgnore, [
|
||||
[UA_BROWSER, [VERSION, MAJOR]],
|
||||
[UA_ENGINE, [VERSION]],
|
||||
[UA_OS, [VERSION]]
|
||||
[BROWSER, [VERSION, MAJOR]],
|
||||
[ENGINE, [VERSION]],
|
||||
[OS, [VERSION]]
|
||||
]);
|
||||
setProps.call(props.isIgnoreRgx, [
|
||||
[UA_BROWSER, / ?browser$/i],
|
||||
[UA_OS, / ?os$/i]
|
||||
[BROWSER, / ?browser$/i],
|
||||
[OS, / ?os$/i]
|
||||
]);
|
||||
setProps.call(props.toString, [
|
||||
[UA_BROWSER, [NAME, VERSION]],
|
||||
[UA_CPU, [ARCHITECTURE]],
|
||||
[UA_DEVICE, [VENDOR, MODEL]],
|
||||
[UA_ENGINE, [NAME, VERSION]],
|
||||
[UA_OS, [NAME, VERSION]]
|
||||
[BROWSER, [NAME, VERSION]],
|
||||
[CPU, [ARCHITECTURE]],
|
||||
[DEVICE, [VENDOR, MODEL]],
|
||||
[ENGINE, [NAME, VERSION]],
|
||||
[OS, [NAME, VERSION]]
|
||||
]);
|
||||
return props;
|
||||
})();
|
||||
@@ -1116,14 +1119,14 @@
|
||||
return item.detectFeature().get();
|
||||
};
|
||||
|
||||
if (itemType != UA_RESULT) {
|
||||
if (itemType != RESULT) {
|
||||
IData.prototype.is = function (strToCheck) {
|
||||
var is = false;
|
||||
for (var i in this) {
|
||||
if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) {
|
||||
is = true;
|
||||
if (strToCheck != UNDEF_TYPE) break;
|
||||
} else if (strToCheck == UNDEF_TYPE && is) {
|
||||
if (strToCheck != TYPEOF.UNDEFINED) break;
|
||||
} else if (strToCheck == TYPEOF.UNDEFINED && is) {
|
||||
is = !is;
|
||||
break;
|
||||
}
|
||||
@@ -1133,11 +1136,11 @@
|
||||
IData.prototype.toString = function () {
|
||||
var str = EMPTY;
|
||||
for (var i in toString_props) {
|
||||
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
|
||||
if (typeof(this[toString_props[i]]) !== TYPEOF.UNDEFINED) {
|
||||
str += (str ? ' ' : EMPTY) + this[toString_props[i]];
|
||||
}
|
||||
}
|
||||
return str || UNDEF_TYPE;
|
||||
return str || TYPEOF.UNDEFINED;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1173,19 +1176,19 @@
|
||||
setProps.call(this, CH_ALL_VALUES);
|
||||
if (isHttpUACH) {
|
||||
setProps.call(this, [
|
||||
[BRANDS, itemListToArray(uach[CH_HEADER])],
|
||||
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
|
||||
[MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
|
||||
[MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
|
||||
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
|
||||
[PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
|
||||
[ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
|
||||
[FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])],
|
||||
[BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
|
||||
[BRANDS, itemListToArray(uach[CH])],
|
||||
[FULLVERLIST, itemListToArray(uach[CH_FULL_VER_LIST])],
|
||||
[MOBILE, /\?1/.test(uach[CH_MOBILE])],
|
||||
[MODEL, stripQuotes(uach[CH_MODEL])],
|
||||
[PLATFORM, stripQuotes(uach[CH_PLATFORM])],
|
||||
[PLATFORMVER, stripQuotes(uach[CH_PLATFORM_VER])],
|
||||
[ARCHITECTURE, stripQuotes(uach[CH_ARCH])],
|
||||
[FORMFACTORS, itemListToArray(uach[CH_FORM_FACTORS])],
|
||||
[BITNESS, stripQuotes(uach[CH_BITNESS])]
|
||||
]);
|
||||
} else {
|
||||
for (var prop in uach) {
|
||||
if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop];
|
||||
if(this.hasOwnProperty(prop) && typeof uach[prop] !== TYPEOF.UNDEFINED) this[prop] = uach[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1210,30 +1213,30 @@
|
||||
this.detectFeature = function () {
|
||||
if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
|
||||
switch (this.itemType) {
|
||||
case UA_BROWSER:
|
||||
case BROWSER:
|
||||
// Brave-specific detection
|
||||
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
|
||||
if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == TYPEOF.FUNCTION) {
|
||||
this.set(NAME, 'Brave');
|
||||
}
|
||||
break;
|
||||
case UA_DEVICE:
|
||||
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 !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
|
||||
if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== TYPEOF.UNDEFINED && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
|
||||
this.set(MODEL, 'iPad')
|
||||
.set(TYPE, TABLET);
|
||||
}
|
||||
break;
|
||||
case UA_OS:
|
||||
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 UA_RESULT:
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var detect = function (itemType) {
|
||||
return data[itemType]
|
||||
@@ -1241,22 +1244,33 @@
|
||||
.detectFeature()
|
||||
.get();
|
||||
};
|
||||
this.set(UA_BROWSER, detect(UA_BROWSER))
|
||||
.set(UA_CPU, detect(UA_CPU))
|
||||
.set(UA_DEVICE, detect(UA_DEVICE))
|
||||
.set(UA_ENGINE, detect(UA_ENGINE))
|
||||
.set(UA_OS, detect(UA_OS));
|
||||
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 != UA_RESULT) {
|
||||
if (this.itemType != RESULT) {
|
||||
rgxMapper.call(this.data, this.ua, this.rgxMap);
|
||||
}
|
||||
if (this.itemType == UA_BROWSER) {
|
||||
this.set(MAJOR, majorize(this.get(VERSION)));
|
||||
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;
|
||||
};
|
||||
@@ -1266,14 +1280,14 @@
|
||||
rgxMap = this.rgxMap;
|
||||
|
||||
switch (this.itemType) {
|
||||
case UA_BROWSER:
|
||||
case UA_ENGINE:
|
||||
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 == UA_BROWSER &&
|
||||
if (this.itemType == BROWSER &&
|
||||
!/not.a.brand/i.test(brandName) &&
|
||||
(!prevName ||
|
||||
(/Chrom/.test(prevName) && brandName != CHROMIUM) ||
|
||||
@@ -1288,20 +1302,20 @@
|
||||
}
|
||||
prevName = brandName;
|
||||
}
|
||||
if (this.itemType == UA_ENGINE && brandName == CHROMIUM) {
|
||||
if (this.itemType == ENGINE && brandName == CHROMIUM) {
|
||||
this.set(VERSION, brandVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UA_CPU:
|
||||
case CPU:
|
||||
var archName = uaCH[ARCHITECTURE];
|
||||
if (archName) {
|
||||
if (archName && uaCH[BITNESS] == '64') archName += '64';
|
||||
rgxMapper.call(this.data, archName + ';', rgxMap);
|
||||
}
|
||||
break;
|
||||
case UA_DEVICE:
|
||||
case DEVICE:
|
||||
if (uaCH[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
@@ -1331,7 +1345,7 @@
|
||||
this.set(TYPE, ff);
|
||||
}
|
||||
break;
|
||||
case UA_OS:
|
||||
case OS:
|
||||
var osName = uaCH[PLATFORM];
|
||||
if(osName) {
|
||||
var osVersion = uaCH[PLATFORMVER];
|
||||
@@ -1345,7 +1359,7 @@
|
||||
.set(VERSION, undefined);
|
||||
}
|
||||
break;
|
||||
case UA_RESULT:
|
||||
case RESULT:
|
||||
var data = this.data;
|
||||
var parse = function (itemType) {
|
||||
return data[itemType]
|
||||
@@ -1354,11 +1368,11 @@
|
||||
.parseCH()
|
||||
.get();
|
||||
};
|
||||
this.set(UA_BROWSER, parse(UA_BROWSER))
|
||||
.set(UA_CPU, parse(UA_CPU))
|
||||
.set(UA_DEVICE, parse(UA_DEVICE))
|
||||
.set(UA_ENGINE, parse(UA_ENGINE))
|
||||
.set(UA_OS, parse(UA_OS));
|
||||
this.set(BROWSER, parse(BROWSER))
|
||||
.set(CPU, parse(CPU))
|
||||
.set(DEVICE, parse(DEVICE))
|
||||
.set(ENGINE, parse(ENGINE))
|
||||
.set(OS, parse(OS));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
@@ -1376,9 +1390,9 @@
|
||||
|
||||
function UAParser (ua, extensions, headers) {
|
||||
|
||||
if (typeof ua === OBJ_TYPE) {
|
||||
if (typeof ua === TYPEOF.OBJECT) {
|
||||
if (isExtensions(ua, true)) {
|
||||
if (typeof extensions === OBJ_TYPE) {
|
||||
if (typeof extensions === TYPEOF.OBJECT) {
|
||||
headers = extensions; // case UAParser(extensions, headers)
|
||||
}
|
||||
extensions = ua; // case UAParser(extensions)
|
||||
@@ -1387,13 +1401,13 @@
|
||||
extensions = undefined;
|
||||
}
|
||||
ua = undefined;
|
||||
} else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) {
|
||||
} else if (typeof ua === TYPEOF.STRING && !isExtensions(extensions, true)) {
|
||||
headers = extensions; // case UAParser(ua, headers)
|
||||
extensions = undefined;
|
||||
}
|
||||
|
||||
if (headers) {
|
||||
if (typeof headers.append === FUNC_TYPE) {
|
||||
if (typeof headers.append === TYPEOF.FUNCTION) {
|
||||
// Convert Headers object into a plain object
|
||||
var kv = {};
|
||||
headers.forEach(function (v, k) { kv[String(k).toLowerCase()] = v; });
|
||||
@@ -1414,7 +1428,7 @@
|
||||
return new UAParser(ua, extensions, headers).getResult();
|
||||
}
|
||||
|
||||
var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string
|
||||
var userAgent = typeof ua === TYPEOF.STRING ? ua : // Passed user-agent string
|
||||
(headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers
|
||||
((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent
|
||||
EMPTY)), // empty string
|
||||
@@ -1425,15 +1439,15 @@
|
||||
defaultRegexes,
|
||||
|
||||
createItemFunc = function (itemType) {
|
||||
if (itemType == UA_RESULT) {
|
||||
if (itemType == RESULT) {
|
||||
return function () {
|
||||
return new UAItem(itemType, userAgent, regexMap, httpUACH)
|
||||
.set('ua', userAgent)
|
||||
.set(UA_BROWSER, this.getBrowser())
|
||||
.set(UA_CPU, this.getCPU())
|
||||
.set(UA_DEVICE, this.getDevice())
|
||||
.set(UA_ENGINE, this.getEngine())
|
||||
.set(UA_OS, this.getOS())
|
||||
.set(BROWSER, this.getBrowser())
|
||||
.set(CPU, this.getCPU())
|
||||
.set(DEVICE, this.getDevice())
|
||||
.set(ENGINE, this.getEngine())
|
||||
.set(OS, this.getOS())
|
||||
.get();
|
||||
};
|
||||
} else {
|
||||
@@ -1447,16 +1461,15 @@
|
||||
|
||||
// public methods
|
||||
setProps.call(this, [
|
||||
['getBrowser', createItemFunc(UA_BROWSER)],
|
||||
['getCPU', createItemFunc(UA_CPU)],
|
||||
['getDevice', createItemFunc(UA_DEVICE)],
|
||||
['getEngine', createItemFunc(UA_ENGINE)],
|
||||
['getOS', createItemFunc(UA_OS)],
|
||||
['getResult', createItemFunc(UA_RESULT)],
|
||||
['getBrowser', createItemFunc(BROWSER)],
|
||||
['getCPU', createItemFunc(CPU)],
|
||||
['getDevice', createItemFunc(DEVICE)],
|
||||
['getEngine', createItemFunc(ENGINE)],
|
||||
['getOS', createItemFunc(OS)],
|
||||
['getResult', createItemFunc(RESULT)],
|
||||
['getUA', function () { return userAgent; }],
|
||||
['setUA', function (ua) {
|
||||
if (isString(ua))
|
||||
userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
|
||||
if (isString(ua)) userAgent = trim(ua, UA_MAX_LENGTH);
|
||||
return this;
|
||||
}]
|
||||
])
|
||||
|
||||
@@ -271,6 +271,26 @@
|
||||
"major" : "11"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Bing",
|
||||
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/605.1.15 BingSapphire/31.8.430522001",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Bing",
|
||||
"version" : "31.8.430522001",
|
||||
"major" : "31"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Bing",
|
||||
"ua" : "Mozilla/5.0 (Linux; Android 9; MIX 2 Build/PKQ1.190118.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4893.0 Mobile Safari/537.36 BingWeb/6.9.12",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Bing",
|
||||
"version" : "6.9.12",
|
||||
"major" : "6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Blazer",
|
||||
"ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; PalmSource/hspr-H102; Blazer/4.0) 16;320x320",
|
||||
@@ -611,6 +631,16 @@
|
||||
"major" : "55"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "PaleMoon",
|
||||
"ua" : "(Windows NT 6.2; WOW64) KHTML/4.11 Gecko/20130308 Firefox/23.0 (PaleMoon/20.3)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "PaleMoon",
|
||||
"version" : "20.3",
|
||||
"major" : "20"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "PaleMoon",
|
||||
"ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:52.9) Gecko/20100101 Goanna/3.4 Firefox/52.9 PaleMoon/27.6.1",
|
||||
@@ -1338,6 +1368,16 @@
|
||||
"major" : "5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Mozilla",
|
||||
"ua" : "Mozilla/2.02 [fr] (WinNT; I)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Mozilla",
|
||||
"version" : "2.02",
|
||||
"major" : "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "MSIE",
|
||||
"ua" : "Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC)",
|
||||
@@ -1688,6 +1728,36 @@
|
||||
"major" : "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Qwant",
|
||||
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) QwantMobile/6.7.6 Mobile/15E148 Safari/605.1.15",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Qwant",
|
||||
"version" : "6.7.6",
|
||||
"major" : "6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Qwant",
|
||||
"ua" : "QwantMobile/2.0 (Android 8.0.0; Mobile; rv:59.0) Gecko/59.0 Firefox/59.0 QwantBrowser/59.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Qwant",
|
||||
"version" : "59.0",
|
||||
"major" : "59"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Qwant",
|
||||
"ua" : "QwantMobile/2.0 (iPad; CPU OS 15_8_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) QwantiOS/2.7.0b1 Mobile/15E148 Safari/605.1.15",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Qwant",
|
||||
"version" : "2.0",
|
||||
"major" : "2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Rekonq 2",
|
||||
"ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.21 (KHTML, like Gecko) rekonq/2.2.1 Safari/537.21",
|
||||
|
||||
@@ -289,7 +289,15 @@
|
||||
},
|
||||
{
|
||||
"desc" : "68k",
|
||||
"ua" : "'Mozilla/1.1 (Macintosh; U; 68K)'",
|
||||
"ua" : "Mozilla/1.1 (Macintosh; U; 68K)",
|
||||
"expect" :
|
||||
{
|
||||
"architecture" : "68k"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "MC680x0",
|
||||
"ua" : "AmigaVoyager/3.2 (AmigaOS/MC680x0)",
|
||||
"expect" :
|
||||
{
|
||||
"architecture" : "68k"
|
||||
|
||||
@@ -44,6 +44,105 @@
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPad using Chrome",
|
||||
"ua": "Mozilla/5.0 (iPad13,10; CPU OS 16_6_2 like Mac OS X) AppleWebKit/602.3 (KHTML, like Gecko) CriOS/97.0.0.5927.72 Mobile/11S296 Safari/602.3",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPad",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPad using DuckDuckGo",
|
||||
"ua": "Mozilla/5.0 (iPad; iPad13,6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) DuckDuckGo/130.0.0.0 Mobile/15E148 Safari/604.1",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPad",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPad using Discord",
|
||||
"ua": "Discord/52.0 (iPad; iOS 14.4; Scale/2.00)",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPad",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPad using Firefox",
|
||||
"ua": "Mozilla/5.0 (iPad; iPad14,2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Firefox/120.0 Mobile/15E148 Safari/604.1",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPad",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPad using iTunes",
|
||||
"ua": "itunesstored/1.0 iOS/15.6.1 model/iPad13,8 hwp/t8103 build/19G82 (5; dt:238)",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPad",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPad using MS OneNote",
|
||||
"ua": "Microsoft Office OneNote/16.81/240108 (iOS/16.5.1; Tablet; AppStore; Apple/iPad8,3)",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPad",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPad using MS Word",
|
||||
"ua": "Microsoft Office Word/2.44.1211 (iOS/13.7; Tablet; es-MX; AppStore; Apple/iPad11,3)",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPad",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPad using Quora",
|
||||
"ua": "Quora 8.4.30 rv:3230 env:prod (iPad11,3; iPadOS 17.7; en_GB) AppleWebKit",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPad",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPad using TuneIn Radio",
|
||||
"ua": "TuneIn Radio/27.1.0; iPad6,3; iPadOS/16.6",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPad",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPad using TuneIn Radio Pro",
|
||||
"ua": "TuneIn Radio Pro/21.4.1; iPad8,9; iOS/15.0",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPad",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPad using YouTube",
|
||||
"ua": "com.google.ios.youtube/20.31.6 (iPad13,5; U; CPU iPadOS 18_6 like Mac OS X; en_US)",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPad",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPod",
|
||||
"ua": "Mozilla/5.0 (iPod touch; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53",
|
||||
@@ -62,6 +161,15 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPhone using Spotify",
|
||||
"ua": "Spotify/8.7.70 iOS/16.0 (iPhone15,3)",
|
||||
"expect": {
|
||||
"vendor": "Apple",
|
||||
"model": "iPhone15,3",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iPhone SE",
|
||||
"ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone8,4;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]",
|
||||
|
||||
@@ -1,7 +1,25 @@
|
||||
[
|
||||
{
|
||||
"desc": "BlackBerry 9650",
|
||||
"ua": "Mozilla/5.0 (BlackBerry; U; BlackBerry 9650; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.524 Mobile Safari/534.8+",
|
||||
"expect": {
|
||||
"vendor": "BlackBerry",
|
||||
"model": "9650",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "BlackBerry 9780",
|
||||
"ua": "Mozilla/5.0 (BlackBerry; U; BlackBerry 9780; en) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.546 Mobile Safari/534.8+",
|
||||
"expect": {
|
||||
"vendor": "BlackBerry",
|
||||
"model": "9780",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "BlackBerry Priv",
|
||||
"ua": "User-Agent: Mozilla/5.0 (Linux; Android 5.1.1; STV100-1 Build/LMY47V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/46.0.2490.76 Mobile Safari/537.36",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 5.1.1; STV100-1 Build/LMY47V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/46.0.2490.76 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "BlackBerry",
|
||||
"model": "STV100-1",
|
||||
@@ -28,7 +46,7 @@
|
||||
},
|
||||
{
|
||||
"desc": "BlackBerry Key2 LE",
|
||||
"ua": "User-Agent: Mozilla/5.0 (Linux; Android 8.1.0; BBE100-1 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497 Mobile Safari/537.36",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 8.1.0; BBE100-1 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "BlackBerry",
|
||||
"model": "BBE100-1",
|
||||
|
||||
20
test/data/ua/device/hisense.json
Normal file
20
test/data/ua/device/hisense.json
Normal file
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{
|
||||
"desc": "Hisense E50 Lite",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 11; Hisense E50 Lite) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5249.126 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Hisense",
|
||||
"model": "E50 Lite",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Hisense V40s",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 11; Hisense V40s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.125 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Hisense",
|
||||
"model": "V40s",
|
||||
"type": "mobile"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -701,6 +701,15 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Huawei P10 Lite",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 8.0.0; WAS-L03T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5199.205 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Huawei",
|
||||
"model": "WAS-L03T",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Huawei P20 Lite",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 Mobile Safari/537.36",
|
||||
@@ -728,6 +737,15 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Huawei P30",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 10; ELE-L04) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.109 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Huawei",
|
||||
"model": "ELE-L04",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Huawei P30",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 9; ELE-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36",
|
||||
@@ -782,6 +800,15 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Huawei Nova",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 7.0; HUAWEI CAN-L13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.5113.212 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Huawei",
|
||||
"model": "CAN-L13",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Huawei Nova 5T",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 10; YAL-L21) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36",
|
||||
@@ -899,6 +926,15 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Huawei Y7p",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 10; ART-L28; HMSCore 6.8.0.311) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4476.0 HuaweiBrowser/12.1.2.312 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Huawei",
|
||||
"model": "ART-L28",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Huawei Y7p",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 9; ART-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36",
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
[
|
||||
{
|
||||
"desc": "Nokia 1",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 10; Nokia 1 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5304.15 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Nokia",
|
||||
"model": "1",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Nokia3xx",
|
||||
"ua": "Nokia303/14.87 CLDC-1.1",
|
||||
|
||||
29
test/data/ua/device/wiko.json
Normal file
29
test/data/ua/device/wiko.json
Normal file
@@ -0,0 +1,29 @@
|
||||
[
|
||||
{
|
||||
"desc": "Wiko Life 3",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 11; Wiko U316AT) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5199.205 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Wiko",
|
||||
"model": "U316AT",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Wiko Ride 3",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 11; Wiko U614AS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "Wiko",
|
||||
"model": "U614AS",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Wiko T10",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 11; WIKO T10 Build/RP1A.200720.011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.5304.62 Mobile Safari/537.36",
|
||||
"expect": {
|
||||
"vendor": "WIKO",
|
||||
"model": "T10",
|
||||
"type": "mobile"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -116,6 +116,15 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Mi 11 Lite 5G",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 12; zh-CN; Mi 11 Lite 5G Build/SKQ1.211006.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.136 Mobile Safari/537.36 XiaoMi/MiuiBrowser/13.17.0-gn",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "Mi 11 Lite 5G",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Mi 5s Plus",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; zh-cn; MI 5s Plus Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/8.7.1",
|
||||
@@ -188,6 +197,15 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Mi 10T",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 12; fr-CA; Mi 10T Build/SKQ1.211006.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.79 Mobile Safari/537.36 XiaoMi/MiuiBrowser/13.16.1-gn",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "Mi 10T",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Mi A2",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 9; Mi A2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36",
|
||||
@@ -314,6 +332,15 @@
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Pad 5",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 12; ca-ES; Xiaomi Pad 5 Build/SKQ1.220303.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.86 Mobile Safari/537.36 XiaoMi/MiuiBrowser/13.6.0-gn",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "mi Pad 5",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Xiaomi Pad 6S Pro 12.4",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 14; 24018RPACC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36",
|
||||
@@ -458,6 +485,15 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "XiaoMi Redmi Note 10 Lite",
|
||||
"ua": "Mozilla/5.0 (Linux; U; Android 12; es-VE; Mi Note 10 Lite Build/SKQ1.210908.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.88 Mobile Safari/537.36 XiaoMi/MiuiBrowser/13.16.1-gn",
|
||||
"expect": {
|
||||
"vendor": "Xiaomi",
|
||||
"model": "Mi Note 10 Lite",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "XiaoMi Redmi Note 10 Pro",
|
||||
"ua": "Mozilla/5.0 (Linux; Android 13; M2101K6P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36",
|
||||
|
||||
@@ -965,7 +965,7 @@
|
||||
"ua" : "Mozilla/5.0 (compatible; Qwantbot-news/2.0; +https://help.qwant.com/bot/)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Qwantbot",
|
||||
"name" : "Qwantbot-news",
|
||||
"version" : "2.0",
|
||||
"type" : "crawler"
|
||||
}
|
||||
@@ -1100,6 +1100,26 @@
|
||||
"type" : "crawler"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "SurdotlyBot",
|
||||
"ua" : "Mozilla/5.0 (compatible; SurdotlyBot/1.0; +http://sur.ly/bot.html)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "SurdotlyBot",
|
||||
"version" : "1.0",
|
||||
"type" : "crawler"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Swiftbot",
|
||||
"ua" : "Swiftbot/1.0 (swiftype.com)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Swiftbot",
|
||||
"version" : "1.0",
|
||||
"type" : "crawler"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Teoma",
|
||||
"ua" : "Mozilla/2.0 (compatible; Ask Jeeves/Teoma; +http://sp.ask.com/docs/about/tech_crawling.html)",
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
},
|
||||
{
|
||||
"desc" : "Blueno",
|
||||
"ua" : "acebookexternalhit/1.1 (compatible; Blueno/1.0; +http://naver.me/scrap)",
|
||||
"ua" : "facebookexternalhit/1.1 (compatible; Blueno/1.0; +http://naver.me/scrap)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Blueno",
|
||||
@@ -119,6 +119,16 @@
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Discordbot",
|
||||
"ua" : "Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Discordbot",
|
||||
"version" : "2.0",
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "DuckAssistBot",
|
||||
"ua" : "DuckAssistBot/1.2; (+http://duckduckgo.com/duckassistbot.html)",
|
||||
@@ -239,6 +249,16 @@
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "KeybaseBot",
|
||||
"ua" : "Mozilla/5.0 (compatible; KeybaseBot; +https://keybase.io)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "KeybaseBot",
|
||||
"version" : "undefined",
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Meta-ExternalFetcher",
|
||||
"ua" : "meta-externalfetcher/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)",
|
||||
@@ -309,6 +329,36 @@
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Slack-ImgProxy",
|
||||
"ua" : "Slack-ImgProxy 0.19 (+https://api.slack.com/robots)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Slack-ImgProxy",
|
||||
"version" : "0.19",
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Slackbot",
|
||||
"ua" : "Slackbot 1.0 (+https://api.slack.com/robots)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Slackbot",
|
||||
"version" : "1.0",
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Slackbot-LinkExpanding",
|
||||
"ua" : "Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Slackbot-LinkExpanding",
|
||||
"version" : "1.0",
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Snap URL Preview",
|
||||
"ua" : "Snap URL Preview Service; bot; snapchat; https://developers.snap.com/robots ",
|
||||
@@ -339,6 +389,16 @@
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Twitterbot",
|
||||
"ua" : "Twitterbot/1.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Twitterbot",
|
||||
"version" : "1.0",
|
||||
"type" : "fetcher"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "UptimeRobot",
|
||||
"ua" : "Mozilla/5.0 (compatible; UptimeRobot/2.0; http://www.uptimerobot.com/)",
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
[
|
||||
{
|
||||
"desc" : "iOS 18.6",
|
||||
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.6 Mobile/15E148 Safari/604.1",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "18.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "iOS 26",
|
||||
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "26.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "iOS in App",
|
||||
"ua" : "AppName/version CFNetwork/version Darwin/version",
|
||||
@@ -17,6 +35,15 @@
|
||||
"version" : "5.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "iOS with DuckDuckGo",
|
||||
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Mobile/15E148 Safari/604.1 Ddg/26.0",
|
||||
"expect" :
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "18.7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "iOS with Opera Mini",
|
||||
"ua" : "Opera/9.80 (iPhone; Opera Mini/7.1.32694/27.1407; U; en) Presto/2.8.119 Version/11.10",
|
||||
@@ -35,6 +62,33 @@
|
||||
"version" : "13.6.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iOS with Instagram",
|
||||
"ua": "Instagram 5.0.2 (iPhone5,1; iPhone OS 7_0_4; en_US; en) AppleWebKit/420+",
|
||||
"expect":
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "7.0.4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iOS with MS Word App",
|
||||
"ua": "Microsoft Office Word/2.44.1211 (iOS/13.7; Tablet; es-MX; AppStore; Apple/iPad11,3)",
|
||||
"expect":
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "13.7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iOS with Quora App",
|
||||
"ua": "Quora 8.4.30 rv:3230 env:prod (iPad11,3; iPadOS 17.7; en_GB) AppleWebKit",
|
||||
"expect":
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "17.7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iOS with Slack App",
|
||||
"ua": "com.tinyspeck.chatlyio/23.04.10 (iPhone; iOS 16.4.1; Scale/3.00)",
|
||||
@@ -44,6 +98,33 @@
|
||||
"version" : "16.4.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iOS with Snapchat",
|
||||
"ua": "Snapchat/12.12.1.40 (iPhone15,2; iOS 16.2; gzip)",
|
||||
"expect":
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "16.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iOS with Spotify App",
|
||||
"ua": "Spotify/8.7.70 iOS/16.0 (iPhone15,3)",
|
||||
"expect":
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "16.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "iOS with TuneIn Radio App",
|
||||
"ua": "TuneIn Radio/27.1.0; iPad6,3; iPadOS/16.6",
|
||||
"expect":
|
||||
{
|
||||
"name" : "iOS",
|
||||
"version" : "16.6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "iOS BE App",
|
||||
"ua" : "APP-BE Test/1.0 (iPad; Apple; CPU iPhone OS 7_0_2 like Mac OS X)",
|
||||
|
||||
37
test/unit/cli/cli.spec.js
Normal file
37
test/unit/cli/cli.spec.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const assert = require('node:assert');
|
||||
const { exec } = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const { UAParser } = require('../../../src/main/ua-parser');
|
||||
const uap = new UAParser();
|
||||
|
||||
const input = [
|
||||
'Opera/9.25 (Windows NT 6.0; U; ru)',
|
||||
'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
|
||||
];
|
||||
const output = input.map(x => uap.setUA(x).getResult());
|
||||
|
||||
describe('npx ua-parser-js <string>', () => {
|
||||
it ('print result to stdout', () => {
|
||||
exec('node ./script/cli.js "TEST"', (err, stdout, stderr) => {
|
||||
assert.deepEqual(JSON.parse(stdout), JSON.parse(JSON.stringify([uap.setUA("TEST").getResult()])));
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe('npx ua-parser-js --input-file=<filepath>', () => {
|
||||
it ('load file and print result to stdout', () => {
|
||||
exec('node ./script/cli.js --input-file="../test/unit/cli/input.txt"', (err, stdout, stderr) => {
|
||||
assert.deepEqual(JSON.parse(stdout), JSON.parse(JSON.stringify(output)));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('npx ua-parser-js --input-file=<filepath> --output-file=<filepath>', () => {
|
||||
it ('load file and save result to file', () => {
|
||||
exec('node ./script/cli.js --input-file="../test/unit/cli/input.txt" --output-file="../test/unit/cli/output.json"', (err, stdout, stderr) => {
|
||||
fs.readFile('test/unit/cli/output.json', (err, data) => {
|
||||
assert.deepEqual(JSON.parse(data), JSON.parse(JSON.stringify(output)));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
2
test/unit/cli/input.txt
Normal file
2
test/unit/cli/input.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Opera/9.25 (Windows NT 6.0; U; ru)
|
||||
Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)
|
||||
32
test/unit/cli/output.json
Normal file
32
test/unit/cli/output.json
Normal file
@@ -0,0 +1,32 @@
|
||||
[
|
||||
{
|
||||
"ua": "Opera/9.25 (Windows NT 6.0; U; ru)",
|
||||
"browser": {
|
||||
"name": "Opera",
|
||||
"version": "9.25",
|
||||
"major": "9"
|
||||
},
|
||||
"cpu": {},
|
||||
"device": {},
|
||||
"engine": {},
|
||||
"os": {
|
||||
"name": "Windows",
|
||||
"version": "Vista"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ua": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)",
|
||||
"browser": {
|
||||
"name": "IE",
|
||||
"version": "5.5",
|
||||
"major": "5"
|
||||
},
|
||||
"cpu": {},
|
||||
"device": {},
|
||||
"engine": {},
|
||||
"os": {
|
||||
"name": "Windows",
|
||||
"version": "NT"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -10,7 +10,6 @@ var cpus = require('../data/ua/cpu/cpu-all.json');
|
||||
var devices = readJsonFiles('test/data/ua/device');
|
||||
var engines = require('../data/ua/engine/engine-all.json');
|
||||
var os = readJsonFiles('test/data/ua/os');
|
||||
var { Headers } = require('undici');
|
||||
|
||||
function readJsonFiles(dir) {
|
||||
var list = [];
|
||||
@@ -163,6 +162,14 @@ describe('Extending Regex', function () {
|
||||
assert.deepEqual(myParser3.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"});
|
||||
});
|
||||
|
||||
describe('User-agent with trailing space', function () {
|
||||
it ('trailing space will be trimmed', function () {
|
||||
const uastring = ' Opera/9.21 (Windows NT 5.1; U; ru) ';
|
||||
const { ua } = UAParser(uastring);
|
||||
assert.equal(ua, 'Opera/9.21 (Windows NT 5.1; U; ru) ');
|
||||
});
|
||||
});
|
||||
|
||||
describe('User-agent length', function () {
|
||||
var UA_MAX_LENGTH = 500;
|
||||
|
||||
@@ -375,12 +382,15 @@ describe('Read user-agent data from req.headers', function () {
|
||||
assert.strictEqual(engine.name, "EdgeHTML");
|
||||
});
|
||||
|
||||
it('Fetch API\'s Header can be passed directly into headers', () => {
|
||||
const reqHeaders = new Headers();
|
||||
reqHeaders.append('User-Agent', 'Midori/0.2.2 (X11; Linux i686; U; en-us) WebKit/531.2+');
|
||||
const { browser } = UAParser(reqHeaders);
|
||||
assert.strictEqual(browser.is('Midori'), true);
|
||||
});
|
||||
// Headers supported in node 18+ - https://developer.mozilla.org/en-US/docs/Web/API/Headers
|
||||
if (typeof Headers !== 'undefined') {
|
||||
it('Fetch API\'s Header can be passed directly into headers', () => {
|
||||
const reqHeaders = new Headers();
|
||||
reqHeaders.append('User-Agent', 'Midori/0.2.2 (X11; Linux i686; U; en-us) WebKit/531.2+');
|
||||
const { browser } = UAParser(reqHeaders);
|
||||
assert.strictEqual(browser.is('Midori'), true);
|
||||
});
|
||||
}
|
||||
|
||||
it('Headers field name should be case insensitive', function () {
|
||||
const hEaDeRs = {
|
||||
|
||||
Reference in New Issue
Block a user