Update 2.0.0-beta.3-pro-business

This commit is contained in:
Faisal Salman 2024-07-24 13:48:45 +07:00
commit 44d9d5700f
34 changed files with 1062 additions and 276 deletions

View File

@ -16,6 +16,20 @@
- Provided Extensions submodule `'ua-parser-js/extensions'` - Provided Extensions submodule `'ua-parser-js/extensions'`
- Provided Helpers submodule `'ua-parser-js/helpers'` - Provided Helpers submodule `'ua-parser-js/helpers'`
## Version 2.0.0-beta.3
- Breaking:
- AR/VR devices moved to new device type: `xr`
- New property in `browser`: `type`
- In `ua-parser-js/extensions` submodule, `bots` divided into `crawler` / `fetcher`
- New features:
- Parse directly from command line using `npx ua-parser-js`
- Extensions can be passed as a list to `UAParser()`
- Add new browser: Pico Browser, Twitter, Wolvic
- Improve browser detection: DuckDuckGo, ICEBrowser, Klar, QQ, Sleipnir
- Improve device detection: Oculus Quest & Oppo Pad
- Update latest client hints spec: `formFactor` -> `formFactors`
## Version 2.0.0-beta.2 ## Version 2.0.0-beta.2
- Increase UA_MAX_LENGTH to 500 - Increase UA_MAX_LENGTH to 500

View File

@ -1,8 +1,8 @@
# UAPARSER.JS PRO BUSINESS LICENSE # UAPARSER.JS PRO BUSINESS LICENSE
Version 1, October 2023 Version 2, July 2024
Copyright (C) 2023 Faisal Salman <f+ua-parser-js@faisalman.com> Copyright (C) 2023-2024 Faisal Salman <f+ua-parser-js@faisalman.com>
--- ---
@ -10,7 +10,7 @@ Copyright (C) 2023 Faisal Salman <f+ua-parser-js@faisalman.com>
"We" are the team behind UAParser.js. "We" are the team behind UAParser.js.
"You" are the individual/organization/company who is responsible for purchasing this license. "You" are the individual, organization, or company who is responsible for purchasing this license.
"The Code" is UAParser.js. "The Code" is UAParser.js.
@ -20,31 +20,31 @@ Copyright (C) 2023 Faisal Salman <f+ua-parser-js@faisalman.com>
## License ## License
We retains all title, intellectual property, and ownership rights to The Code. We retain all title, intellectual property, and ownership rights to The Code.
The Code is licensed, not sold, to You for use solely subject to the terms and conditions detailed here. The Code is licensed, not sold, to You for use solely subject to the terms and conditions detailed here.
We grant You (and only You) a limited, non-exclusive, non-transferable, non-sublicensable, royalty-free right to use, copy, and modify The Code. We grant You (and only You) a limited, non-exclusive, non-transferable, non-sublicensable, royalty-free right to use, copy, and modify The Code.
This license is only valid for You as 1 (one) individual/organization/company and can not be transferred to other individual/organization/company. This license is only valid for You as one (1) individual, organization, or company and cannot be transferred to another individual, organization, or company.
--- ---
## Rights ## Rights
You may use and modify The Code only in 1 (one) Project, if you use it in more than 1 (one) Project you have to purchase for another license. You may use and modify The Code only in one (1) Project. If you use it in more than one (1) Project, you must purchase another license.
If You transfer a Project to a client, the use of The Code must be limited to the original functionality that You created for them. The Code must not be extracted, reproduced, or used in any other way. You must inform your client of this condition. If You transfer a Project to a client, the use of The Code must be limited to the original functionality that You created for them. The Code must not be extracted, reproduced, or used in any other way. You must inform your client of this condition.
You have the right to get lifetime updates and a 1 (one) year support, starting from the time you make the purchase. You have the right to receive lifetime updates and one (1) year of support, starting from the time you make the purchase.
--- ---
## Restriction ## Restrictions
You may not redistribute The Code, as-is or modified, except as a part of a Project that you made. You may not redistribute The Code, as-is or modified, except as a part of a Project that you made.
You may not deliver a Project that contains The Code as an open-source Project that might be used for commercial purpose to the general public, except with our written consent. You may not deliver a Project that contains The Code as an open-source Project that might be used for commercial purposes by the general public, except with our written consent.
You may not use The Code for unlawful, inappropriate, illegal, or offensive purposes. You may not use The Code for unlawful, inappropriate, illegal, or offensive purposes.
@ -52,19 +52,19 @@ You may not use The Code for unlawful, inappropriate, illegal, or offensive purp
## Limitations ## Limitations
The Code is provided 'as is' without warranty of any kind, expressed, or implied. The Code is provided 'as is' without warranty of any kind, expressed or implied.
We shall not be liable for any damages, including but not limited to, direct, indirect, special, incidental, or consequential damages or losses that occur by the use of The Code. We shall not be liable for any damages, including but not limited to, direct, indirect, special, incidental, or consequential damages or losses that occur by the use of The Code.
We reserve the rights to discontinue the lifetime updates for The Code at any time, with or without notice. We reserve the right to discontinue the lifetime updates for The Code at any time, with or without notice.
We offer support only for question within the scope of The Code functionality or related at our sole discretion. We offer support only for questions within the scope of The Code's functionality or related issues at our sole discretion.
--- ---
## Terminations ## Termination
This license works indefinitely but can be revoked at any time if there is a violation to any of the terms. This license is effective indefinitely but can be revoked at any time if there is a violation of any of the terms.
--- ---

View File

@ -1,5 +1,5 @@
<p align="center"> <p align="center">
<a href="https://uaparser.js.org"><img src="https://raw.githubusercontent.com/faisalman/ua-parser-js/gh-pages/images/uap-header.png"></a> <a href="https://uaparser.dev"><img src="https://raw.githubusercontent.com/faisalman/ua-parser-js/gh-pages/images/uap-header.png"></a>
</p> </p>
# UAParser.js # UAParser.js
@ -14,10 +14,10 @@ npm install @ua-parser-js/pro-business
# Documentation # Documentation
https://docs.uaparser.js.org/v2 https://docs.uaparser.dev
# License # License
UAParser.js PRO Business UAParser.js PRO Business
Copyright (c) 2012-2023 Faisal Salman <<f@faisalman.com>> Copyright (c) 2012-2024 Faisal Salman <<f@faisalman.com>>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

7
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "@ua-parser-js/pro-business", "name": "@ua-parser-js/pro-business",
"version": "2.0.0-beta.2", "version": "2.0.0-beta.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@ua-parser-js/pro-business", "name": "@ua-parser-js/pro-business",
"version": "2.0.0-beta.2", "version": "2.0.0-beta.3",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -22,6 +22,9 @@
} }
], ],
"license": "UAParser.js-PRO-Business", "license": "UAParser.js-PRO-Business",
"bin": {
"pro-business": "script/cli.js"
},
"devDependencies": { "devDependencies": {
"@babel/parser": "7.15.8", "@babel/parser": "7.15.8",
"@babel/traverse": "7.23.2", "@babel/traverse": "7.23.2",

View File

@ -1,6 +1,6 @@
Package.describe({ Package.describe({
name: 'faisalman:ua-parser-js', name: 'faisalman:ua-parser-js',
version: '2.0.0-beta.2', version: '2.0.0-beta.3',
summary: 'Lightweight JavaScript-based user-agent string parser', summary: 'Lightweight JavaScript-based user-agent string parser',
git: 'https://github.com/faisalman/ua-parser-js.git', git: 'https://github.com/faisalman/ua-parser-js.git',
documentation: 'readme.md' documentation: 'readme.md'

View File

@ -1,7 +1,7 @@
{ {
"title": "UAParser.js PRO Business", "title": "UAParser.js PRO Business",
"name": "@ua-parser-js/pro-business", "name": "@ua-parser-js/pro-business",
"version": "2.0.0-beta.2", "version": "2.0.0-beta.3",
"author": "Faisal Salman <f@faisalman.com> (http://faisalman.com)", "author": "Faisal Salman <f@faisalman.com> (http://faisalman.com)",
"description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment", "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client Hints data. Supports browser & node.js environment",
"keywords": [ "keywords": [
@ -18,7 +18,8 @@
"ua-parser-js", "ua-parser-js",
"browser-detection", "browser-detection",
"device-detection", "device-detection",
"os-detection" "os-detection",
"bot-detection"
], ],
"homepage": "https://github.com/faisalman/ua-parser-js", "homepage": "https://github.com/faisalman/ua-parser-js",
"contributors": [ "contributors": [
@ -196,6 +197,7 @@
"dist", "dist",
"src" "src"
], ],
"bin": "./script/cli.js",
"scripts": { "scripts": {
"build": "./script/build-dist.sh && ./script/build-module.js", "build": "./script/build-dist.sh && ./script/build-module.js",
"build+test": "npm run build && npm run test", "build+test": "npm run build && npm run test",
@ -205,7 +207,7 @@
"test:eslint": "eslint src && eslint script", "test:eslint": "eslint src && eslint script",
"test:jshint": "jshint src/main", "test:jshint": "jshint src/main",
"test:lockfile-lint": "npx lockfile-lint -p package-lock.json", "test:lockfile-lint": "npx lockfile-lint -p package-lock.json",
"test:mocha": "mocha -R list test/mocha*js", "test:mocha": "mocha test/mocha*js",
"test:playwright": "playwright test" "test:playwright": "playwright test"
}, },
"devDependencies": { "devDependencies": {

4
script/cli.js Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env node
const UAParser = require('ua-parser-js');
console.log(JSON.stringify(process.argv.slice(2).map(ua => UAParser(ua)), null, 4));

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Enums for UAParser.js v2.0.0-beta.2 /* Enums for UAParser.js v2.0.0-beta.3
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License */ UAParser.js PRO Business License */
@ -104,6 +104,7 @@ const Browser = Object.freeze({
PALEMOON: 'PaleMoon', PALEMOON: 'PaleMoon',
PHANTOMJS: 'PhantomJS', PHANTOMJS: 'PhantomJS',
PHOENIX: 'Phoenix', PHOENIX: 'Phoenix',
PICOBROWSER: 'Pico Browser',
POLARIS: 'Polaris', POLARIS: 'Polaris',
PUFFIN: 'Puffin', PUFFIN: 'Puffin',
QQ: 'QQBrowser', QQ: 'QQBrowser',
@ -128,9 +129,9 @@ const Browser = Object.freeze({
TESLA: 'Tesla', TESLA: 'Tesla',
TIKTOK: 'TikTok', TIKTOK: 'TikTok',
TIZEN: 'Tizen Browser', TIZEN: 'Tizen Browser',
TWITTER: 'Twitter',
UC: 'UCBrowser', UC: 'UCBrowser',
UP: 'UP.Browser', UP: 'UP.Browser',
VIERA: 'Viera',
VIVALDI: 'Vivaldi', VIVALDI: 'Vivaldi',
VIVO: 'Vivo Browser', VIVO: 'Vivo Browser',
W3M: 'w3m', W3M: 'w3m',
@ -139,11 +140,21 @@ const Browser = Object.freeze({
WECHAT: 'WeChat', WECHAT: 'WeChat',
WEIBO: 'Weibo', WEIBO: 'Weibo',
WHALE: 'Whale', WHALE: 'Whale',
WOLVIC: 'Wolvic',
YANDEX: 'Yandex' YANDEX: 'Yandex'
// TODO : test! // TODO : test!
}); });
const BrowserType = Object.freeze({
CRAWLER: 'crawler',
CLI: 'cli',
EMAIL: 'email',
FETCHER: 'fetcher',
INAPP: 'inapp',
MODULE: 'module'
});
const CPU = Object.freeze({ const CPU = Object.freeze({
ARM : 'arm', ARM : 'arm',
ARM_64: 'arm64', ARM_64: 'arm64',
@ -171,7 +182,8 @@ const Device = Object.freeze({
MOBILE: 'mobile', MOBILE: 'mobile',
SMARTTV: 'smarttv', SMARTTV: 'smarttv',
TABLET: 'tablet', TABLET: 'tablet',
WEARABLE: 'wearable' WEARABLE: 'wearable',
XR: 'xr'
}); });
const Vendor = Object.freeze({ const Vendor = Object.freeze({
@ -342,6 +354,7 @@ const OS = Object.freeze({
module.exports = { module.exports = {
Browser, Browser,
BrowserType,
CPU, CPU,
Device, Device,
Vendor, Vendor,

View File

@ -3,7 +3,7 @@
// Source: /src/enums/ua-parser-enums.js // Source: /src/enums/ua-parser-enums.js
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Enums for UAParser.js v2.0.0-beta.2 /* Enums for UAParser.js v2.0.0-beta.3
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License */ UAParser.js PRO Business License */
@ -108,6 +108,7 @@ const Browser = Object.freeze({
PALEMOON: 'PaleMoon', PALEMOON: 'PaleMoon',
PHANTOMJS: 'PhantomJS', PHANTOMJS: 'PhantomJS',
PHOENIX: 'Phoenix', PHOENIX: 'Phoenix',
PICOBROWSER: 'Pico Browser',
POLARIS: 'Polaris', POLARIS: 'Polaris',
PUFFIN: 'Puffin', PUFFIN: 'Puffin',
QQ: 'QQBrowser', QQ: 'QQBrowser',
@ -132,9 +133,9 @@ const Browser = Object.freeze({
TESLA: 'Tesla', TESLA: 'Tesla',
TIKTOK: 'TikTok', TIKTOK: 'TikTok',
TIZEN: 'Tizen Browser', TIZEN: 'Tizen Browser',
TWITTER: 'Twitter',
UC: 'UCBrowser', UC: 'UCBrowser',
UP: 'UP.Browser', UP: 'UP.Browser',
VIERA: 'Viera',
VIVALDI: 'Vivaldi', VIVALDI: 'Vivaldi',
VIVO: 'Vivo Browser', VIVO: 'Vivo Browser',
W3M: 'w3m', W3M: 'w3m',
@ -143,11 +144,21 @@ const Browser = Object.freeze({
WECHAT: 'WeChat', WECHAT: 'WeChat',
WEIBO: 'Weibo', WEIBO: 'Weibo',
WHALE: 'Whale', WHALE: 'Whale',
WOLVIC: 'Wolvic',
YANDEX: 'Yandex' YANDEX: 'Yandex'
// TODO : test! // TODO : test!
}); });
const BrowserType = Object.freeze({
CRAWLER: 'crawler',
CLI: 'cli',
EMAIL: 'email',
FETCHER: 'fetcher',
INAPP: 'inapp',
MODULE: 'module'
});
const CPU = Object.freeze({ const CPU = Object.freeze({
ARM : 'arm', ARM : 'arm',
ARM_64: 'arm64', ARM_64: 'arm64',
@ -175,7 +186,8 @@ const Device = Object.freeze({
MOBILE: 'mobile', MOBILE: 'mobile',
SMARTTV: 'smarttv', SMARTTV: 'smarttv',
TABLET: 'tablet', TABLET: 'tablet',
WEARABLE: 'wearable' WEARABLE: 'wearable',
XR: 'xr'
}); });
const Vendor = Object.freeze({ const Vendor = Object.freeze({
@ -346,6 +358,7 @@ const OS = Object.freeze({
export { export {
Browser, Browser,
BrowserType,
CPU, CPU,
Device, Device,
Vendor, Vendor,

View File

@ -1,13 +1,14 @@
// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.2 // Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.3
// Project: https://github.com/faisalman/ua-parser-js // Project: https://github.com/faisalman/ua-parser-js
// Definitions by: Faisal Salman <https://github.com/faisalman> // Definitions by: Faisal Salman <https://github.com/faisalman>
import type { UAParserExt } from "../main/ua-parser"; import type { UAParserExt } from "../main/ua-parser";
export const Apps: UAParserExt;
export const Bots: UAParserExt;
export const CLIs: UAParserExt; export const CLIs: UAParserExt;
export const Crawlers: UAParserExt;
export const ExtraDevices: UAParserExt; export const ExtraDevices: UAParserExt;
export const Emails: UAParserExt; export const Emails: UAParserExt;
export const Fetchers: UAParserExt;
export const InApps: UAParserExt;
export const MediaPlayers: UAParserExt; export const MediaPlayers: UAParserExt;
export const Modules: UAParserExt; export const Modules: UAParserExt;

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Extensions for UAParser.js v2.0.0-beta.2 /* Extensions for UAParser.js v2.0.0-beta.3
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License */ UAParser.js PRO Business License */
@ -14,33 +14,74 @@ const VENDOR = 'vendor';
const VERSION = 'version'; const VERSION = 'version';
const MOBILE = 'mobile'; const MOBILE = 'mobile';
const TABLET = 'tablet'; const TABLET = 'tablet';
const CRAWLER = 'crawler';
const CLI = 'cli';
const EMAIL = 'email';
const FETCHER = 'fetcher';
const INAPP = 'inapp';
const MODULE = 'module';
const Apps = Object.freeze({ //////////////////////
browser : [ // COMMAND LINE APPS
[/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']] /////////////////////
]
});
const Bots = Object.freeze({
browser : [
// Googlebot / BingBot / MSNBot / FacebookBot
[/((?:google|bing|msn|facebook)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
// GPTBot - https://platform.openai.com/docs/gptbot
[/(gptbot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
// Slackbot - https://api.slack.com/robots
[/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
]
});
const CLIs = Object.freeze({ const CLIs = Object.freeze({
browser : [ browser : [
// wget / curl / lynx // wget / curl / lynx
[/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] [/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]]
] ]
}); });
////////////////////////
// CRAWLERS / SPIDERS
///////////////////////
const Crawlers = Object.freeze({
browser : [
// Amazonbot - https://developer.amazon.com/amazonbot
// Applebot - http://apple.com/go/applebot
// Bingbot - http://www.bing.com/bingbot.htm
// DuckDuckBot - http://duckduckgo.com/duckduckbot.html
// FacebookBot - https://developers.facebook.com/docs/sharing/bot/
// GPTBot - https://platform.openai.com/docs/gptbot
[/((?:amazon|apple|bing|duckduck|facebook|gpt)bot)\/([\w\.]+)/i],
[NAME, VERSION, [TYPE, CRAWLER]],
// Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001
[/(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i],
[NAME, VERSION, [TYPE, CRAWLER]],
// Bytespider
// Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp
[/((?:bytespider|(?=yahoo! )slurp))/i],
[NAME, [TYPE, CRAWLER]],
// ClaudeBot
[/(claude(?:bot|-web))\/([\w\.]+)/i],
[NAME, VERSION, [TYPE, CRAWLER]],
// Googlebot - http://www.google.com/bot.html
[
/(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i
],
[NAME, VERSION, [TYPE, CRAWLER]],
// Sogou Spider
[/(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i],
[NAME, VERSION, [TYPE, CRAWLER]],
// Yandex Bots - https://yandex.com/bots
[
/(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i
],
[NAME, VERSION, [TYPE, CRAWLER]]
]
});
//////////////////
// EXTRA DEVICES
/////////////////
const ExtraDevices = Object.freeze({ const ExtraDevices = Object.freeze({
device : [[ device : [[
/(nook)[\w ]+build\/(\w+)/i, // Nook /(nook)[\w ]+build\/(\w+)/i, // Nook
@ -117,13 +158,63 @@ const ExtraDevices = Object.freeze({
] ]
}); });
///////////////
// EMAIL APPS
//////////////
const Emails = Object.freeze({ const Emails = Object.freeze({
browser : [ browser : [
// Microsoft Outlook / Thunderbird // Microsoft Outlook / Thunderbird
[/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']] [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]]
] ]
}); });
///////////////////////
// ON-DEMAND SCRAPERS
//////////////////////
const Fetchers = Object.freeze({
browser : [
// BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot
[/(bingpreview|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i],
[NAME, VERSION, [TYPE, FETCHER]],
// Google Bots / Snapchat
[/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i],
[NAME, [TYPE, FETCHER]],
// Slackbot - https://api.slack.com/robots
[/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i],
[NAME, VERSION, [TYPE, FETCHER]],
// WhatsApp
[/(whatsapp)\/([\w\.]+)[\/ ][ianw]/i],
[NAME, VERSION, [TYPE, FETCHER]],
// Yandex Bots - https://yandex.com/bots
[
/(yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i,
/(yandex(?:sitelinks|userproxy))/i
],
[NAME, VERSION, [TYPE, FETCHER]]
]
});
////////////////////
// IN-APP BROWSERS
///////////////////
const InApps = Object.freeze({
browser : [
[/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]]
]
});
//////////////////////
// MEDIA PLAYER APPS
/////////////////////
const MediaPlayers = Object.freeze({ const MediaPlayers = Object.freeze({
browser : [[ browser : [[
@ -230,19 +321,24 @@ const MediaPlayers = Object.freeze({
] ]
}); });
////////////////////////
// MODULES / LIBRARIES
///////////////////////
const Modules = Object.freeze({ const Modules = Object.freeze({
browser : [ browser : [
// Axios/jsdom/Scrapy // Axios/jsdom/Scrapy
[/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']] [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, MODULE]]
] ]
}); });
module.exports = { module.exports = {
Apps,
Bots,
CLIs, CLIs,
Crawlers,
ExtraDevices, ExtraDevices,
Emails, Emails,
Fetchers,
InApps,
MediaPlayers, MediaPlayers,
Modules Modules
}; };

View File

@ -3,7 +3,7 @@
// Source: /src/extensions/ua-parser-extensions.js // Source: /src/extensions/ua-parser-extensions.js
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Extensions for UAParser.js v2.0.0-beta.2 /* Extensions for UAParser.js v2.0.0-beta.3
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License */ UAParser.js PRO Business License */
@ -18,33 +18,74 @@ const VENDOR = 'vendor';
const VERSION = 'version'; const VERSION = 'version';
const MOBILE = 'mobile'; const MOBILE = 'mobile';
const TABLET = 'tablet'; const TABLET = 'tablet';
const CRAWLER = 'crawler';
const CLI = 'cli';
const EMAIL = 'email';
const FETCHER = 'fetcher';
const INAPP = 'inapp';
const MODULE = 'module';
const Apps = Object.freeze({ //////////////////////
browser : [ // COMMAND LINE APPS
[/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']] /////////////////////
]
});
const Bots = Object.freeze({
browser : [
// Googlebot / BingBot / MSNBot / FacebookBot
[/((?:google|bing|msn|facebook)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
// GPTBot - https://platform.openai.com/docs/gptbot
[/(gptbot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
// Slackbot - https://api.slack.com/robots
[/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
]
});
const CLIs = Object.freeze({ const CLIs = Object.freeze({
browser : [ browser : [
// wget / curl / lynx // wget / curl / lynx
[/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']] [/(wget|curl|lynx)[\/ ]([\w\.]+)/i], [NAME, VERSION, [TYPE, CLI]]
] ]
}); });
////////////////////////
// CRAWLERS / SPIDERS
///////////////////////
const Crawlers = Object.freeze({
browser : [
// Amazonbot - https://developer.amazon.com/amazonbot
// Applebot - http://apple.com/go/applebot
// Bingbot - http://www.bing.com/bingbot.htm
// DuckDuckBot - http://duckduckgo.com/duckduckbot.html
// FacebookBot - https://developers.facebook.com/docs/sharing/bot/
// GPTBot - https://platform.openai.com/docs/gptbot
[/((?:amazon|apple|bing|duckduck|facebook|gpt)bot)\/([\w\.]+)/i],
[NAME, VERSION, [TYPE, CRAWLER]],
// Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001
[/(baiduspider)[-imagevdonsfcpr]{0,6}\/([\w\.]+)/i],
[NAME, VERSION, [TYPE, CRAWLER]],
// Bytespider
// Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp
[/((?:bytespider|(?=yahoo! )slurp))/i],
[NAME, [TYPE, CRAWLER]],
// ClaudeBot
[/(claude(?:bot|-web))\/([\w\.]+)/i],
[NAME, VERSION, [TYPE, CRAWLER]],
// Googlebot - http://www.google.com/bot.html
[
/(google(?:bot|other)(?:-image|-video|-news|-extended)?|(?:storebot-)?google(?:-inspectiontool)?)\/?([\w\.]*)/i
],
[NAME, VERSION, [TYPE, CRAWLER]],
// Sogou Spider
[/(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i],
[NAME, VERSION, [TYPE, CRAWLER]],
// Yandex Bots - https://yandex.com/bots
[
/(yandex(?:(?:mobile)?(?:accessibility|additional|renderresources|screenshot|sprav)?bot|image(?:s|resizer)|video(?:parser)?|blogs|adnet|favicons|fordomain|market|media|metrika|news|ontodb(?:api)?|pagechecker|partner|rca|tracker|turbo|vertis|webmaster|antivirus))\/([\w\.]+)/i
],
[NAME, VERSION, [TYPE, CRAWLER]]
]
});
//////////////////
// EXTRA DEVICES
/////////////////
const ExtraDevices = Object.freeze({ const ExtraDevices = Object.freeze({
device : [[ device : [[
/(nook)[\w ]+build\/(\w+)/i, // Nook /(nook)[\w ]+build\/(\w+)/i, // Nook
@ -121,13 +162,63 @@ const ExtraDevices = Object.freeze({
] ]
}); });
///////////////
// EMAIL APPS
//////////////
const Emails = Object.freeze({ const Emails = Object.freeze({
browser : [ browser : [
// Microsoft Outlook / Thunderbird // Microsoft Outlook / Thunderbird
[/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']] [/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, EMAIL]]
] ]
}); });
///////////////////////
// ON-DEMAND SCRAPERS
//////////////////////
const Fetchers = Object.freeze({
browser : [
// BingPreview / Mastodon / Pinterestbot / Redditbot / Telegrambot / Twitterbot
[/(bingpreview|mastodon|(?:discord|linkedin|pinterest|reddit|telegram|twitter)bot)\/([\w\.]+)/i],
[NAME, VERSION, [TYPE, FETCHER]],
// Google Bots / Snapchat
[/(feedfetcher-google|google-read-aloud|(?=bot; )snapchat)/i],
[NAME, [TYPE, FETCHER]],
// Slackbot - https://api.slack.com/robots
[/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i],
[NAME, VERSION, [TYPE, FETCHER]],
// WhatsApp
[/(whatsapp)\/([\w\.]+)[\/ ][ianw]/i],
[NAME, VERSION, [TYPE, FETCHER]],
// Yandex Bots - https://yandex.com/bots
[
/(yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i,
/(yandex(?:sitelinks|userproxy))/i
],
[NAME, VERSION, [TYPE, FETCHER]]
]
});
////////////////////
// IN-APP BROWSERS
///////////////////
const InApps = Object.freeze({
browser : [
[/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, INAPP]]
]
});
//////////////////////
// MEDIA PLAYER APPS
/////////////////////
const MediaPlayers = Object.freeze({ const MediaPlayers = Object.freeze({
browser : [[ browser : [[
@ -234,19 +325,24 @@ const MediaPlayers = Object.freeze({
] ]
}); });
////////////////////////
// MODULES / LIBRARIES
///////////////////////
const Modules = Object.freeze({ const Modules = Object.freeze({
browser : [ browser : [
// Axios/jsdom/Scrapy // Axios/jsdom/Scrapy
[/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']] [/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, MODULE]]
] ]
}); });
export { export {
Apps,
Bots,
CLIs, CLIs,
Crawlers,
ExtraDevices, ExtraDevices,
Emails, Emails,
Fetchers,
InApps,
MediaPlayers, MediaPlayers,
Modules Modules
}; };

View File

@ -1,4 +1,4 @@
// Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.2 // Type definitions for Helpers submodule of UAParser.js v2.0.0-beta.3
// Project: https://github.com/faisalman/ua-parser-js // Project: https://github.com/faisalman/ua-parser-js
// Definitions by: Faisal Salman <https://github.com/faisalman> // Definitions by: Faisal Salman <https://github.com/faisalman>

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Helpers for UAParser.js v2.0.0-beta.2 /* Helpers for UAParser.js v2.0.0-beta.3
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
AGPLv3 License */ AGPLv3 License */

View File

@ -3,7 +3,7 @@
// Source: /src/helpers/ua-parser-helpers.js // Source: /src/helpers/ua-parser-helpers.js
/////////////////////////////////////////////// ///////////////////////////////////////////////
/* Helpers for UAParser.js v2.0.0-beta.2 /* Helpers for UAParser.js v2.0.0-beta.3
https://github.com/faisalman/ua-parser-js https://github.com/faisalman/ua-parser-js
Author: Faisal Salman <f@faisalman.com> Author: Faisal Salman <f@faisalman.com>
AGPLv3 License */ AGPLv3 License */

View File

@ -1,4 +1,4 @@
// Type definitions for UAParser.js v2.0.0-beta.2 // Type definitions for UAParser.js v2.0.0-beta.3
// Project: https://github.com/faisalman/ua-parser-js // Project: https://github.com/faisalman/ua-parser-js
// Definitions by: Faisal Salman <https://github.com/faisalman> // Definitions by: Faisal Salman <https://github.com/faisalman>
@ -15,6 +15,7 @@ declare namespace UAParser {
name?: string; name?: string;
version?: string; version?: string;
major?: string; major?: string;
type?: 'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'module';
} }
interface ICPU extends IData<ICPU> { interface ICPU extends IData<ICPU> {
@ -22,7 +23,7 @@ declare namespace UAParser {
} }
interface IDevice extends IData<IDevice> { interface IDevice extends IData<IDevice> {
type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable'; type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable' | 'xr' | 'embedded';
vendor?: string; vendor?: string;
model?: string; model?: string;
} }
@ -48,7 +49,7 @@ declare namespace UAParser {
type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[]; type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[];
type UAParserProps = 'browser' | 'cpu' | 'device' | 'engine' | 'os'; type UAParserProps = 'browser' | 'cpu' | 'device' | 'engine' | 'os';
type UAParserExt = Partial<Record<UAParserProps, RegexMap>>; type UAParserExt = Partial<Record<UAParserProps, RegexMap>> | Partial<Record<UAParserProps, RegexMap>>[];
export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: Record<string, string>): IResult; export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: Record<string, string>): IResult;
export function UAParser(uastring?: string, headers?: Record<string, string>): IResult; export function UAParser(uastring?: string, headers?: Record<string, string>): IResult;
@ -61,6 +62,7 @@ declare namespace UAParser {
NAME: 'name'; NAME: 'name';
VERSION: 'version'; VERSION: 'version';
MAJOR: 'major'; MAJOR: 'major';
TYPE: 'type';
}; };
static readonly CPU: { static readonly CPU: {
ARCHITECTURE: 'architecture'; ARCHITECTURE: 'architecture';
@ -74,6 +76,7 @@ declare namespace UAParser {
SMARTTV: 'smarttv'; SMARTTV: 'smarttv';
TABLET: 'tablet'; TABLET: 'tablet';
WEARABLE: 'wearable'; WEARABLE: 'wearable';
XR: 'xr';
EMBEDDED: 'embedded'; EMBEDDED: 'embedded';
}; };
static readonly ENGINE: { static readonly ENGINE: {

View File

@ -1,10 +1,10 @@
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v2.0.0-beta.2 /* UAParser.js v2.0.0-beta.3
Copyright © 2012-2023 Faisal Salman <f@faisalman.com> Copyright © 2012-2023 Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License *//* UAParser.js PRO Business License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
Supports browser & node.js environment. Supports browser & node.js environment.
Demo : https://faisalman.github.io/ua-parser-js Demo : https://uaparser.dev
Source : https://github.com/faisalman/ua-parser-js */ Source : https://github.com/faisalman/ua-parser-js */
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
@ -19,7 +19,7 @@
// Constants // Constants
///////////// /////////////
var LIBVERSION = '2.0.0-beta.2', var LIBVERSION = '2.0.0-beta.3',
EMPTY = '', EMPTY = '',
UNKNOWN = '?', UNKNOWN = '?',
FUNC_TYPE = 'function', FUNC_TYPE = 'function',
@ -38,11 +38,12 @@
TABLET = 'tablet', TABLET = 'tablet',
SMARTTV = 'smarttv', SMARTTV = 'smarttv',
WEARABLE = 'wearable', WEARABLE = 'wearable',
XR = 'xr',
EMBEDDED = 'embedded', EMBEDDED = 'embedded',
USER_AGENT = 'user-agent', USER_AGENT = 'user-agent',
UA_MAX_LENGTH = 500, UA_MAX_LENGTH = 500,
BRANDS = 'brands', BRANDS = 'brands',
FORMFACTOR = 'formFactor', FORMFACTORS = 'formFactors',
FULLVERLIST = 'fullVersionList', FULLVERLIST = 'fullVersionList',
PLATFORM = 'platform', PLATFORM = 'platform',
PLATFORMVER = 'platformVersion', PLATFORMVER = 'platformVersion',
@ -51,12 +52,12 @@
CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list',
CH_HEADER_ARCH = CH_HEADER + '-arch', CH_HEADER_ARCH = CH_HEADER + '-arch',
CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS,
CH_HEADER_FORM_FACTOR = CH_HEADER + '-form-factor', CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors',
CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE,
CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, CH_HEADER_MODEL = CH_HEADER + '-' + MODEL,
CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM,
CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version',
CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS], CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS],
UA_BROWSER = 'browser', UA_BROWSER = 'browser',
UA_CPU = 'cpu', UA_CPU = 'cpu',
UA_DEVICE = 'device', UA_DEVICE = 'device',
@ -100,12 +101,21 @@
// Helper // Helper
////////// //////////
var extend = function (regexes, extensions) { var extend = function (defaultRgx, extensions) {
var mergedRegexes = {}; var mergedRgx = {};
for (var i in regexes) { var extraRgx = extensions;
mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; if (!isExtensions(extensions)) {
extraRgx = {};
for (var i in extensions) {
for (var j in extensions[i]) {
extraRgx[j] = extensions[i][j].concat(extraRgx[j] ? extraRgx[j] : []);
} }
return mergedRegexes; }
}
for (var k in defaultRgx) {
mergedRgx[k] = extraRgx[k] && extraRgx[k].length % 2 === 0 ? extraRgx[k].concat(defaultRgx[k]) : defaultRgx[k];
}
return mergedRgx;
}, },
enumerize = function (arr) { enumerize = function (arr) {
var enums = {}; var enums = {};
@ -123,9 +133,9 @@
} }
return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
}, },
isExtensions = function (obj) { isExtensions = function (obj, deep) {
for (var prop in obj) { for (var prop in obj) {
return /^(browser|cpu|device|engine|os)$/.test(prop); return /^(browser|cpu|device|engine|os)$/.test(prop) || (deep ? isExtensions(obj[prop]) : false);
} }
}, },
isString = function (val) { isString = function (val) {
@ -269,12 +279,13 @@
'RT' : 'ARM' 'RT' : 'ARM'
}, },
formFactorMap = { formFactorsMap = {
'embedded' : 'Automotive', 'embedded' : 'Automotive',
'mobile' : 'Mobile', 'mobile' : 'Mobile',
'tablet' : ['Tablet', 'EInk'], 'tablet' : ['Tablet', 'EInk'],
'smarttv' : 'TV', 'smarttv' : 'TV',
'wearable' : ['VR', 'XR', 'Watch'], 'wearable' : 'Watch',
'xr' : ['VR', 'XR'],
'?' : ['Desktop', 'Unknown'], '?' : ['Desktop', 'Unknown'],
'*' : undefined '*' : undefined
}; };
@ -309,17 +320,20 @@
/\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu
], [VERSION, [NAME, 'Baidu']], [ ], [VERSION, [NAME, 'Baidu']], [
/(kindle)\/([\w\.]+)/i, // Kindle /(kindle)\/([\w\.]+)/i, // Kindle
/(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i,
// Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir
// Trident based // Trident based
/(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser
/(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer
// Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon
/(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar)\/([-\w\.]+)/i,
// Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar
/(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi
/(weibo)__([\d\.]+)/i // Weibo /(weibo)__([\d\.]+)/i // Weibo
], [NAME, VERSION], [ ], [NAME, VERSION], [
/\bddg\/([\w\.]+)/i // DuckDuckGo
], [VERSION, [NAME, 'DuckDuckGo']], [
/(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser
], [VERSION, [NAME, 'UCBrowser']], [ ], [VERSION, [NAME, 'UCBrowser']], [
/microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser
@ -352,8 +366,10 @@
], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [
/\bqihu|(qi?ho?o?|360)browser/i // 360 /\bqihu|(qi?ho?o?|360)browser/i // 360
], [[NAME, '360' + SUFFIX_BROWSER]], [ ], [[NAME, '360' + SUFFIX_BROWSER]], [
/(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i /\b(qq)\/([\w\.]+)/i // QQ
], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser ], [[NAME, /(.+)/, '$1Browser'], VERSION], [
/(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i
], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser
/samsungbrowser\/([\w\.]+)/i // Samsung Internet /samsungbrowser\/([\w\.]+)/i // Samsung Internet
], [VERSION, [NAME, SAMSUNG + ' Internet']], [ ], [VERSION, [NAME, SAMSUNG + ' Internet']], [
/(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
@ -366,7 +382,7 @@
/(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla
/m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser
], [NAME, VERSION], [ ], [NAME, VERSION], [
/(lbbrowser)/i, // LieBao Browser /(lbbrowser|rekonq)/i, // LieBao Browser/Rekonq
/\[(linkedin)app\]/i // LinkedIn App for iOS & Android /\[(linkedin)app\]/i // LinkedIn App for iOS & Android
], [NAME], [ ], [NAME], [
@ -379,6 +395,7 @@
/safari (line)\/([\w\.]+)/i, // Line App for iOS /safari (line)\/([\w\.]+)/i, // Line App for iOS
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
/(alipay)client\/([\w\.]+)/i, // Alipay /(alipay)client\/([\w\.]+)/i, // Alipay
/(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter
/(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat
], [NAME, VERSION], [ ], [NAME, VERSION], [
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
@ -418,23 +435,24 @@
], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [
/(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape
], [[NAME, 'Netscape'], VERSION], [ ], [[NAME, 'Netscape'], VERSION], [
/(wolvic)\/([\w\.]+)/i // Wolvic
], [NAME, VERSION], [
/mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality
], [VERSION, [NAME, FIREFOX+' Reality']], [ ], [VERSION, [NAME, FIREFOX+' Reality']], [
/ekiohf.+(flow)\/([\w\.]+)/i, // Flow /ekiohf.+(flow)\/([\w\.]+)/i, // Flow
/(swiftfox)/i, // Swiftfox /(swiftfox)/i, // Swiftfox
/(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i, /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i,
// IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar // 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|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i,
// Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
/(firefox)\/([\w\.]+)/i, // Other Firefox-based /(firefox)\/([\w\.]+)/i, // Other Firefox-based
/(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla
// Other // Other
/(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
// Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser
/(links) \(([\w\.]+)/i, // Links /(links) \(([\w\.]+)/i // Links
/panasonic;(viera)/i // Panasonic Viera ], [NAME, [VERSION, /_/g, '.']], [
], [NAME, VERSION], [
/(cobalt)\/([\w\.]+)/i // Cobalt /(cobalt)\/([\w\.]+)/i // Cobalt
], [NAME, [VERSION, /[^\d\.]+./, EMPTY]] ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]]
@ -521,6 +539,8 @@
/; (\w+) bui.+ oppo/i, /; (\w+) bui.+ oppo/i,
/\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i
], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [ ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [
/\b(opd2\d{3}a?) bui/i
], [MODEL, [VENDOR, 'OPPO'], [TYPE, TABLET]], [
// Vivo // Vivo
/vivo (\w+)(?: bui|\))/i, /vivo (\w+)(?: bui|\))/i,
@ -704,12 +724,17 @@
], [VENDOR, MODEL, [TYPE, WEARABLE]], [ ], [VENDOR, MODEL, [TYPE, WEARABLE]], [
/(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch
], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [
/droid.+; (glass) \d/i // Google Glass
], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [
/droid.+; (wt63?0{2,3})\)/i /droid.+; (wt63?0{2,3})\)/i
], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [
/(quest( 2| pro)?)/i // Oculus Quest
], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [ ///////////////////
// XR
///////////////////
/droid.+; (glass) \d/i // Google Glass
], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [
/(quest( \d| pro)?)/i // Oculus Quest
], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [
/////////////////// ///////////////////
// EMBEDDED // EMBEDDED
@ -840,7 +865,7 @@
var defaultProps = (function () { var defaultProps = (function () {
var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
setProps.call(props.init, [ setProps.call(props.init, [
[UA_BROWSER, [NAME, VERSION, MAJOR]], [UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]],
[UA_CPU, [ARCHITECTURE]], [UA_CPU, [ARCHITECTURE]],
[UA_DEVICE, [TYPE, MODEL, VENDOR]], [UA_DEVICE, [TYPE, MODEL, VENDOR]],
[UA_ENGINE, [NAME, VERSION]], [UA_ENGINE, [NAME, VERSION]],
@ -968,7 +993,7 @@
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
[PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
[ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
[FORMFACTOR, itemListToArray(uach[CH_HEADER_FORM_FACTOR])], [FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])],
[BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
]); ]);
} else { } else {
@ -1088,15 +1113,15 @@
this.set(TYPE, CONSOLE) this.set(TYPE, CONSOLE)
.set(VENDOR, MICROSOFT); .set(VENDOR, MICROSOFT);
} }
if (uaCH[FORMFACTOR]) { if (uaCH[FORMFACTORS]) {
var ff; var ff;
if (typeof uaCH[FORMFACTOR] !== 'string') { if (typeof uaCH[FORMFACTORS] !== 'string') {
var idx = 0; var idx = 0;
while (!ff && idx < uaCH[FORMFACTOR].length) { while (!ff && idx < uaCH[FORMFACTORS].length) {
ff = strMapper(uaCH[FORMFACTOR][idx++], formFactorMap); ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap);
} }
} else { } else {
ff = strMapper(uaCH[FORMFACTOR], formFactorMap); ff = strMapper(uaCH[FORMFACTORS], formFactorsMap);
} }
this.set(TYPE, ff); this.set(TYPE, ff);
} }
@ -1147,7 +1172,7 @@
function UAParser (ua, extensions, headers) { function UAParser (ua, extensions, headers) {
if (typeof ua === OBJ_TYPE) { if (typeof ua === OBJ_TYPE) {
if (isExtensions(ua)) { if (isExtensions(ua, true)) {
if (typeof extensions === OBJ_TYPE) { if (typeof extensions === OBJ_TYPE) {
headers = extensions; // case UAParser(extensions, headers) headers = extensions; // case UAParser(extensions, headers)
} }
@ -1157,7 +1182,7 @@
extensions = undefined; extensions = undefined;
} }
ua = undefined; ua = undefined;
} else if (typeof ua === STR_TYPE && !isExtensions(extensions)) { } else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) {
headers = extensions; // case UAParser(ua, headers) headers = extensions; // case UAParser(ua, headers)
extensions = undefined; extensions = undefined;
} }
@ -1218,7 +1243,7 @@
} }
UAParser.VERSION = LIBVERSION; UAParser.VERSION = LIBVERSION;
UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]); UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR, TYPE]);
UAParser.CPU = enumerize([ARCHITECTURE]); UAParser.CPU = enumerize([ARCHITECTURE]);
UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]); UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]);
UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]); UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]);

View File

@ -3,12 +3,12 @@
// Source: /src/main/ua-parser.js // Source: /src/main/ua-parser.js
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
/* UAParser.js v2.0.0-beta.2 /* UAParser.js v2.0.0-beta.3
Copyright © 2012-2023 Faisal Salman <f@faisalman.com> Copyright © 2012-2023 Faisal Salman <f@faisalman.com>
UAParser.js PRO Business License *//* UAParser.js PRO Business License *//*
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
Supports browser & node.js environment. Supports browser & node.js environment.
Demo : https://faisalman.github.io/ua-parser-js Demo : https://uaparser.dev
Source : https://github.com/faisalman/ua-parser-js */ Source : https://github.com/faisalman/ua-parser-js */
///////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////
@ -21,7 +21,7 @@
// Constants // Constants
///////////// /////////////
var LIBVERSION = '2.0.0-beta.2', var LIBVERSION = '2.0.0-beta.3',
EMPTY = '', EMPTY = '',
UNKNOWN = '?', UNKNOWN = '?',
FUNC_TYPE = 'function', FUNC_TYPE = 'function',
@ -40,11 +40,12 @@
TABLET = 'tablet', TABLET = 'tablet',
SMARTTV = 'smarttv', SMARTTV = 'smarttv',
WEARABLE = 'wearable', WEARABLE = 'wearable',
XR = 'xr',
EMBEDDED = 'embedded', EMBEDDED = 'embedded',
USER_AGENT = 'user-agent', USER_AGENT = 'user-agent',
UA_MAX_LENGTH = 500, UA_MAX_LENGTH = 500,
BRANDS = 'brands', BRANDS = 'brands',
FORMFACTOR = 'formFactor', FORMFACTORS = 'formFactors',
FULLVERLIST = 'fullVersionList', FULLVERLIST = 'fullVersionList',
PLATFORM = 'platform', PLATFORM = 'platform',
PLATFORMVER = 'platformVersion', PLATFORMVER = 'platformVersion',
@ -53,12 +54,12 @@
CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list',
CH_HEADER_ARCH = CH_HEADER + '-arch', CH_HEADER_ARCH = CH_HEADER + '-arch',
CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS,
CH_HEADER_FORM_FACTOR = CH_HEADER + '-form-factor', CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors',
CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE,
CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, CH_HEADER_MODEL = CH_HEADER + '-' + MODEL,
CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM,
CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version',
CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTOR, BITNESS], CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS],
UA_BROWSER = 'browser', UA_BROWSER = 'browser',
UA_CPU = 'cpu', UA_CPU = 'cpu',
UA_DEVICE = 'device', UA_DEVICE = 'device',
@ -102,12 +103,21 @@
// Helper // Helper
////////// //////////
var extend = function (regexes, extensions) { var extend = function (defaultRgx, extensions) {
var mergedRegexes = {}; var mergedRgx = {};
for (var i in regexes) { var extraRgx = extensions;
mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i]; if (!isExtensions(extensions)) {
extraRgx = {};
for (var i in extensions) {
for (var j in extensions[i]) {
extraRgx[j] = extensions[i][j].concat(extraRgx[j] ? extraRgx[j] : []);
} }
return mergedRegexes; }
}
for (var k in defaultRgx) {
mergedRgx[k] = extraRgx[k] && extraRgx[k].length % 2 === 0 ? extraRgx[k].concat(defaultRgx[k]) : defaultRgx[k];
}
return mergedRgx;
}, },
enumerize = function (arr) { enumerize = function (arr) {
var enums = {}; var enums = {};
@ -125,9 +135,9 @@
} }
return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
}, },
isExtensions = function (obj) { isExtensions = function (obj, deep) {
for (var prop in obj) { for (var prop in obj) {
return /^(browser|cpu|device|engine|os)$/.test(prop); return /^(browser|cpu|device|engine|os)$/.test(prop) || (deep ? isExtensions(obj[prop]) : false);
} }
}, },
isString = function (val) { isString = function (val) {
@ -271,12 +281,13 @@
'RT' : 'ARM' 'RT' : 'ARM'
}, },
formFactorMap = { formFactorsMap = {
'embedded' : 'Automotive', 'embedded' : 'Automotive',
'mobile' : 'Mobile', 'mobile' : 'Mobile',
'tablet' : ['Tablet', 'EInk'], 'tablet' : ['Tablet', 'EInk'],
'smarttv' : 'TV', 'smarttv' : 'TV',
'wearable' : ['VR', 'XR', 'Watch'], 'wearable' : 'Watch',
'xr' : ['VR', 'XR'],
'?' : ['Desktop', 'Unknown'], '?' : ['Desktop', 'Unknown'],
'*' : undefined '*' : undefined
}; };
@ -311,17 +322,20 @@
/\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu
], [VERSION, [NAME, 'Baidu']], [ ], [VERSION, [NAME, 'Baidu']], [
/(kindle)\/([\w\.]+)/i, // Kindle /(kindle)\/([\w\.]+)/i, // Kindle
/(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i,
// Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir
// Trident based // Trident based
/(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser
/(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer
// Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon
/(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar)\/([-\w\.]+)/i,
// Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar
/(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi /(heytap|ovi)browser\/([\d\.]+)/i, // HeyTap/Ovi
/(weibo)__([\d\.]+)/i // Weibo /(weibo)__([\d\.]+)/i // Weibo
], [NAME, VERSION], [ ], [NAME, VERSION], [
/\bddg\/([\w\.]+)/i // DuckDuckGo
], [VERSION, [NAME, 'DuckDuckGo']], [
/(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser
], [VERSION, [NAME, 'UCBrowser']], [ ], [VERSION, [NAME, 'UCBrowser']], [
/microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser
@ -354,8 +368,10 @@
], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [
/\bqihu|(qi?ho?o?|360)browser/i // 360 /\bqihu|(qi?ho?o?|360)browser/i // 360
], [[NAME, '360' + SUFFIX_BROWSER]], [ ], [[NAME, '360' + SUFFIX_BROWSER]], [
/(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i /\b(qq)\/([\w\.]+)/i // QQ
], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser ], [[NAME, /(.+)/, '$1Browser'], VERSION], [
/(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i
], [[NAME, /(.+)/, '$1' + SUFFIX_BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser
/samsungbrowser\/([\w\.]+)/i // Samsung Internet /samsungbrowser\/([\w\.]+)/i // Samsung Internet
], [VERSION, [NAME, SAMSUNG + ' Internet']], [ ], [VERSION, [NAME, SAMSUNG + ' Internet']], [
/(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
@ -368,7 +384,7 @@
/(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla
/m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser
], [NAME, VERSION], [ ], [NAME, VERSION], [
/(lbbrowser)/i, // LieBao Browser /(lbbrowser|rekonq)/i, // LieBao Browser/Rekonq
/\[(linkedin)app\]/i // LinkedIn App for iOS & Android /\[(linkedin)app\]/i // LinkedIn App for iOS & Android
], [NAME], [ ], [NAME], [
@ -381,6 +397,7 @@
/safari (line)\/([\w\.]+)/i, // Line App for iOS /safari (line)\/([\w\.]+)/i, // Line App for iOS
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
/(alipay)client\/([\w\.]+)/i, // Alipay /(alipay)client\/([\w\.]+)/i, // Alipay
/(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter
/(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat
], [NAME, VERSION], [ ], [NAME, VERSION], [
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
@ -420,23 +437,24 @@
], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [
/(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape
], [[NAME, 'Netscape'], VERSION], [ ], [[NAME, 'Netscape'], VERSION], [
/(wolvic)\/([\w\.]+)/i // Wolvic
], [NAME, VERSION], [
/mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality
], [VERSION, [NAME, FIREFOX+' Reality']], [ ], [VERSION, [NAME, FIREFOX+' Reality']], [
/ekiohf.+(flow)\/([\w\.]+)/i, // Flow /ekiohf.+(flow)\/([\w\.]+)/i, // Flow
/(swiftfox)/i, // Swiftfox /(swiftfox)/i, // Swiftfox
/(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i, /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i,
// IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar // 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|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i,
// Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
/(firefox)\/([\w\.]+)/i, // Other Firefox-based /(firefox)\/([\w\.]+)/i, // Other Firefox-based
/(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla
// Other // Other
/(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
// Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser
/(links) \(([\w\.]+)/i, // Links /(links) \(([\w\.]+)/i // Links
/panasonic;(viera)/i // Panasonic Viera ], [NAME, [VERSION, /_/g, '.']], [
], [NAME, VERSION], [
/(cobalt)\/([\w\.]+)/i // Cobalt /(cobalt)\/([\w\.]+)/i // Cobalt
], [NAME, [VERSION, /[^\d\.]+./, EMPTY]] ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]]
@ -523,6 +541,8 @@
/; (\w+) bui.+ oppo/i, /; (\w+) bui.+ oppo/i,
/\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i
], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [ ], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [
/\b(opd2\d{3}a?) bui/i
], [MODEL, [VENDOR, 'OPPO'], [TYPE, TABLET]], [
// Vivo // Vivo
/vivo (\w+)(?: bui|\))/i, /vivo (\w+)(?: bui|\))/i,
@ -706,12 +726,17 @@
], [VENDOR, MODEL, [TYPE, WEARABLE]], [ ], [VENDOR, MODEL, [TYPE, WEARABLE]], [
/(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch
], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [
/droid.+; (glass) \d/i // Google Glass
], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [
/droid.+; (wt63?0{2,3})\)/i /droid.+; (wt63?0{2,3})\)/i
], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [
/(quest( 2| pro)?)/i // Oculus Quest
], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [ ///////////////////
// XR
///////////////////
/droid.+; (glass) \d/i // Google Glass
], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [
/(quest( \d| pro)?)/i // Oculus Quest
], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [
/////////////////// ///////////////////
// EMBEDDED // EMBEDDED
@ -842,7 +867,7 @@
var defaultProps = (function () { var defaultProps = (function () {
var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
setProps.call(props.init, [ setProps.call(props.init, [
[UA_BROWSER, [NAME, VERSION, MAJOR]], [UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]],
[UA_CPU, [ARCHITECTURE]], [UA_CPU, [ARCHITECTURE]],
[UA_DEVICE, [TYPE, MODEL, VENDOR]], [UA_DEVICE, [TYPE, MODEL, VENDOR]],
[UA_ENGINE, [NAME, VERSION]], [UA_ENGINE, [NAME, VERSION]],
@ -970,7 +995,7 @@
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
[PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
[ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
[FORMFACTOR, itemListToArray(uach[CH_HEADER_FORM_FACTOR])], [FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])],
[BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
]); ]);
} else { } else {
@ -1090,15 +1115,15 @@
this.set(TYPE, CONSOLE) this.set(TYPE, CONSOLE)
.set(VENDOR, MICROSOFT); .set(VENDOR, MICROSOFT);
} }
if (uaCH[FORMFACTOR]) { if (uaCH[FORMFACTORS]) {
var ff; var ff;
if (typeof uaCH[FORMFACTOR] !== 'string') { if (typeof uaCH[FORMFACTORS] !== 'string') {
var idx = 0; var idx = 0;
while (!ff && idx < uaCH[FORMFACTOR].length) { while (!ff && idx < uaCH[FORMFACTORS].length) {
ff = strMapper(uaCH[FORMFACTOR][idx++], formFactorMap); ff = strMapper(uaCH[FORMFACTORS][idx++], formFactorsMap);
} }
} else { } else {
ff = strMapper(uaCH[FORMFACTOR], formFactorMap); ff = strMapper(uaCH[FORMFACTORS], formFactorsMap);
} }
this.set(TYPE, ff); this.set(TYPE, ff);
} }
@ -1149,7 +1174,7 @@
function UAParser (ua, extensions, headers) { function UAParser (ua, extensions, headers) {
if (typeof ua === OBJ_TYPE) { if (typeof ua === OBJ_TYPE) {
if (isExtensions(ua)) { if (isExtensions(ua, true)) {
if (typeof extensions === OBJ_TYPE) { if (typeof extensions === OBJ_TYPE) {
headers = extensions; // case UAParser(extensions, headers) headers = extensions; // case UAParser(extensions, headers)
} }
@ -1159,7 +1184,7 @@
extensions = undefined; extensions = undefined;
} }
ua = undefined; ua = undefined;
} else if (typeof ua === STR_TYPE && !isExtensions(extensions)) { } else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) {
headers = extensions; // case UAParser(ua, headers) headers = extensions; // case UAParser(ua, headers)
extensions = undefined; extensions = undefined;
} }
@ -1220,7 +1245,7 @@
} }
UAParser.VERSION = LIBVERSION; UAParser.VERSION = LIBVERSION;
UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]); UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR, TYPE]);
UAParser.CPU = enumerize([ARCHITECTURE]); UAParser.CPU = enumerize([ARCHITECTURE]);
UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]); UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]);
UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]); UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]);

View File

@ -28,6 +28,7 @@ expectType<IBrowser>(browser);
expectType<string | undefined>(browser.name); expectType<string | undefined>(browser.name);
expectType<string | undefined>(browser.version); expectType<string | undefined>(browser.version);
expectType<string | undefined>(browser.major); expectType<string | undefined>(browser.major);
expectType<'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'module' | undefined>(browser.type);
expectType<boolean>(browser.is('')); expectType<boolean>(browser.is(''));
expectType<string>(browser.toString()); expectType<string>(browser.toString());
expectType<IBrowser | PromiseLike<IBrowser>>(browser.withClientHints()); expectType<IBrowser | PromiseLike<IBrowser>>(browser.withClientHints());

View File

@ -1,5 +1,5 @@
const { FuzzedDataProvider } = require('@jazzer.js/core'); const { FuzzedDataProvider } = require('@jazzer.js/core');
const UAParser = require('ua-parser-js'); const { UAParser } = require('../src/main/ua-parser');
const UA_MAX_LENGTH = 350; const UA_MAX_LENGTH = 350;
module.exports.fuzz = function (buffer) { module.exports.fuzz = function (buffer) {

View File

@ -7,8 +7,7 @@ describe('Returns', () => {
assert.deepEqual(new UAParser('').getResult(), assert.deepEqual(new UAParser('').getResult(),
{ {
ua : '', ua : '',
//ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined }, browser: { name: undefined, version: undefined, major: undefined, type: undefined },
browser: { name: undefined, version: undefined, major: undefined },
cpu: { architecture: undefined }, cpu: { architecture: undefined },
device: { vendor: undefined, model: undefined, type: undefined }, device: { vendor: undefined, model: undefined, type: undefined },
engine: { name: undefined, version: undefined}, engine: { name: undefined, version: undefined},

View File

@ -3,37 +3,41 @@ const assert = require('assert');
const parseJS = require('@babel/parser').parse; const parseJS = require('@babel/parser').parse;
const traverse = require('@babel/traverse').default; const traverse = require('@babel/traverse').default;
const safe = require('safe-regex'); const safe = require('safe-regex');
const UAParser = require('../src/main/ua-parser'); const { UAParser } = require('../src/main/ua-parser');
const { Bots, CLIs, Emails, Modules } = require('../src/extensions/ua-parser-extensions'); const clis = require('./specs/browser-clis.json');
const crawlers = require('./specs/browser-crawlers.json');
const emails = require('./specs/browser-emails.json');
const fetchers = require('./specs/browser-fetchers.json');
const modules = require('./specs/browser-modules.json');
const { CLIs, Crawlers, Emails, Fetchers, Modules } = require('../src/extensions/ua-parser-extensions');
describe('Extensions', () => {
[
['CLIs', clis, CLIs],
['Crawlers', crawlers, Crawlers],
['Emails', emails, Emails],
['Fetchers', fetchers, Fetchers],
['Modules', modules, Modules]
]
.forEach((list) => {
describe(list[0], () => {
list[1].forEach((agent) => {
it(`Can detect ${agent.desc}`, () => {
let browser = UAParser(agent.ua, list[2]).browser;
assert.strictEqual(String(browser.name), agent.expect.name);
assert.strictEqual(String(browser.version), agent.expect.version);
assert.strictEqual(String(browser.type), agent.expect.type);
});
});
});
});
describe('Bots', () => {
it('Can detect bots', () => {
const googleBot = 'Googlebot-Video/1.0';
const gptBot = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)';
const msnBot = 'msnbot-media/1.1 (+http://search.msn.com/msnbot.htm)';
const bingPreview = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b';
const opera = 'Opera/8.5 (Macintosh; PPC Mac OS X; U; en)';
const wget = 'Wget/1.21.1';
const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)';
const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)'; const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)';
const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0';
const axios = 'axios/1.3.5'; const axios = 'axios/1.3.5';
const jsdom = 'Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3'; const jsdom = 'Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3';
const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)'; const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)';
const botParser = new UAParser(Bots);
assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"});
assert.deepEqual(botParser.setUA(gptBot).getBrowser(), {name: "GPTBot", version: "1.0", major: "1", type: "bot"});
assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"});
assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"});
assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8"});
// try merging Bots & CLIs
const botsAndCLIs = { browser : [...Bots.browser, ...CLIs.browser]};
const botsAndCLIsParser = new UAParser(botsAndCLIs);
assert.deepEqual(botsAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"});
assert.deepEqual(botsAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"});
const emailParser = new UAParser(Emails); const emailParser = new UAParser(Emails);
assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"}); assert.deepEqual(emailParser.setUA(outlook).getBrowser(), {name: "Microsoft Outlook", version: "16.0.9126", major: "16", type: "email"});
assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); assert.deepEqual(emailParser.setUA(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"});
@ -43,15 +47,29 @@ describe('Bots', () => {
assert.deepEqual(moduleParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "module"}); assert.deepEqual(moduleParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "module"});
assert.deepEqual(moduleParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "module"}); assert.deepEqual(moduleParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "module"});
}); });
});
// TODO : move test spec to JSON file describe('Merge', () => {
it('Can merge multiple extensions', () => {
const wget = 'Wget/1.21.1';
const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)';
// try merging crawlers & CLIs
const crawlersAndCLIs = { browser : [...Crawlers.browser, ...CLIs.browser]};
const crawlersAndCLIsParser = new UAParser(crawlersAndCLIs);
assert.deepEqual(crawlersAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"});
assert.deepEqual(crawlersAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"crawler"});
// alternative merge options
const crawlersAndCLIsParser2 = new UAParser([Crawlers, CLIs]);
const crawlersAndCLIsParser3 = new UAParser(facebookBot, [Crawlers, CLIs]);
assert.deepEqual(crawlersAndCLIsParser2.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"});
assert.deepEqual(crawlersAndCLIsParser3.getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"crawler"});
});
});
describe('Testing regexes', () => { describe('Testing regexes', () => {
let regexes; let regexes;
before('Read main js file', () => {
let code = fs.readFileSync('src/extensions/ua-parser-extensions.js', 'utf8').toString(); let code = fs.readFileSync('src/extensions/ua-parser-extensions.js', 'utf8').toString();
let ast = parseJS(code, { sourceType: 'script' }); let ast = parseJS(code, { sourceType: 'script' });
regexes = []; regexes = [];
@ -60,20 +78,16 @@ describe('Testing regexes', () => {
regexes.push(path.node.pattern); regexes.push(path.node.pattern);
} }
}); });
if (regexes.length === 0) { if (regexes.length === 0) {
throw new Error('Regexes cannot be empty!'); throw new Error('Regexes cannot be empty!');
} }
});
describe('Begin testing', () => { describe('Checking for potentially vulnerable regex', () => {
it('all regexes in extension file', () => { for (let regex of regexes) {
regexes.forEach(regex => { it('Test against `safe-regex` : ' + regex, () => {
describe('Test against `safe-regex` : ' + regex, () => {
it('should be safe from potentially vulnerable regex', () => {
assert.strictEqual(safe(regex), true); assert.strictEqual(safe(regex), true);
}); });
}); }
});
});
}); });
}); });

View File

@ -1,5 +1,5 @@
const assert = require('assert'); const assert = require('assert');
const UAParser = require('../src/main/ua-parser'); const { UAParser } = require('../src/main/ua-parser');
const { isAppleSilicon, isChromiumBased } = require('../src/helpers/ua-parser-helpers'); const { isAppleSilicon, isChromiumBased } = require('../src/helpers/ua-parser-helpers');
describe('isAppleSilicon', () => { describe('isAppleSilicon', () => {

View File

@ -4,12 +4,13 @@ var assert = require('assert');
var requirejs = require('requirejs'); var requirejs = require('requirejs');
var parseJS = require('@babel/parser').parse; var parseJS = require('@babel/parser').parse;
var traverse = require('@babel/traverse').default; var traverse = require('@babel/traverse').default;
var UAParser = require('../src/main/ua-parser'); var {UAParser} = require('../src/main/ua-parser');
var browsers = require('./specs/browser-all.json'); var browsers = require('./specs/browser-all.json');
var cpus = require('./specs/cpu-all.json'); var cpus = require('./specs/cpu-all.json');
var devices = require('./specs/device-all.json'); var devices = require('./specs/device-all.json');
var engines = require('./specs/engine-all.json'); var engines = require('./specs/engine-all.json');
var os = require('./specs/os-all.json'); var os = require('./specs/os-all.json');
var parser = new UAParser(); var parser = new UAParser();
var methods = [ var methods = [
{ {
@ -82,7 +83,7 @@ describe('Returns', function () {
assert.deepEqual(new UAParser('').getResult(), assert.deepEqual(new UAParser('').getResult(),
{ {
ua : '', ua : '',
browser: { name: undefined, version: undefined, major: undefined }, browser: { name: undefined, version: undefined, major: undefined, type: undefined },
cpu: { architecture: undefined }, cpu: { architecture: undefined },
device: { vendor: undefined, model: undefined, type: undefined }, device: { vendor: undefined, model: undefined, type: undefined },
engine: { name: undefined, version: undefined}, engine: { name: undefined, version: undefined},
@ -125,6 +126,13 @@ describe('Extending Regex', function () {
}); });
let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max'; let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max';
assert.deepEqual(myParser2.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}); assert.deepEqual(myParser2.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"});
let myParser3 = new UAParser([{
browser: myOwnListOfBrowsers
}, {
device: myOwnListOfDevices
}]);
assert.deepEqual(myParser3.setUA(myUA2).getDevice(), {vendor: "MyTab", model: "14 Pro Max", type: "tablet"});
}); });
describe('User-agent length', function () { describe('User-agent length', function () {
@ -463,22 +471,22 @@ describe('Map UA-CH headers', function () {
}); });
}); });
it('Can detect form-factor from client-hints', function () { it('Can detect form-factors from client-hints', function () {
const FFVR = { const FFVR = {
'sec-ch-ua-form-factor' : '"VR"' 'sec-ch-ua-form-factors' : '"VR"'
}; };
const FFEInk = { const FFEInk = {
'sec-ch-ua-form-factor' : '"Tablet", "EInk"' 'sec-ch-ua-form-factors' : '"Tablet", "EInk"'
}; };
const FFUnknown = { const FFUnknown = {
'sec-ch-ua-form-factor' : '"Unknown"' 'sec-ch-ua-form-factors' : '"Unknown"'
}; };
UAParser(FFVR).withClientHints().then(function (ua) { UAParser(FFVR).withClientHints().then(function (ua) {
assert.strictEqual(ua.device.type, 'wearable'); assert.strictEqual(ua.device.type, 'xr');
}); });
UAParser(FFEInk).withClientHints().then(function (ua) { UAParser(FFEInk).withClientHints().then(function (ua) {

View File

@ -40,7 +40,7 @@ test('read client hints data', async ({ page }) => {
} }
], ],
platform: 'New OS', platform: 'New OS',
formFactor: 'New Form Factor' formFactors: 'New Form Factor'
}); });
} }
} }

View File

@ -209,6 +209,16 @@
"major" : "11" "major" : "11"
} }
}, },
{
"desc" : "Blazer",
"ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; PalmSource/hspr-H102; Blazer/4.0) 16;320x320",
"expect" :
{
"name" : "Blazer",
"version" : "4.0",
"major" : "4"
}
},
{ {
"desc" : "Bolt", "desc" : "Bolt",
"ua" : "Mozilla/5.0 (X11; 78; CentOS; US-en) AppleWebKit/527+ (KHTML, like Gecko) Bolt/0.862 Version/3.0 Safari/523.15", "ua" : "Mozilla/5.0 (X11; 78; CentOS; US-en) AppleWebKit/527+ (KHTML, like Gecko) Bolt/0.862 Version/3.0 Safari/523.15",
@ -389,6 +399,26 @@
"major" : "78" "major" : "78"
} }
}, },
{
"desc" : "Comodo Dragon",
"ua" : "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/535.7 (KHTML, like Gecko) Comodo_Dragon/16.1.1.0 Chrome/16.0.912.63 Safari/535.7",
"expect" :
{
"name" : "Comodo Dragon",
"version" : "16.1.1.0",
"major" : "16"
}
},
{
"desc" : "Conkeror",
"ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:6.0.1) Gecko/20110831 conkeror/0.9.3",
"expect" :
{
"name" : "conkeror",
"version" : "0.9.3",
"major" : "0"
}
},
{ {
"desc" : "Dillo", "desc" : "Dillo",
"ua" : "Dillo/2.2", "ua" : "Dillo/2.2",
@ -419,6 +449,16 @@
"major" : "1" "major" : "1"
} }
}, },
{
"desc" : "DuckDuckGo",
"ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.1517.4.1 Ddg/17.4.1",
"expect" :
{
"name" : "DuckDuckGo",
"version" : "17.4.1",
"major" : "17"
}
},
{ {
"desc" : "DuckDuckGo", "desc" : "DuckDuckGo",
"ua" : "Mozilla/5.0 (Linux; Android 8.1.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.131 Mobile DuckDuckGo/5 Safari/537.36", "ua" : "Mozilla/5.0 (Linux; Android 8.1.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.131 Mobile DuckDuckGo/5 Safari/537.36",
@ -449,6 +489,16 @@
"major" : "5" "major" : "5"
} }
}, },
{
"desc" : "Go Browser",
"ua" : "NokiaE66/GoBrowser/2.0.297",
"expect" :
{
"name" : "GoBrowser",
"version" : "2.0.297",
"major" : "2"
}
},
{ {
"desc" : "Waterfox", "desc" : "Waterfox",
"ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.2.2 Waterfox/55.2.2", "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.2.2 Waterfox/55.2.2",
@ -708,6 +758,16 @@
"major" : "2" "major" : "2"
} }
}, },
{
"desc" : "ICEBrowser",
"ua" : "Mozilla/5.0 (Java 1.6.0_01; Windows XP 5.1 x86; en) ICEbrowser/v6_1_2",
"expect" :
{
"name" : "ICEbrowser",
"version" : "6.1.2",
"major" : "6"
}
},
{ {
"desc" : "IceCat", "desc" : "IceCat",
"ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092921 IceCat/3.0.3-g1", "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092921 IceCat/3.0.3-g1",
@ -768,6 +828,26 @@
"major" : "11" "major" : "11"
} }
}, },
{
"desc" : "Iron",
"ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1250.0 Iron/22.0.2150.0 Safari/537.4",
"expect" :
{
"name" : "Iron",
"version" : "22.0.2150.0",
"major" : "22"
}
},
{
"desc" : "Jasmine",
"ua" : "SAMSUNG-S8000/S8000XXIF3 SHP/VPP/R5 Jasmine/1.0 Nextreaming SMM-MMS/1.2.0 profile/MIDP-2.1 configuration/CLDC-1.1",
"expect" :
{
"name" : "Jasmine",
"version" : "1.0",
"major" : "1"
}
},
{ {
"desc" : "K-Meleon", "desc" : "K-Meleon",
"ua" : "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031016 K-Meleon/0.8.2", "ua" : "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031016 K-Meleon/0.8.2",
@ -788,6 +868,16 @@
"major" : "2" "major" : "2"
} }
}, },
{
"desc" : "Klar < 4.1",
"ua" : "Mozilla/5.0 (Linux; Android 7.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Klar/1.0 Chrome/58.0.3029.83 Mobile Safari/537.36",
"expect" :
{
"name" : "Klar",
"version" : "1.0",
"major" : "1"
}
},
{ {
"desc" : "Konqueror", "desc" : "Konqueror",
"ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux; X11; x86_64) KHTML/3.5.6 (like Gecko) (Kubuntu)", "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux; X11; x86_64) KHTML/3.5.6 (like Gecko) (Kubuntu)",
@ -808,6 +898,36 @@
"major" : "5" "major" : "5"
} }
}, },
{
"desc" : "PicoBrowser",
"ua" : "Mozilla/5.0 (X11; Linux x86_64; Pico Neo3 Link OS5.8.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36",
"expect" :
{
"name" : "Pico Browser",
"version" : "3.3.22",
"major" : "3"
}
},
{
"desc" : "PicoBrowser",
"ua" : "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.4.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.22 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0",
"expect" :
{
"name" : "Pico Browser",
"version" : "3.3.22",
"major" : "3"
}
},
{
"desc" : "Rekonq",
"ua" : "Mozilla/5.0 (X11; U; Linux x86_64; cs-CZ) AppleWebKit/533.3 (KHTML, like Gecko) rekonq Safari/533.3",
"expect" :
{
"name" : "rekonq",
"version" : "undefined",
"major" : "undefined"
}
},
{ {
"desc" : "Smart Lenovo Browser", "desc" : "Smart Lenovo Browser",
"ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 SLBrowser/8.0.0.10171 SLBChan/8", "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 SLBrowser/8.0.0.10171 SLBChan/8",
@ -1008,6 +1128,26 @@
"major" : "6" "major" : "6"
} }
}, },
{
"desc" : "NetSurf in Plan9",
"ua" : "Mozilla/5.0 (Plan9) NetSurf/3.12",
"expect" :
{
"name" : "NetSurf",
"version" : "3.12",
"major" : "3"
}
},
{
"desc" : "NetSurf in Linux",
"ua" : "NetSurf/3.10 (Linux; Arch Linux)",
"expect" :
{
"name" : "NetSurf",
"version" : "3.10",
"major" : "3"
}
},
{ {
"desc" : "Nokia Browser", "desc" : "Nokia Browser",
"ua" : "Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaN8-00/025.007; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.1.37 Mobile Safari/533.4 3gpp-gba", "ua" : "Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaN8-00/025.007; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/533.4 (KHTML, like Gecko) NokiaBrowser/7.3.1.37 Mobile Safari/533.4 3gpp-gba",
@ -1188,6 +1328,16 @@
"major" : "1" "major" : "1"
} }
}, },
{
"desc" : "OviBrowser",
"ua" : "Mozilla/5.0 (Series40; NokiaX3-02/le6.32; Profile/MIDP-2.1 Configuration/CLDC-1.1) Gecko/20100401 S40OviBrowser/1.0.0.11.8",
"expect" :
{
"name" : "OviBrowser",
"version" : "1.0.0.11.8",
"major" : "1"
}
},
{ {
"desc" : "PhantomJS", "desc" : "PhantomJS",
"ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.2 Safari/534.34", "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.2 Safari/534.34",
@ -1219,7 +1369,7 @@
} }
}, },
{ {
"desc" : "QQ", "desc" : "QQBrowser",
"ua" : "Mozilla/5.0 (Linux; U; Android 4.4.4; zh-cn; OPPO R7s Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/7.1 Mobile Safari/537.36", "ua" : "Mozilla/5.0 (Linux; U; Android 4.4.4; zh-cn; OPPO R7s Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/7.1 Mobile Safari/537.36",
"expect" : "expect" :
{ {
@ -1228,6 +1378,26 @@
"major" : "7" "major" : "7"
} }
}, },
{
"desc" : "QQBrowser",
"ua" : "Mozilla/5.0 (Linux; U; Android 9; zh-cn; vivo X21 Build/PKQ1.180819.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/9.9 Mobile Safari/537.36",
"expect" :
{
"name" : "QQBrowser",
"version" : "9.9",
"major" : "9"
}
},
{
"desc" : "Quark",
"ua" : "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; JLH-AN00 Build/HONORJLH-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Quark/5.8.2.221 Mobile Safari/537.36",
"expect" :
{
"name" : "Quark",
"version" : "5.8.2.221",
"major" : "5"
}
},
{ {
"desc" : "QupZilla", "desc" : "QupZilla",
"ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) QupZilla/1.8.9 Safari/538.1", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) QupZilla/1.8.9 Safari/538.1",
@ -1238,6 +1408,16 @@
"major" : "1" "major" : "1"
} }
}, },
{
"desc" : "Rekonq 2",
"ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.21 (KHTML, like Gecko) rekonq/2.2.1 Safari/537.21",
"expect" :
{
"name" : "rekonq",
"version" : "2.2.1",
"major" : "2"
}
},
{ {
"desc" : "RockMelt", "desc" : "RockMelt",
"ua" : "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) RockMelt/0.8.36.78 Chrome/7.0.517.44 Safari/534.7", "ua" : "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) RockMelt/0.8.36.78 Chrome/7.0.517.44 Safari/534.7",
@ -1358,6 +1538,37 @@
"major" : "2" "major" : "2"
} }
}, },
{
"desc" : "Sleipnir",
"ua" : "Mozilla/5.0 (Linux; Android 10; SOV37 Build/52.1.C.0.220; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.120 Mobile Safari/537.36 Sleipnir/3.7.5",
"expect" :
{
"name" : "Sleipnir",
"version" : "3.7.5",
"major" : "3"
}
},
{
"desc" : "Sleipnir",
"ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; Sleipnir 2.8.4)",
"expect" :
{
"name" : "Sleipnir",
"version" : "2.8.4",
"major" : "2"
}
},
{
"desc" : "Sleipnir",
"ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022) Sleipnir/2.8.4",
"expect" :
{
"name" : "Sleipnir",
"version" : "2.8.4",
"major" : "2"
}
},
{ {
"desc" : "SlimBrowser", "desc" : "SlimBrowser",
"ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SlimBrowser)", "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SlimBrowser)",
@ -1469,7 +1680,7 @@
} }
}, },
{ {
"desc" : "UPBrowser", "desc" : "UP.Browser",
"ua" : "BenQ-CF61/1.00/WAP2.0/MIDP2.0/CLDC1.0 UP.Browser/6.3.0.4.c.1.102 (GUI) MMP/2.0", "ua" : "BenQ-CF61/1.00/WAP2.0/MIDP2.0/CLDC1.0 UP.Browser/6.3.0.4.c.1.102 (GUI) MMP/2.0",
"expect" : "expect" :
{ {
@ -1539,13 +1750,23 @@
} }
}, },
{ {
"desc" : "Viera", "desc" : "w3m",
"ua" : "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", "ua" : "w3m/0.5.1",
"expect" : "expect" :
{ {
"name" : "VIERA", "name" : "w3m",
"version" : "undefined", "version" : "0.5.1",
"major" : "undefined" "major" : "0"
}
},
{
"desc" : "Wolvic",
"ua" : "Mozilla/5.0 (Android 12; Mobile VR; rv:121.0) Gecko/121.0 Firefox/121.0 Wolvic/1.6.1",
"expect" :
{
"name" : "Wolvic",
"version" : "1.6.1",
"major" : "1"
} }
}, },
{ {
@ -1753,7 +1974,7 @@
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Mobile/14A456 QQ/6.5.3.410 V1_IPH_SQ_6.5.3_1_APP_A Pixel/1080 Core/UIWebView NetType/WIFI Mem/26", "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Mobile/14A456 QQ/6.5.3.410 V1_IPH_SQ_6.5.3_1_APP_A Pixel/1080 Core/UIWebView NetType/WIFI Mem/26",
"expect" : "expect" :
{ {
"name" : "QQ", "name" : "QQBrowser",
"version" : "6.5.3.410", "version" : "6.5.3.410",
"major" : "6" "major" : "6"
} }
@ -1763,7 +1984,7 @@
"ua" : "Mozilla/5.0 (Linux; Android 6.0; PRO 6 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/37.0.0.0 Mobile MQQBrowser/6.8 TBS/036824 Safari/537.36 V1_AND_SQ_6.5.8_422_YYB_D PA QQ/6.5.8.2910 NetType/WIFI WebP/0.3.0 Pixel/1080", "ua" : "Mozilla/5.0 (Linux; Android 6.0; PRO 6 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/37.0.0.0 Mobile MQQBrowser/6.8 TBS/036824 Safari/537.36 V1_AND_SQ_6.5.8_422_YYB_D PA QQ/6.5.8.2910 NetType/WIFI WebP/0.3.0 Pixel/1080",
"expect" : "expect" :
{ {
"name" : "QQ", "name" : "QQBrowser",
"version" : "6.5.8.2910", "version" : "6.5.8.2910",
"major" : "6" "major" : "6"
} }
@ -1904,6 +2125,46 @@
"name" : "LinkedIn" "name" : "LinkedIn"
} }
}, },
{
"desc" : "Links in Linux",
"ua" : "Links (2.xpre7; Linux 2.4.18 i586; x)",
"expect" :
{
"name" : "Links",
"version" : "2.xpre7",
"major" : "2"
}
},
{
"desc" : "Links in Mac",
"ua" : "Links (2.1pre33; Darwin 8.11.0 Power Macintosh; 169x55)",
"expect" :
{
"name" : "Links",
"version" : "2.1pre33",
"major" : "2"
}
},
{
"desc" : "Links in NetBSD",
"ua" : "Links (2.29; NetBSD 10.0 i386; GNU C 10.5; x)",
"expect" :
{
"name" : "Links",
"version" : "2.29",
"major" : "2"
}
},
{
"desc" : "Links in FreeBSD",
"ua" : "Links (2.1pre15; FreeBSD 5.3-RELEASE i386; 196x84)",
"expect" :
{
"name" : "Links",
"version" : "2.1pre15",
"major" : "2"
}
},
{ {
"desc" : "Safari including comma in minor version number", "desc" : "Safari including comma in minor version number",
"ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6,2 Safari/605.1.15", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6,2 Safari/605.1.15",
@ -2071,5 +2332,25 @@
"version" : "12.33.0.36", "version" : "12.33.0.36",
"major" : "12" "major" : "12"
} }
},
{
"desc" : "Twitter",
"ua" : "Mozilla/5.0 (Linux; Android 13; CPH2531 Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.120 Mobile Safari/537.36 TwitterAndroid",
"expect" :
{
"name" : "Twitter",
"version" : "undefined",
"major" : "undefined"
}
},
{
"desc" : "Twitter",
"ua" : "Mozilla/5.0 (iPad; CPU OS 15_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/19H12 Twitter for iPhone/10.34",
"expect" :
{
"name" : "Twitter",
"version" : "10.34",
"major" : "10"
}
} }
] ]

View File

@ -0,0 +1,42 @@
[
{
"desc" : "curl",
"ua" : "curl/7.38.0",
"expect" :
{
"name" : "curl",
"version" : "7.38.0",
"type" : "cli"
}
},
{
"desc" : "lynx",
"ua" : "Lynx 2.8.8dev.3",
"expect" :
{
"name" : "Lynx",
"version" : "2.8.8dev.3",
"type" : "cli"
}
},
{
"desc" : "lynx",
"ua" : "Lynx/2.6",
"expect" :
{
"name" : "Lynx",
"version" : "2.6",
"type" : "cli"
}
},
{
"desc" : "wget",
"ua" : "Wget/1.21.1",
"expect" :
{
"name" : "Wget",
"version" : "1.21.1",
"type" : "cli"
}
}
]

View File

@ -0,0 +1,92 @@
[
{
"desc" : "Applebot",
"ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12B410 Safari/600.1.4 (Applebot/0.1;+http://www.apple.com/go/applebot)",
"expect" :
{
"name" : "Applebot",
"version" : "0.1",
"type" : "crawler"
}
},
{
"desc" : "Amazonbot",
"ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5 (Amazonbot/0.1; +https://developer.amazon.com/support/amazonbot)",
"expect" :
{
"name" : "Amazonbot",
"version" : "0.1",
"type" : "crawler"
}
},
{
"desc" : "Bytespider",
"ua" : "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.1511.1269 Mobile Safari/537.36; Bytespider",
"expect" :
{
"name" : "Bytespider",
"version" : "undefined",
"type" : "crawler"
}
},
{
"desc" : "ClaudeBot",
"ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)",
"expect" :
{
"name" : "ClaudeBot",
"version" : "1.0",
"type" : "crawler"
}
},
{
"desc" : "ClaudeWeb",
"ua" : "Claude-Web/1.0 (web crawler; +https://www.anthropic.com/; bots@anthropic.com)",
"expect" :
{
"name" : "Claude-Web",
"version" : "1.0",
"type" : "crawler"
}
},
{
"desc" : "FacebookBot",
"ua" : "Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/",
"expect" :
{
"name" : "FacebookBot",
"version" : "1.0",
"type" : "crawler"
}
},
{
"desc" : "Googlebot-Video",
"ua" : "Googlebot-Video/1.0",
"expect" :
{
"name" : "Googlebot-Video",
"version" : "1.0",
"type" : "crawler"
}
},
{
"desc" : "GPTBot",
"ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)",
"expect" :
{
"name" : "GPTBot",
"version" : "1.0",
"type" : "crawler"
}
},
{
"desc" : "YandexBot",
"ua" : "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)",
"expect" :
{
"name" : "YandexBot",
"version" : "3.0",
"type" : "crawler"
}
}
]

View File

@ -0,0 +1,12 @@
[
{
"desc" : "Thunderbird",
"ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0",
"expect" :
{
"name" : "Thunderbird",
"version" : "78.13.0",
"type" : "email"
}
}
]

View File

@ -0,0 +1,12 @@
[
{
"desc" : "BingPreview",
"ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b",
"expect" :
{
"name" : "BingPreview",
"version" : "1.0b",
"type" : "fetcher"
}
}
]

View File

@ -0,0 +1,12 @@
[
{
"desc" : "Scrapy",
"ua" : "Scrapy/1.5.0 (+https://scrapy.org)",
"expect" :
{
"name" : "Scrapy",
"version" : "1.5.0",
"type" : "module"
}
}
]

View File

@ -1373,7 +1373,7 @@
"expect": { "expect": {
"vendor": "Facebook", "vendor": "Facebook",
"model": "Quest", "model": "Quest",
"type": "wearable" "type": "xr"
} }
}, },
{ {
@ -1382,7 +1382,16 @@
"expect": { "expect": {
"vendor": "Facebook", "vendor": "Facebook",
"model": "Quest 2", "model": "Quest 2",
"type": "wearable" "type": "xr"
}
},
{
"desc": "Oculus Quest 3",
"ua": "Mozilla/5.0 (X11; Linux x86_64; Quest 3) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/31.4.0.6.51.566757996 Chrome/120.0.6099.283 VR Safari/537.36",
"expect": {
"vendor": "Facebook",
"model": "Quest 3",
"type": "xr"
} }
}, },
{ {
@ -1391,7 +1400,7 @@
"expect": { "expect": {
"vendor": "Facebook", "vendor": "Facebook",
"model": "Quest Pro", "model": "Quest Pro",
"type": "wearable" "type": "xr"
} }
}, },
{ {
@ -1502,6 +1511,15 @@
"type": "mobile" "type": "mobile"
} }
}, },
{
"desc": "OPPO Pad",
"ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; OPD2101 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.3.9.1290 Mobile Safari/537.36",
"expect": {
"vendor": "OPPO",
"model": "OPD2101",
"type": "tablet"
}
},
{ {
"desc": "OPPO Neo", "desc": "OPPO Neo",
"ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; R831T Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 OppoBrowser/3.3.2 Mobile Safari/534.30", "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; R831T Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 OppoBrowser/3.3.2 Mobile Safari/534.30",