diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 3ec50a0bb..b8b83265b 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: faisalman patreon: # Replace with a single Patreon username open_collective: ua-parser-js ko_fi: # Replace with a single Ko-fi username diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..1705fdbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,43 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Library version** +Which version of the library that you use, eg: v0.7.35 or v2.0.0-alpha.3 + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen, or what's referred in the docs https://docs.uaparser.dev/ + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +For issues related to detection results, you can send the screenshots of the demo section at https://uaparser.dev/#demo to confirm. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..c658b1889 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,22 @@ +# Prerequisites + +- [ ] I have read and follow the [contributing](https://github.com/faisalman/ua-parser-js/blob/master/CONTRIBUTING.md) guidelines +- [ ] I have read and accept the [Contributor License Agreement (CLA)](https://gist.github.com/faisalman/2ed16621ebb544157eba85a7f7381417) Document and I hereby sign the CLA + +# Type of Change + +Bug fix, feature, docs update, ... + +# Description + +Please include a summary of the change (current behavior vs new behavior), which issue is fixed (you can also link to an open issue here), and why this change is necessary. + +# Test + +Please describe the tests that you ran to verify your changes. + +# Impact + +Does this PR introduce a breaking change? What changes might users need to make due to this PR? + +# Other Info \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 000000000..2df153f92 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1 @@ +- [ ] I have read and accept the [Contributor License Agreement (CLA)](https://gist.github.com/faisalman/2ed16621ebb544157eba85a7f7381417) Document and I hereby sign the CLA \ No newline at end of file diff --git a/.github/workflows/analysis-codeql.yml b/.github/workflows/analysis-codeql.yml new file mode 100644 index 000000000..b718e6878 --- /dev/null +++ b/.github/workflows/analysis-codeql.yml @@ -0,0 +1,78 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: CodeQL Analysis + +on: + push: + branches: [ "master"] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '15 6 * * 0' + +permissions: read-all + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Use only 'java' to analyze code written in Java, Kotlin or both + # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + # - name: Autobuild + # uses: github/codeql-action/autobuild@v2 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/analysis-dependency.yml b/.github/workflows/analysis-dependency.yml new file mode 100644 index 000000000..99afdf01c --- /dev/null +++ b/.github/workflows/analysis-dependency.yml @@ -0,0 +1,20 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: Dependency Analysis +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v3 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v2 diff --git a/.github/workflows/analysis-scorecard.yml b/.github/workflows/analysis-scorecard.yml new file mode 100644 index 000000000..0c8f2b902 --- /dev/null +++ b/.github/workflows/analysis-scorecard.yml @@ -0,0 +1,72 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: OpenSSF's Scorecard Analysis +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '45 2 * * 4' + push: + branches: [ "master" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@e38b1902ae4f44df626f11ba0734b14fb91f8f86 # v2.1.2 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4 + with: + sarif_file: results.sarif diff --git a/.github/workflows/publish-github-packages.yml b/.github/workflows/publish-github-packages.yml new file mode 100644 index 000000000..25f1ca8f2 --- /dev/null +++ b/.github/workflows/publish-github-packages.yml @@ -0,0 +1,22 @@ +name: Publish to GitHub Package + +on: + release: + types: [published] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18.x' + registry-url: https://npm.pkg.github.com/ + - run: npm ci + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/publish-npm-packages.yml b/.github/workflows/publish-npm-packages.yml new file mode 100644 index 000000000..b4f35d974 --- /dev/null +++ b/.github/workflows/publish-npm-packages.yml @@ -0,0 +1,23 @@ +name: Publish to NPM + +on: + release: + types: [published] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18.x' + registry-url: 'https://registry.npmjs.org' + - run: npm install -g npm + - run: npm ci + - run: npm publish --provenance --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml deleted file mode 100644 index 203184294..000000000 --- a/.github/workflows/run-test.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: ua-parser-js-run-test - -on: [push, pull_request] - -permissions: - contents: read - -jobs: - run-test: - runs-on: ubuntu-latest - strategy: - matrix: - arch: [amd64, ppc64le] - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - - name: Run the test - run: | - npm install - npm run test-ci diff --git a/.github/workflows/test-ci.yml b/.github/workflows/test-ci.yml new file mode 100644 index 000000000..f1e262b8b --- /dev/null +++ b/.github/workflows/test-ci.yml @@ -0,0 +1,28 @@ +name: UAParser.js CI-Test + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + run-test: + runs-on: ubuntu-latest + strategy: + matrix: + arch: [amd64] + node-version: ['22.13', 'lts/*'] + include: + - arch: ppc64le + node-version: 20 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - name: Run the test + run: | + echo "Running on ubuntu-latest-${{ matrix.arch }} with node version set as ${{ matrix.node-version }}" + npm ci + npx playwright install --with-deps + npm test diff --git a/.gitignore b/.gitignore index ca8c34e70..4a99144dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ node_modules/ npm-debug.log +playwright-report/ +test-results/ ### vim ### .*.s[a-w][a-z] diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 5798043ca..000000000 --- a/.jshintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "esversion": 3 -} \ No newline at end of file diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 43c97e719..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0f87fc37f..000000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -arch: - - amd64 - - ppc64le -language: node_js -node_js: - - stable - - lts/* - -notifications: - email: false - -cache: - directories: - - node_modules - -sudo: false - -script: npm run test-ci \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..d1f9ac63d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,345 @@ +# UAParser.js Changelog + +## Migrating from v1 to v2 + +### What's Breaking: + +- **Licensing Changes:** + - UAParser.js is now licensed under AGPLv3 for open-source use, with PRO Licenses available for commercial/proprietary use + +- **Browser Detection on Mobile Devices:** + - `"Chrome"` => `"Mobile Chrome"` + - `"Firefox"` => `"Mobile Firefox"` + +- **OS Detection:** + - `"Mac OS"` => `"macOS"` + - `"Chromium OS"` => `"Chrome OS"` + +### What's New: + +- **Support for ES Modules & TypeScript:** + - Import directly as an ES module with TypeScript support: `import { UAParser } from 'ua-parser-js'` + +- **Support for Custom/Predefined Extensions:** + - Pass custom regexes or predefined extensions as a list to `UAParser()` + +- **Support for CLI Parsing:** + - Parse a user-agent directly from the command line using `npx ua-parser-js "[User-Agent]"` + +- **Enhanced Detection with Client Hints:** + - `withClientHints()`: Improves detection accuracy by leveraging client hints + +- **Enhanced Detection with Feature Detection:** + - `withFeatureCheck()`: Refines detection results using feature detection + +- **Simple Comparison for Detection Results:** + - `is()`: Enables easy comparison checks against the detection result + +- **Detailed Result Output:** + - `toString()`: Returns the detection result in form of a full-name string + +- **New Device Type:** + - Added `xr` to identify AR/VR devices + +- **New Browser Property:** + - Added `browser.type` to identify additional browser types: + - `crawler`, `cli`, `email`, `fetcher`, `inapp`, `library`, `mediaplayer` + +- **New Submodules:** + - **`'ua-parser-js/enums'`**: Provides constants for these specific properties: + - `browser.name`, `browser.type`, `cpu.architecture`, `device.type`, `device.vendor`, `engine.name`, `os.name` + + - **`'ua-parser-js/extensions'`**: Predefined extensions for various use cases: + - `Bots`, `Crawlers`, `CLIs`, `Emails`, `ExtraDevices`, `Fetchers`, `InApps`, `Libraries`, `Mediaplayers` + + - **`'ua-parser-js/helpers'`**: Provides utility methods to extend detection functionality: + - `getDeviceVendor()`: Guesses the device vendor based on its model name + - `isAppleSilicon()`: Detects Apple Silicon device properties + - `isAIBot()`: Checks if the user-agent is an AI bot + - `isBot()`: Checks if the user-agent is a bot + - `isChromeFamily()`: Checks if the browser is Chrome-based (uses Blink engine) — e.g., New Opera, New Edge, Vivaldi, Brave, Arc, etc. + - `isElectron()`: Detects if current window is running within Electron + - `isFromEU()`: Detects if current browser's timezone is from an EU country + - `isFrozenUA()`: Checks if the user-agent matches a frozen/reduced user-agent pattern + - `isStandalonePWA()`: Detects if current window is a standalone PWA + +--- + +## Version 2.0.4 + +- Add new browser: Edge WebView, Edge WebView2 +- Add new device vendor: Lava, Retroid, Vizio +- Add new OS: ArcaOS, Knoppix, Xubuntu, Windows CE, Windows RT +- Improve device detection: Google Pixel & Pixelbook Series, HMD, Infinix, LG WebOS TV, Motorola, Nothing, OnePlus, Sony, Tecno +- Improve OS detection: AIX, Arch, Fuchsia, Haiku, HarmonyOS, Mint, MorphOS, Solaris, Windows +- Improve `withClientHints()` browser naming adjustments: `Microsoft Edge WebView2` => `Edge WebView2` +- Identify device that uses Firefox Reality / Wolvic as `xr` +- Identify device with large screen as `smarttv` +- Identify Windows CE & Windows RT as distinct OS variants +- `extensions` submodule: + - Remove lookbehind assertion to ensure regex compatibility + - Add new crawlers: ChatGLM, Daum, iAskBot, Onespot, Qwantbot, Startpage + - Add new emails: Apple's Mail, DaumMail, Polymail, ProtonMail, SparkDesktop, Zimbra, ZohoMail-Desktop + - Add new fetchers: Iframely, MistralAI-User, Perplexity-User + - Add new inApps: Discord, Evernote, Figma, Flipboard, Mattermost, Notion, Postman, Rambox, Rocket.Chat, Microsoft Teams, TikTok Lite, VS Code + - Add new libraries: AdobeAIR, aiohttp, nutch, httpx, urllib3 +- `enum` submodule: + - Fix mistakenly placed `BLU` categorized as browser name instead of device vendor + +## Version 2.0.3 + +- Add new browser: Dooble, Ecosia, LG Browser, Otter, qutebrowser, Surf +- Add new device: BLU, Facebook Portal TV +- Improve device detection: Archos, LG, Meta Quest +- Remove jazzer.js fuzz test +- Improve `withClientHints()`: + - Browser naming adjustments: + - `HuaweiBrowser` => `Huawei Browser` + - `Miui Browser` => `MIUI Browser` + - `OperaMobile` => `Opera Mobi` + - `YaBrowser` => `Yandex` +- `extensions` submodule: + - Add new Crawler: AdIdxBot, Linespider, LinkedInBot, OpenAI Image Downloader, SemrushBot, Yahoo! Slurp + - Add new Fetcher: Better Uptime Bot, Google-PageRenderer, GoogleImageProxy, MicrosoftPreview, Snap URL Preview, SkypeUriPreview, TelegramBot + - Add new Vehicles: BMW, Jeep + - Add OS detection of WhatsApp user-agent + +## Version 2.0.2 + +- Fix TypeScript dependency issue + +## Version 2.0.1 + +- Add new browser: Ladybird, Daum +- Add new device: Apple HomePod +- Add new device vendor: HMD +- Add new OS: Ubuntu Touch, Windows IoT +- Improve CPU detection: ARM, x86 +- Improve device detection: Lenovo, Nokia, Nvidia, Xiaomi + - Tablet: Google, Honor, Huawei, Infinix, Nokia, OnePlus, Xiaomi + - Wearable: Asus, Google, LG, Motorola, OnePlus, Oppo, Samsung, Sony + - Smart-TV: Xiaomi, unidentified vendors + - Improve detection for unknown VR devices + - Improve device model detection for Generic devices +- Improve OS detection: Linux, Symbian +- Improve TypeScript definitions for Headers +- Improve `withClientHints()`: + - `engine.version` also get updated + - Infer `device.vendor` & `device.type` by guessing from `device.model` + - Browser naming adjustments: + - `Google Chrome` => `Chrome` + - `Microsoft Edge` => `Edge` + - `Android WebView` => `Chrome WebView` + - `HeadlessChrome` => `Chrome Headless` +- `enums` submodule: + - Add TypeScript definitions +- `extensions` submodule: + - Add new list: + - `Vehicles`: BYD, Rivian, Volvo + - Add new Fetcher: Bluesky + - Add new Library: Apache-HttpClient, go-http-client, got, GuzzleHttp, Java-http-client, libwww-perl, lua-resty-http, Needle, OkHttp, node-fetch, PHP-SOAP, PostmanRuntime, superagent + +## Version 2.0.0 + +- `ua-parser-js/extensions` submodule: + - Add new CLI: ELinks, HTTPie + - Add new crawler: AI2Bot, aiHitBot, anthropic-ai, Diffbot, ImagesiftBot, magpie-crawler, Omgilibot, Screaming Frog SEO Spider, Seznambot, Teoma, Timpibot, VelenPublicWebCrawler, Webzio-Extended, YouBot + - Add new email: Airmail, BlueMail, eMClient, NaverMailApp, Sparrow, Yahoo + - Add new fetcher: cohere-ai, Vercelbot + - Add new library: java, python-urllib, python-requests +- `ua-parser-js/helpers` submodule: + - Add new method `isAIBot()`: Checks if the user-agent is an AI bot + +## Version 2.0.0-rc.3 + +- Add support for Headers object +- Add new device: Advan, Cat, Energizer, Honor, IMO, Micromax, Smartfren +- Add new engine: Servo +- `ua-parser-js/extensions` submodule: + - Breaking change: rename `module` to `library` + - Add new email clients: Evolution, KMail, Kontact + - Add new bots: 360Spider, Archive.org Bots, CCBot, DataForSeoBot, DuckAssistBot, Exabot, Google Bots, Meta Bots, MojeekBot, PerplexityBot, PetalBot, TurnitinBot, Yeti, YisouSpider + +## Version 2.0.0-rc.2 + +- Fix incorrect import path in ESM files +- Add new browser: 115, SlimBoat, Slimjet, LibreWolf +- Improve browser detection: 2345, 360, Dragon, Iron, Maxthon +- `ua-parser-js/enums` submodule: + - Add Chromecast OS variants: Android/Fuchsia/Linux/SmartSpeaker +- `ua-parser-js/helpers` submodule: + - Add new method: `isBot()` to check if the browser is identified as a bot + +## Version 2.0.0-rc.1 + +- Fix Python Request mistakenly identified as Meta Quest +- Add new browser: Helio +- Add new device: itel, Nothing, Pico, TCL +- Add new engine: ArkWeb +- Add new OS: OpenHarmony, Pico +- Improve browser detection: Quark +- Improve device detection: Xiaomi, Amazon Echo Show, Google Chromecast, Samsung Galaxy Watch +- `ua-parser-js/helpers` submodule: + - Add new method: + - `getDeviceVendor()` to guess for a device vendor based on its model name + - `isElectron()` to check if current window is running inside Electron + - `isFromEU()` to check if current window is from an EU (European Union) country + - `isStandalonePWA()` to check if current window is a standalone PWA + - Rename `isChromiumBased()` to `isChromeFamily()` + - Update `isAppleSilicon()` to also checks for WebGL renderer info +- `ua-parser-js/extensions` submodule: + - Restore `Bots` as a compilation of all these browser types: `cli`, `crawler`, `fetcher`, and `library` + +## Version 2.0.0-beta.3 + +- Breaking: + - AR/VR devices moved to new device type: `xr` + - New property in `browser`: `type` +- 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` +- In `ua-parser-js/extensions` submodule, `bots` divided into `crawler` / `fetcher` + +## Version 2.0.0-beta.2 + +- Increase UA_MAX_LENGTH to 500 +- Add TypeScript declaration file in `ua-parser-js/extensions` submodule +- Improve TypeScript module resolution +- Add new methods in `ua-parser-js/helpers` submodule: `isAppleSilicon()` & `isChromiumBased()` +- Fix misidentified WebView token as device model +- Add new browser: Alipay, Klarna, Opera GX, Smart Lenovo Browser, Vivo Browser +- Rename browser: Avant, Baidu, Samsung Internet, Sogou Explorer, Sogou Mobile, WeChat +- Improve client-hints detection: Edge, Xbox + +## Version 2.0.0-beta.1 + +- Update Client Hints Form-Factor +- Provide in-package type definitions +- Add new device: Ulefone +- Improve device detection: Realme, Xiaomi Redmi + +## Version 2.0.0-alpha.3 + +- Add `withFeatureCheck()` method +- Add `isFrozenUA()` method in `ua-parser-js/helpers` submodule +- Add `MediaPlayers` & `Modules` in `ua-parser-js/extensions` submodule +- Fix issue with ESM import + +## Version 2.0.0-alpha.2 + +- Fix browser result always returning Chromium when using withClientHints() +- Fix infinite-loop when await-ing withClientHints() in non-client-hints browser + +## Version 2.0.0-alpha.1 + +- Initial work on new major version + +--- + +## Version 0.7.40 / 1.0.40 +- Add new browser: 115, LibreWolf, Slimboat, Slimjet +- Add new device: Advan, Cat, Energizer, IMO, Micromax, Smartfren +- Add new engine: ArkWeb, Servo +- Add new os: OpenHarmony +- Improve browser detection: 2345, 360, Dragon, Iron, Maxthon +- Recognize Honor as a separate device vendor from Huawei +- Fix Python Request mistakenly identified as Meta Quest + +## Version 0.7.39 / 1.0.39 +- Add new feature: executable command using `npx ua-parser-js "[INSERT-UA-HERE]"` +- Add new browser: Helio, Pico Browser, Wolvic +- Add new device vendor: itel, Nothing, TCL +- Improve browser detection: ICEBrowser, Klar, QQBrowser, Quark, Rekonq, Sleipnir +- Improve device detection: Xiaomi Pro, Amazon Echo Show, Samsung Galaxy Watch +- Removed from browser: Viera + +## Version 0.7.38 / 1.0.38 +- Fix error on getOS() when userAgentData.platform is undefined +- Add new browser: Opera GX, Twitter +- Improve browser detection: DuckDuckGo +- Improve device detection: OPPO Pad, Oculus Quest + +## Version 0.7.37 / 1.0.37 +- Fix misidentified WebView token as device model +- Increase UA_MAX_LENGTH to 500 +- Add new browser: Alipay, Klarna, Smart Lenovo Browser, Vivo Browser +- Add new device: Ulefone +- Improve device detection: Realme, Xiaomi Redmi +- Rename browser: Avant, Baidu, Samsung Internet, Sogou Explorer, Sogou Mobile, WeChat + +## Version 0.7.36 / 1.0.36 +- Add new browser: Snapchat +- Add new devices: Infinix, Tecno +- Improve device detection: Amazon Fire TV, Xiaomi POCO +- Improve OS detection: iOS + +## Version 0.7.35 / 1.0.35 +- Fix result from user-supplied user-agent being altered +- Add new browser: Heytap, TikTok +- Add new engine: LibWeb +- Add new OS: SerenityOS +- Improve browser detection: Yandex +- Improve device detection: iPhone, Amazon Echo +- Improve OS detection: iOS + +## Version 0.7.34 / 1.0.34 +- Fix Sharp Mobile detected as Huawei Tablet +- Fix IE8 bug +- Add new devices : Kobo e-Reader, Apple Watch, and some new SmartTV devices +- Add new OS : watchOS +- Improve browser detection : Kakao, Naver, Brave +- Improve device detection : Oculus, iPad +- Improve OS detection : Chrome OS +- Using navigator.userAgentData as fallback for device.type & os.name + +## Version 0.7.33 / 1.0.33 + +- Add new browser : Cobalt +- Identify Macintosh as an Apple device +- Fix ReDoS vulnerability + +## Version 0.7.32 / 1.0.32 + +- Add new browser : DuckDuckGo, Huawei Browser, LinkedIn +- Add new OS : HarmonyOS +- Add some Huawei models +- Add Sharp Aquos TV +- Improve detection Xiaomi Mi CC9 +- Fix Sony Xperia 1 III misidentified as Acer tablet +- Fix Detect Sony BRAVIA as SmartTV +- Fix Detect Xiaomi Mi TV as SmartTV +- Fix Detect Galaxy Tab S8 as tablet +- Fix WeGame mistakenly identified as WeChat +- Fix included commas in Safari / Mobile Safari version +- Increase UA_MAX_LENGTH to 350 + +## Version 0.7.31 / 1.0.2 + +- Fix OPPO Reno A5 incorrect detection +- Fix TypeError Bug +- Use AST to extract regexes and verify them with safe-regex + +## Version 0.7.30 / 1.0.1 + +- Add new browser : Obigo, UP.Browser, Klar +- Add new device : Oculus, Roku +- Add new OS: Maemo, HP-UX, Android-x86, Deepin, elementary OS, GhostBSD, Linspire, Manjaro, Sabayon +- Improve detection for Sony Xperia 1ii, LG Android TV, and some more devices +- Improve detection for ARM64 CPU +- Improve detection for Windows Mobile, Netscape, Mac on PowerPC +- Categorize PDA as mobile +- Fix Sharp devices misjudged as Huawei +- Fix trailing comma for ES3 compatibility +- Some code refactor + +## Version 0.7 / 1.0 + +Version 1.0.x is basically the equivalent of version 0.7.x (mirror/duplicate). See [#536](https://github.com/faisalman/ua-parser-js/issues/536) for the reason behind this confusion. + +## Version 0.8 + +Version 0.8 was created by accident. This version is now deprecated and no longer maintained, please update to version 0.7 / 1.0. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..b148af6e5 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,29 @@ +# UAParser.js Code of Conduct + +## Introduction + +Welcome to the UAParser.js community! We're here to collaborate on developing an awesome project. Here are some general guidelines to make our community a great place: + +### 1. Be Kind, Honest, and Respectful + +Always treat others with kindness and respect. We value different opinions and encourage positive communication. + +### 2. Keep Conversations Civil and On-Topic + +Please keep discussions related to the project. If you want to talk about something else, find the right place for it. + +### 3. Mutual Assistance, Appreciation, and Acknowledgement + +Feel free to ask for help, show gratitude for contributions, and make sure to give credit where it's due. + +### 4. Resolving Disagreements + +In the event of a disagreement, we encourage open and respectful dialogue. It's important to remember that it's okay to have differing opinions, and if a common ground can't be reached, we suggest using the 'agree to disagree' approach. + +## Reporting Issues + +If you see any behavior that goes against this code of conduct, report it to [f@faisalman.com](mailto:f@faisalman.com). + +## Conclusion + +Together, we can make this project awesome! \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..0bb377bc7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,7 @@ +# UAParser.js: How to Contribute + +* Fork and clone this repository +* Make some changes as required +* Write unit test to showcase its functionality under `/test` +* Run the test suites to make sure it's not breaking anything `$ npm run build+test` +* Submit a pull request & check the CLA in the submission form \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..213ad0b2e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,616 @@ +# GNU AFFERO GENERAL PUBLIC LICENSE + +Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +## Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains +free software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing +under this license. + +The precise terms and conditions for copying, distribution and +modification follow. + +## TERMS AND CONDITIONS + +### 0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public +License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +### 13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your +version supports such interaction) an opportunity to receive the +Corresponding Source of your version by providing access to the +Corresponding Source from a network server at no charge, through some +standard or customary means of facilitating copying of software. This +Corresponding Source shall include the Corresponding Source for any +work covered by version 3 of the GNU General Public License that is +incorporated pursuant to the following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU Affero General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever +published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..0ea5465c7 --- /dev/null +++ b/README.md @@ -0,0 +1,251 @@ +[![https://uaparser.dev](https://raw.githubusercontent.com/faisalman/ua-parser-js/gh-pages/images/uap-header.png)](https://uaparser.dev) +[![https://uaparser.dev](https://github.com/user-attachments/assets/9f30f3d4-5cfe-441c-8f86-ead7c955f940)](https://uaparser.dev) +[![https://uaparser.dev](https://github.com/user-attachments/assets/50da50fc-7c8a-46e3-a2bc-6a8249914372)](https://uaparser.dev) +[![https://uaparser.dev](https://github.com/user-attachments/assets/9f2aaff0-a9b4-4ac9-bdf3-eea8081a2582)](https://uaparser.dev) + +

+ + + + + + + + +

+ +# UAParser.js + +The most comprehensive, compact, & up-to-date isomorphic JavaScript library to detect +user's Browser, Engine, OS, CPU, and Device type/model. Runs either in browser +(client-side) or node.js (server-side). + +# Demo + + * Live demo: https://uaparser.dev + + +# Documentation + + * `version 1.x` : https://github.com/faisalman/ua-parser-js/tree/1.0.x#documentation + * `version 2.x` : https://docs.uaparser.dev + +Before upgrading from `v0.7` / `v1.0`, please read [CHANGELOG](CHANGELOG.md) to +see what's new & breaking. + +# License Options + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Open-Source EditionsPRO / Commercial Editions
License optionsMIT (v0.7~v1.0)AGPL (>=v2.0)PRO PersonalPRO BusinessPRO Enterprise
Browser detectionâš ī¸âœ…âœ…âœ…âœ…
CPU detectionâš ī¸âœ…âœ…âœ…âœ…
Device detectionâš ī¸âœ…âœ…âœ…âœ…
Engine detectionâš ī¸âœ…âœ…âœ…âœ…
OS detectionâš ī¸âœ…âœ…âœ…âœ…
Bot detection❌✅✅✅✅
AI Bot detection❌✅✅✅✅
Extras (Apps, Libs, Emails, Media Players, etc) detection❌✅✅✅✅
Enhanced detection result❌✅✅✅✅
Client Hints support❌✅✅✅✅
CommonJS support✅✅✅✅✅
ES modules support❌✅✅✅✅
npm module✅✅✅✅✅
TypeScript declarationsâš ī¸âœ…âœ…âœ…âœ…
Allows commercial use✅✅❌✅✅
Permissive (non-copyleft) license✅❌✅✅✅
Unlimited use per 1 license✅✅✅❌✅
1-year product support❌❌✅✅✅
Lifetime updates✅✅✅✅✅
PriceFREE (License)FREE (License)$14 (License)$29 (License)$599 (License)
+

GET THE PRO PACKAGES đŸ“Ĩ

+
+ +# Development + +## Contributors + +Please read [CONTRIBUTING](CONTRIBUTING.md) guide first for the instruction details. + + + + + +Made with [contributors-img](https://contrib.rocks). + +## Backers & Sponsors + + + + +You can support the open-source editions of UAParser.js through one of the following options: + +[![OpenCollective](https://img.shields.io/badge/OpenCollective-dddddd?style=for-the-badge&logo=opencollective&color=dddddd +)](https://opencollective.com/ua-parser-js) +[![GitHub Sponsors](https://img.shields.io/badge/GitHub_Sponsors-333333?style=for-the-badge&logo=githubsponsors&color=333333 +)](https://github.com/sponsors/faisalman) +[![PayPal](https://img.shields.io/badge/Paypal-003087?style=for-the-badge&logo=paypal&color=003087 +)](https://paypal.me/faisalman) +[![WeChat/Alipay](https://img.shields.io/badge/Other_Payment_Methods-Alipay_/_WeChat_Pay-09b83e?style=for-the-badge&logo=mastercard&color=09b83e +)](https://store.faisalman.com/buy/3d71f2f3-cf4d-473c-892a-9d4497c890be) \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..d1f40fe80 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +# Security Policy + +## Reporting a Vulnerability + +To report a security issue, please email `f@faisalman.com` with a description of the issue, reproducible steps to get the issue, affected versions, and, if known, mitigations for the issue. + +If the issue is confirmed as a vulnerability, we will open a new security advisory draft in our GitHub's Security Advisory page [https://github.com/faisalman/ua-parser-js/security/advisories](https://github.com/faisalman/ua-parser-js/security/advisories) and acknowledge your contributions as part of it. This project follows a 90 days disclosure timeline. \ No newline at end of file diff --git a/bower.json b/bower.json deleted file mode 100644 index c59773a0e..000000000 --- a/bower.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "ua-parser-js", - "version": "0.7.33", - "authors": [ - "Faisal Salman " - ], - "private": false, - "main": "src/ua-parser.js", - "ignore": [ - "build", - "node_modules", - "bower_components", - "test", - "tests" - ], - "dependencies": {} -} diff --git a/changelog.md b/changelog.md deleted file mode 100644 index 05fc6b90b..000000000 --- a/changelog.md +++ /dev/null @@ -1,3 +0,0 @@ -# UAParser.js Changelog - -## Version 0.8.0 \ No newline at end of file diff --git a/dist/icons/color/LICENSE.md b/dist/icons/color/LICENSE.md new file mode 100644 index 000000000..c025cd011 --- /dev/null +++ b/dist/icons/color/LICENSE.md @@ -0,0 +1,3 @@ +MIT License +Copyright (c) Cătălin Mariș +https://github.com/alrra/browser-logos \ No newline at end of file diff --git a/dist/icons/color/browser/android-browser.svg b/dist/icons/color/browser/android-browser.svg new file mode 100644 index 000000000..92d59275b --- /dev/null +++ b/dist/icons/color/browser/android-browser.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/avant.png b/dist/icons/color/browser/avant.png new file mode 100644 index 000000000..e0b715d8b Binary files /dev/null and b/dist/icons/color/browser/avant.png differ diff --git a/dist/icons/color/browser/basilisk.png b/dist/icons/color/browser/basilisk.png new file mode 100644 index 000000000..19a59d65b Binary files /dev/null and b/dist/icons/color/browser/basilisk.png differ diff --git a/dist/icons/color/browser/basilisk.svg b/dist/icons/color/browser/basilisk.svg new file mode 100644 index 000000000..64e1f61c3 --- /dev/null +++ b/dist/icons/color/browser/basilisk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/brave.png b/dist/icons/color/browser/brave.png new file mode 100644 index 000000000..06fa47770 Binary files /dev/null and b/dist/icons/color/browser/brave.png differ diff --git a/dist/icons/color/browser/brave.svg b/dist/icons/color/browser/brave.svg new file mode 100644 index 000000000..fb5ed8b82 --- /dev/null +++ b/dist/icons/color/browser/brave.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/chrome.png b/dist/icons/color/browser/chrome.png new file mode 100644 index 000000000..f251ad8e9 Binary files /dev/null and b/dist/icons/color/browser/chrome.png differ diff --git a/dist/icons/color/browser/chrome.svg b/dist/icons/color/browser/chrome.svg new file mode 100644 index 000000000..dcc3b1106 --- /dev/null +++ b/dist/icons/color/browser/chrome.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/chromium.png b/dist/icons/color/browser/chromium.png new file mode 100644 index 000000000..7a3cb15fc Binary files /dev/null and b/dist/icons/color/browser/chromium.png differ diff --git a/dist/icons/color/browser/chromium.svg b/dist/icons/color/browser/chromium.svg new file mode 100644 index 000000000..97b24f226 --- /dev/null +++ b/dist/icons/color/browser/chromium.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/coc-coc.png b/dist/icons/color/browser/coc-coc.png new file mode 100644 index 000000000..f917dbcab Binary files /dev/null and b/dist/icons/color/browser/coc-coc.png differ diff --git a/dist/icons/color/browser/default.png b/dist/icons/color/browser/default.png new file mode 100644 index 000000000..61e01e6a3 Binary files /dev/null and b/dist/icons/color/browser/default.png differ diff --git a/dist/icons/color/browser/default.svg b/dist/icons/color/browser/default.svg new file mode 100644 index 000000000..4d9afe0fd --- /dev/null +++ b/dist/icons/color/browser/default.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/dolphin.png b/dist/icons/color/browser/dolphin.png new file mode 100644 index 000000000..d53805f98 Binary files /dev/null and b/dist/icons/color/browser/dolphin.png differ diff --git a/dist/icons/color/browser/edge.png b/dist/icons/color/browser/edge.png new file mode 100644 index 000000000..931cb1c1b Binary files /dev/null and b/dist/icons/color/browser/edge.png differ diff --git a/dist/icons/color/browser/edge.svg b/dist/icons/color/browser/edge.svg new file mode 100644 index 000000000..b2afc8e2d --- /dev/null +++ b/dist/icons/color/browser/edge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/electron.png b/dist/icons/color/browser/electron.png new file mode 100644 index 000000000..e17799028 Binary files /dev/null and b/dist/icons/color/browser/electron.png differ diff --git a/dist/icons/color/browser/electron.svg b/dist/icons/color/browser/electron.svg new file mode 100644 index 000000000..a766fedd9 --- /dev/null +++ b/dist/icons/color/browser/electron.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/falkon.png b/dist/icons/color/browser/falkon.png new file mode 100644 index 000000000..d7fe06de5 Binary files /dev/null and b/dist/icons/color/browser/falkon.png differ diff --git a/dist/icons/color/browser/falkon.svg b/dist/icons/color/browser/falkon.svg new file mode 100644 index 000000000..0a2e4890f --- /dev/null +++ b/dist/icons/color/browser/falkon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/firefox-reality.png b/dist/icons/color/browser/firefox-reality.png new file mode 100644 index 000000000..9a901a445 Binary files /dev/null and b/dist/icons/color/browser/firefox-reality.png differ diff --git a/dist/icons/color/browser/firefox-reality.svg b/dist/icons/color/browser/firefox-reality.svg new file mode 100644 index 000000000..a157540ab --- /dev/null +++ b/dist/icons/color/browser/firefox-reality.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/firefox.png b/dist/icons/color/browser/firefox.png new file mode 100644 index 000000000..fc7123205 Binary files /dev/null and b/dist/icons/color/browser/firefox.png differ diff --git a/dist/icons/color/browser/firefox.svg b/dist/icons/color/browser/firefox.svg new file mode 100644 index 000000000..f153cedd9 --- /dev/null +++ b/dist/icons/color/browser/firefox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/gsa.svg b/dist/icons/color/browser/gsa.svg new file mode 100644 index 000000000..05d397a8c --- /dev/null +++ b/dist/icons/color/browser/gsa.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/dist/icons/color/browser/icecat.png b/dist/icons/color/browser/icecat.png new file mode 100644 index 000000000..ad1953ae4 Binary files /dev/null and b/dist/icons/color/browser/icecat.png differ diff --git a/dist/icons/color/browser/ie.svg b/dist/icons/color/browser/ie.svg new file mode 100644 index 000000000..4ac80b33f --- /dev/null +++ b/dist/icons/color/browser/ie.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/dist/icons/color/browser/konqueror.png b/dist/icons/color/browser/konqueror.png new file mode 100644 index 000000000..8d2883118 Binary files /dev/null and b/dist/icons/color/browser/konqueror.png differ diff --git a/dist/icons/color/browser/konqueror.svg b/dist/icons/color/browser/konqueror.svg new file mode 100644 index 000000000..a65c880e7 --- /dev/null +++ b/dist/icons/color/browser/konqueror.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/maxthon.png b/dist/icons/color/browser/maxthon.png new file mode 100644 index 000000000..987020e9f Binary files /dev/null and b/dist/icons/color/browser/maxthon.png differ diff --git a/dist/icons/color/browser/midori.png b/dist/icons/color/browser/midori.png new file mode 100644 index 000000000..681d94faa Binary files /dev/null and b/dist/icons/color/browser/midori.png differ diff --git a/dist/icons/color/browser/midori.svg b/dist/icons/color/browser/midori.svg new file mode 100644 index 000000000..66bc085ab --- /dev/null +++ b/dist/icons/color/browser/midori.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/netsurf.png b/dist/icons/color/browser/netsurf.png new file mode 100644 index 000000000..0e0e3717f Binary files /dev/null and b/dist/icons/color/browser/netsurf.png differ diff --git a/dist/icons/color/browser/netsurf.svg b/dist/icons/color/browser/netsurf.svg new file mode 100644 index 000000000..67aa01bc7 --- /dev/null +++ b/dist/icons/color/browser/netsurf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/opera-gx.png b/dist/icons/color/browser/opera-gx.png new file mode 100644 index 000000000..250cc2529 Binary files /dev/null and b/dist/icons/color/browser/opera-gx.png differ diff --git a/dist/icons/color/browser/opera-gx.svg b/dist/icons/color/browser/opera-gx.svg new file mode 100644 index 000000000..aeb51a519 --- /dev/null +++ b/dist/icons/color/browser/opera-gx.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/opera-mini.png b/dist/icons/color/browser/opera-mini.png new file mode 100644 index 000000000..bf220034f Binary files /dev/null and b/dist/icons/color/browser/opera-mini.png differ diff --git a/dist/icons/color/browser/opera-touch.png b/dist/icons/color/browser/opera-touch.png new file mode 100644 index 000000000..3beaa36aa Binary files /dev/null and b/dist/icons/color/browser/opera-touch.png differ diff --git a/dist/icons/color/browser/opera.png b/dist/icons/color/browser/opera.png new file mode 100644 index 000000000..472a15234 Binary files /dev/null and b/dist/icons/color/browser/opera.png differ diff --git a/dist/icons/color/browser/opera.svg b/dist/icons/color/browser/opera.svg new file mode 100644 index 000000000..6e79822ef --- /dev/null +++ b/dist/icons/color/browser/opera.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/palemoon.png b/dist/icons/color/browser/palemoon.png new file mode 100644 index 000000000..8696c0600 Binary files /dev/null and b/dist/icons/color/browser/palemoon.png differ diff --git a/dist/icons/color/browser/puffin.png b/dist/icons/color/browser/puffin.png new file mode 100644 index 000000000..f51149355 Binary files /dev/null and b/dist/icons/color/browser/puffin.png differ diff --git a/dist/icons/color/browser/safari.png b/dist/icons/color/browser/safari.png new file mode 100644 index 000000000..6fe6f54f0 Binary files /dev/null and b/dist/icons/color/browser/safari.png differ diff --git a/dist/icons/color/browser/safari.svg b/dist/icons/color/browser/safari.svg new file mode 100644 index 000000000..214ccc51a --- /dev/null +++ b/dist/icons/color/browser/safari.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/samsung-internet.png b/dist/icons/color/browser/samsung-internet.png new file mode 100644 index 000000000..543645cbe Binary files /dev/null and b/dist/icons/color/browser/samsung-internet.png differ diff --git a/dist/icons/color/browser/samsung-internet.svg b/dist/icons/color/browser/samsung-internet.svg new file mode 100644 index 000000000..5b2caa93b --- /dev/null +++ b/dist/icons/color/browser/samsung-internet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/seamonkey.png b/dist/icons/color/browser/seamonkey.png new file mode 100644 index 000000000..55929783e Binary files /dev/null and b/dist/icons/color/browser/seamonkey.png differ diff --git a/dist/icons/color/browser/silk.png b/dist/icons/color/browser/silk.png new file mode 100644 index 000000000..161e5c446 Binary files /dev/null and b/dist/icons/color/browser/silk.png differ diff --git a/dist/icons/color/browser/sogou-mobile.png b/dist/icons/color/browser/sogou-mobile.png new file mode 100644 index 000000000..33bb21040 Binary files /dev/null and b/dist/icons/color/browser/sogou-mobile.png differ diff --git a/dist/icons/color/browser/ucbrowser.png b/dist/icons/color/browser/ucbrowser.png new file mode 100644 index 000000000..a7f0f978f Binary files /dev/null and b/dist/icons/color/browser/ucbrowser.png differ diff --git a/dist/icons/color/browser/ucbrowser.svg b/dist/icons/color/browser/ucbrowser.svg new file mode 100644 index 000000000..cb461fb2a --- /dev/null +++ b/dist/icons/color/browser/ucbrowser.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/vivaldi.png b/dist/icons/color/browser/vivaldi.png new file mode 100644 index 000000000..64f0d32e7 Binary files /dev/null and b/dist/icons/color/browser/vivaldi.png differ diff --git a/dist/icons/color/browser/vivaldi.svg b/dist/icons/color/browser/vivaldi.svg new file mode 100644 index 000000000..2084b9758 --- /dev/null +++ b/dist/icons/color/browser/vivaldi.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/webkit.png b/dist/icons/color/browser/webkit.png new file mode 100644 index 000000000..cbc0f1b7b Binary files /dev/null and b/dist/icons/color/browser/webkit.png differ diff --git a/dist/icons/color/browser/webkit.svg b/dist/icons/color/browser/webkit.svg new file mode 100644 index 000000000..e1ba7d330 --- /dev/null +++ b/dist/icons/color/browser/webkit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/color/browser/yandex.png b/dist/icons/color/browser/yandex.png new file mode 100644 index 000000000..896456d20 Binary files /dev/null and b/dist/icons/color/browser/yandex.png differ diff --git a/dist/icons/color/os/centos.svg b/dist/icons/color/os/centos.svg new file mode 100644 index 000000000..2338e1876 --- /dev/null +++ b/dist/icons/color/os/centos.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/mono/LICENSE.md b/dist/icons/mono/LICENSE.md new file mode 100644 index 000000000..a4c806be3 --- /dev/null +++ b/dist/icons/mono/LICENSE.md @@ -0,0 +1,3 @@ +CC0-1.0 +https://github.com/simple-icons/simple-icons +https://github.com/coreui/coreui-icons \ No newline at end of file diff --git a/dist/icons/mono/browser/alipay.svg b/dist/icons/mono/browser/alipay.svg new file mode 100644 index 000000000..30fa27f22 --- /dev/null +++ b/dist/icons/mono/browser/alipay.svg @@ -0,0 +1 @@ +Alipay \ No newline at end of file diff --git a/dist/icons/mono/browser/android-browser.svg b/dist/icons/mono/browser/android-browser.svg new file mode 100644 index 000000000..2482150a6 --- /dev/null +++ b/dist/icons/mono/browser/android-browser.svg @@ -0,0 +1 @@ +Android Browser \ No newline at end of file diff --git a/dist/icons/mono/browser/avast-secure-browser.svg b/dist/icons/mono/browser/avast-secure-browser.svg new file mode 100644 index 000000000..b77658a1a --- /dev/null +++ b/dist/icons/mono/browser/avast-secure-browser.svg @@ -0,0 +1 @@ +Avast Secure Browser \ No newline at end of file diff --git a/dist/icons/mono/browser/baidu-browser.svg b/dist/icons/mono/browser/baidu-browser.svg new file mode 100644 index 000000000..f7f0dd163 --- /dev/null +++ b/dist/icons/mono/browser/baidu-browser.svg @@ -0,0 +1 @@ +Baidu Browser \ No newline at end of file diff --git a/dist/icons/mono/browser/brave.svg b/dist/icons/mono/browser/brave.svg new file mode 100644 index 000000000..fa798a452 --- /dev/null +++ b/dist/icons/mono/browser/brave.svg @@ -0,0 +1 @@ +Brave \ No newline at end of file diff --git a/dist/icons/mono/browser/chrome-headless.svg b/dist/icons/mono/browser/chrome-headless.svg new file mode 100644 index 000000000..5fc57d212 --- /dev/null +++ b/dist/icons/mono/browser/chrome-headless.svg @@ -0,0 +1 @@ +Chrome Headless \ No newline at end of file diff --git a/dist/icons/mono/browser/chrome-webview.svg b/dist/icons/mono/browser/chrome-webview.svg new file mode 100644 index 000000000..38b45e32c --- /dev/null +++ b/dist/icons/mono/browser/chrome-webview.svg @@ -0,0 +1 @@ +Chrome Webview \ No newline at end of file diff --git a/dist/icons/mono/browser/chrome.svg b/dist/icons/mono/browser/chrome.svg new file mode 100644 index 000000000..3404d6e21 --- /dev/null +++ b/dist/icons/mono/browser/chrome.svg @@ -0,0 +1 @@ +Chrome \ No newline at end of file diff --git a/dist/icons/mono/browser/chromium.svg b/dist/icons/mono/browser/chromium.svg new file mode 100644 index 000000000..90605418c --- /dev/null +++ b/dist/icons/mono/browser/chromium.svg @@ -0,0 +1 @@ +Chromium \ No newline at end of file diff --git a/dist/icons/mono/browser/duckduckgo.svg b/dist/icons/mono/browser/duckduckgo.svg new file mode 100644 index 000000000..2f466f49e --- /dev/null +++ b/dist/icons/mono/browser/duckduckgo.svg @@ -0,0 +1 @@ +DuckDuckGo \ No newline at end of file diff --git a/dist/icons/mono/browser/edge.svg b/dist/icons/mono/browser/edge.svg new file mode 100644 index 000000000..958a7ed0b --- /dev/null +++ b/dist/icons/mono/browser/edge.svg @@ -0,0 +1 @@ +Edge \ No newline at end of file diff --git a/dist/icons/mono/browser/electron.svg b/dist/icons/mono/browser/electron.svg new file mode 100644 index 000000000..8e855ab30 --- /dev/null +++ b/dist/icons/mono/browser/electron.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/mono/browser/facebook.svg b/dist/icons/mono/browser/facebook.svg new file mode 100644 index 000000000..f66767e46 --- /dev/null +++ b/dist/icons/mono/browser/facebook.svg @@ -0,0 +1 @@ +Facebook \ No newline at end of file diff --git a/dist/icons/mono/browser/firefox-focus.svg b/dist/icons/mono/browser/firefox-focus.svg new file mode 100644 index 000000000..009082ea8 --- /dev/null +++ b/dist/icons/mono/browser/firefox-focus.svg @@ -0,0 +1 @@ +Firefox Focus \ No newline at end of file diff --git a/dist/icons/mono/browser/firefox-reality.svg b/dist/icons/mono/browser/firefox-reality.svg new file mode 100644 index 000000000..e02dcdfeb --- /dev/null +++ b/dist/icons/mono/browser/firefox-reality.svg @@ -0,0 +1 @@ +Firefox Reality \ No newline at end of file diff --git a/dist/icons/mono/browser/firefox.svg b/dist/icons/mono/browser/firefox.svg new file mode 100644 index 000000000..baace0bad --- /dev/null +++ b/dist/icons/mono/browser/firefox.svg @@ -0,0 +1 @@ +Firefox \ No newline at end of file diff --git a/dist/icons/mono/browser/gsa.svg b/dist/icons/mono/browser/gsa.svg new file mode 100644 index 000000000..753feb554 --- /dev/null +++ b/dist/icons/mono/browser/gsa.svg @@ -0,0 +1 @@ +GSA \ No newline at end of file diff --git a/dist/icons/mono/browser/huawei-browser.svg b/dist/icons/mono/browser/huawei-browser.svg new file mode 100644 index 000000000..200489d3c --- /dev/null +++ b/dist/icons/mono/browser/huawei-browser.svg @@ -0,0 +1 @@ +Huawei Browser \ No newline at end of file diff --git a/dist/icons/mono/browser/icecat.svg b/dist/icons/mono/browser/icecat.svg new file mode 100644 index 000000000..05b01735f --- /dev/null +++ b/dist/icons/mono/browser/icecat.svg @@ -0,0 +1 @@ +IceCat \ No newline at end of file diff --git a/dist/icons/mono/browser/ie.svg b/dist/icons/mono/browser/ie.svg new file mode 100644 index 000000000..f444f15fc --- /dev/null +++ b/dist/icons/mono/browser/ie.svg @@ -0,0 +1 @@ +IE \ No newline at end of file diff --git a/dist/icons/mono/browser/instagram.svg b/dist/icons/mono/browser/instagram.svg new file mode 100644 index 000000000..c0e86b0bf --- /dev/null +++ b/dist/icons/mono/browser/instagram.svg @@ -0,0 +1 @@ +Instagram \ No newline at end of file diff --git a/dist/icons/mono/browser/jasmine.svg b/dist/icons/mono/browser/jasmine.svg new file mode 100644 index 000000000..bda1b3ed9 --- /dev/null +++ b/dist/icons/mono/browser/jasmine.svg @@ -0,0 +1 @@ +Jasmine \ No newline at end of file diff --git a/dist/icons/mono/browser/kakaotalk.svg b/dist/icons/mono/browser/kakaotalk.svg new file mode 100644 index 000000000..8e9f9b123 --- /dev/null +++ b/dist/icons/mono/browser/kakaotalk.svg @@ -0,0 +1 @@ +KakaoTalk \ No newline at end of file diff --git a/dist/icons/mono/browser/klarna.svg b/dist/icons/mono/browser/klarna.svg new file mode 100644 index 000000000..c12c2cef4 --- /dev/null +++ b/dist/icons/mono/browser/klarna.svg @@ -0,0 +1 @@ +Klarna \ No newline at end of file diff --git a/dist/icons/mono/browser/line.svg b/dist/icons/mono/browser/line.svg new file mode 100644 index 000000000..9e4dc98c2 --- /dev/null +++ b/dist/icons/mono/browser/line.svg @@ -0,0 +1 @@ +LINE \ No newline at end of file diff --git a/dist/icons/mono/browser/linkedin.svg b/dist/icons/mono/browser/linkedin.svg new file mode 100644 index 000000000..caa6e69d1 --- /dev/null +++ b/dist/icons/mono/browser/linkedin.svg @@ -0,0 +1 @@ +LinkedIn \ No newline at end of file diff --git a/dist/icons/mono/browser/miui-browser.svg b/dist/icons/mono/browser/miui-browser.svg new file mode 100644 index 000000000..a86433446 --- /dev/null +++ b/dist/icons/mono/browser/miui-browser.svg @@ -0,0 +1 @@ +MIUI Browser \ No newline at end of file diff --git a/dist/icons/mono/browser/mobile-chrome.svg b/dist/icons/mono/browser/mobile-chrome.svg new file mode 100644 index 000000000..cc6792673 --- /dev/null +++ b/dist/icons/mono/browser/mobile-chrome.svg @@ -0,0 +1 @@ +Mobile Chrome \ No newline at end of file diff --git a/dist/icons/mono/browser/mobile-firefox.svg b/dist/icons/mono/browser/mobile-firefox.svg new file mode 100644 index 000000000..b00ccef29 --- /dev/null +++ b/dist/icons/mono/browser/mobile-firefox.svg @@ -0,0 +1 @@ +Mobile Firefox \ No newline at end of file diff --git a/dist/icons/mono/browser/mobile-safari.svg b/dist/icons/mono/browser/mobile-safari.svg new file mode 100644 index 000000000..ac9eab67b --- /dev/null +++ b/dist/icons/mono/browser/mobile-safari.svg @@ -0,0 +1 @@ +Mobile Safari \ No newline at end of file diff --git a/dist/icons/mono/browser/mozilla.svg b/dist/icons/mono/browser/mozilla.svg new file mode 100644 index 000000000..507eb6695 --- /dev/null +++ b/dist/icons/mono/browser/mozilla.svg @@ -0,0 +1 @@ +Mozilla \ No newline at end of file diff --git a/dist/icons/mono/browser/naver.svg b/dist/icons/mono/browser/naver.svg new file mode 100644 index 000000000..611ad4b64 --- /dev/null +++ b/dist/icons/mono/browser/naver.svg @@ -0,0 +1 @@ +Naver \ No newline at end of file diff --git a/dist/icons/mono/browser/nokia-browser.svg b/dist/icons/mono/browser/nokia-browser.svg new file mode 100644 index 000000000..bf4b85c6b --- /dev/null +++ b/dist/icons/mono/browser/nokia-browser.svg @@ -0,0 +1 @@ +Nokia Browser \ No newline at end of file diff --git a/dist/icons/mono/browser/oculus-browser.svg b/dist/icons/mono/browser/oculus-browser.svg new file mode 100644 index 000000000..a0d272f06 --- /dev/null +++ b/dist/icons/mono/browser/oculus-browser.svg @@ -0,0 +1 @@ +Oculus Browser \ No newline at end of file diff --git a/dist/icons/mono/browser/opera-coast.svg b/dist/icons/mono/browser/opera-coast.svg new file mode 100644 index 000000000..f7c88c193 --- /dev/null +++ b/dist/icons/mono/browser/opera-coast.svg @@ -0,0 +1 @@ +Opera Coast \ No newline at end of file diff --git a/dist/icons/mono/browser/opera-gx.svg b/dist/icons/mono/browser/opera-gx.svg new file mode 100644 index 000000000..b560822cb --- /dev/null +++ b/dist/icons/mono/browser/opera-gx.svg @@ -0,0 +1 @@ +Opera GX \ No newline at end of file diff --git a/dist/icons/mono/browser/opera-mini.svg b/dist/icons/mono/browser/opera-mini.svg new file mode 100644 index 000000000..1cc64857a --- /dev/null +++ b/dist/icons/mono/browser/opera-mini.svg @@ -0,0 +1 @@ +Opera Mini \ No newline at end of file diff --git a/dist/icons/mono/browser/opera-mobi.svg b/dist/icons/mono/browser/opera-mobi.svg new file mode 100644 index 000000000..dfc102673 --- /dev/null +++ b/dist/icons/mono/browser/opera-mobi.svg @@ -0,0 +1 @@ +Opera Mobi \ No newline at end of file diff --git a/dist/icons/mono/browser/opera-tablet.svg b/dist/icons/mono/browser/opera-tablet.svg new file mode 100644 index 000000000..d47eea29c --- /dev/null +++ b/dist/icons/mono/browser/opera-tablet.svg @@ -0,0 +1 @@ +Opera Tablet \ No newline at end of file diff --git a/dist/icons/mono/browser/opera-touch.svg b/dist/icons/mono/browser/opera-touch.svg new file mode 100644 index 000000000..50211d4de --- /dev/null +++ b/dist/icons/mono/browser/opera-touch.svg @@ -0,0 +1 @@ +Opera Touch \ No newline at end of file diff --git a/dist/icons/mono/browser/opera.svg b/dist/icons/mono/browser/opera.svg new file mode 100644 index 000000000..d1194ee24 --- /dev/null +++ b/dist/icons/mono/browser/opera.svg @@ -0,0 +1 @@ +Opera \ No newline at end of file diff --git a/dist/icons/mono/browser/qqbrowser.svg b/dist/icons/mono/browser/qqbrowser.svg new file mode 100644 index 000000000..6a94a49f7 --- /dev/null +++ b/dist/icons/mono/browser/qqbrowser.svg @@ -0,0 +1 @@ +QQBrowser \ No newline at end of file diff --git a/dist/icons/mono/browser/qqbrowserlite.svg b/dist/icons/mono/browser/qqbrowserlite.svg new file mode 100644 index 000000000..d4ccef286 --- /dev/null +++ b/dist/icons/mono/browser/qqbrowserlite.svg @@ -0,0 +1 @@ +QQBrowserLite \ No newline at end of file diff --git a/dist/icons/mono/browser/safari.svg b/dist/icons/mono/browser/safari.svg new file mode 100644 index 000000000..5d1543889 --- /dev/null +++ b/dist/icons/mono/browser/safari.svg @@ -0,0 +1 @@ +Safari \ No newline at end of file diff --git a/dist/icons/mono/browser/sailfish-browser.svg b/dist/icons/mono/browser/sailfish-browser.svg new file mode 100644 index 000000000..0884da1c3 --- /dev/null +++ b/dist/icons/mono/browser/sailfish-browser.svg @@ -0,0 +1 @@ +Sailfish Browser \ No newline at end of file diff --git a/dist/icons/mono/browser/samsung-internet.svg b/dist/icons/mono/browser/samsung-internet.svg new file mode 100644 index 000000000..15e89529f --- /dev/null +++ b/dist/icons/mono/browser/samsung-internet.svg @@ -0,0 +1 @@ +Samsung Internet \ No newline at end of file diff --git a/dist/icons/mono/browser/smart-lenovo-browser.svg b/dist/icons/mono/browser/smart-lenovo-browser.svg new file mode 100644 index 000000000..e9be1500a --- /dev/null +++ b/dist/icons/mono/browser/smart-lenovo-browser.svg @@ -0,0 +1 @@ +Smart Lenovo Browser \ No newline at end of file diff --git a/dist/icons/mono/browser/snapchat.svg b/dist/icons/mono/browser/snapchat.svg new file mode 100644 index 000000000..3110fbada --- /dev/null +++ b/dist/icons/mono/browser/snapchat.svg @@ -0,0 +1 @@ +Snapchat \ No newline at end of file diff --git a/dist/icons/mono/browser/sogou-explorer.svg b/dist/icons/mono/browser/sogou-explorer.svg new file mode 100644 index 000000000..1449ecb61 --- /dev/null +++ b/dist/icons/mono/browser/sogou-explorer.svg @@ -0,0 +1 @@ +Sogou Explorer \ No newline at end of file diff --git a/dist/icons/mono/browser/sogou-mobile.svg b/dist/icons/mono/browser/sogou-mobile.svg new file mode 100644 index 000000000..36165fe4c --- /dev/null +++ b/dist/icons/mono/browser/sogou-mobile.svg @@ -0,0 +1 @@ +Sogou Mobile \ No newline at end of file diff --git a/dist/icons/mono/browser/tesla.svg b/dist/icons/mono/browser/tesla.svg new file mode 100644 index 000000000..77e66012d --- /dev/null +++ b/dist/icons/mono/browser/tesla.svg @@ -0,0 +1 @@ +Tesla \ No newline at end of file diff --git a/dist/icons/mono/browser/tiktok.svg b/dist/icons/mono/browser/tiktok.svg new file mode 100644 index 000000000..57ce3ae5d --- /dev/null +++ b/dist/icons/mono/browser/tiktok.svg @@ -0,0 +1 @@ +TikTok \ No newline at end of file diff --git a/dist/icons/mono/browser/twitter.svg b/dist/icons/mono/browser/twitter.svg new file mode 100644 index 000000000..b4aeefd37 --- /dev/null +++ b/dist/icons/mono/browser/twitter.svg @@ -0,0 +1 @@ +Twitter \ No newline at end of file diff --git a/dist/icons/mono/browser/vivaldi.svg b/dist/icons/mono/browser/vivaldi.svg new file mode 100644 index 000000000..167e8ad69 --- /dev/null +++ b/dist/icons/mono/browser/vivaldi.svg @@ -0,0 +1 @@ +Vivaldi \ No newline at end of file diff --git a/dist/icons/mono/browser/vivo-browser.svg b/dist/icons/mono/browser/vivo-browser.svg new file mode 100644 index 000000000..db5270682 --- /dev/null +++ b/dist/icons/mono/browser/vivo-browser.svg @@ -0,0 +1 @@ +vivo Browser \ No newline at end of file diff --git a/dist/icons/mono/browser/wechat.svg b/dist/icons/mono/browser/wechat.svg new file mode 100644 index 000000000..c3eb6c4a6 --- /dev/null +++ b/dist/icons/mono/browser/wechat.svg @@ -0,0 +1 @@ +WeChat \ No newline at end of file diff --git a/dist/icons/mono/browser/weibo.svg b/dist/icons/mono/browser/weibo.svg new file mode 100644 index 000000000..13944085e --- /dev/null +++ b/dist/icons/mono/browser/weibo.svg @@ -0,0 +1 @@ +Weibo \ No newline at end of file diff --git a/dist/icons/mono/browser/yandex.svg b/dist/icons/mono/browser/yandex.svg new file mode 100644 index 000000000..16a76fe16 --- /dev/null +++ b/dist/icons/mono/browser/yandex.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/mono/device/acer.svg b/dist/icons/mono/device/acer.svg new file mode 100644 index 000000000..ca9b2bbf7 --- /dev/null +++ b/dist/icons/mono/device/acer.svg @@ -0,0 +1 @@ +Acer \ No newline at end of file diff --git a/dist/icons/mono/device/amazon.svg b/dist/icons/mono/device/amazon.svg new file mode 100644 index 000000000..70316babd --- /dev/null +++ b/dist/icons/mono/device/amazon.svg @@ -0,0 +1 @@ +Amazon \ No newline at end of file diff --git a/dist/icons/mono/device/apple.svg b/dist/icons/mono/device/apple.svg new file mode 100644 index 000000000..de4b0d17b --- /dev/null +++ b/dist/icons/mono/device/apple.svg @@ -0,0 +1 @@ +Apple \ No newline at end of file diff --git a/dist/icons/mono/device/asus.svg b/dist/icons/mono/device/asus.svg new file mode 100644 index 000000000..18ef199da --- /dev/null +++ b/dist/icons/mono/device/asus.svg @@ -0,0 +1 @@ +ASUS \ No newline at end of file diff --git a/dist/icons/mono/device/att.svg b/dist/icons/mono/device/att.svg new file mode 100644 index 000000000..166ca3920 --- /dev/null +++ b/dist/icons/mono/device/att.svg @@ -0,0 +1 @@ +AT&T \ No newline at end of file diff --git a/dist/icons/mono/device/blackberry.svg b/dist/icons/mono/device/blackberry.svg new file mode 100644 index 000000000..b0c55837a --- /dev/null +++ b/dist/icons/mono/device/blackberry.svg @@ -0,0 +1 @@ +Blackberry \ No newline at end of file diff --git a/dist/icons/mono/device/dell.svg b/dist/icons/mono/device/dell.svg new file mode 100644 index 000000000..d272bbf6f --- /dev/null +++ b/dist/icons/mono/device/dell.svg @@ -0,0 +1 @@ +Dell \ No newline at end of file diff --git a/dist/icons/mono/device/facebook.svg b/dist/icons/mono/device/facebook.svg new file mode 100644 index 000000000..f66767e46 --- /dev/null +++ b/dist/icons/mono/device/facebook.svg @@ -0,0 +1 @@ +Facebook \ No newline at end of file diff --git a/dist/icons/mono/device/fairphone.svg b/dist/icons/mono/device/fairphone.svg new file mode 100644 index 000000000..3262e30dc --- /dev/null +++ b/dist/icons/mono/device/fairphone.svg @@ -0,0 +1 @@ +Fairphone \ No newline at end of file diff --git a/dist/icons/mono/device/google.svg b/dist/icons/mono/device/google.svg new file mode 100644 index 000000000..2eaf91554 --- /dev/null +++ b/dist/icons/mono/device/google.svg @@ -0,0 +1 @@ +Google \ No newline at end of file diff --git a/dist/icons/mono/device/hp.svg b/dist/icons/mono/device/hp.svg new file mode 100644 index 000000000..04b5f0ef8 --- /dev/null +++ b/dist/icons/mono/device/hp.svg @@ -0,0 +1 @@ +HP \ No newline at end of file diff --git a/dist/icons/mono/device/huawei.svg b/dist/icons/mono/device/huawei.svg new file mode 100644 index 000000000..0c72bab82 --- /dev/null +++ b/dist/icons/mono/device/huawei.svg @@ -0,0 +1 @@ +Huawei \ No newline at end of file diff --git a/dist/icons/mono/device/lenovo.svg b/dist/icons/mono/device/lenovo.svg new file mode 100644 index 000000000..c03719a43 --- /dev/null +++ b/dist/icons/mono/device/lenovo.svg @@ -0,0 +1 @@ +Lenovo \ No newline at end of file diff --git a/dist/icons/mono/device/lg.svg b/dist/icons/mono/device/lg.svg new file mode 100644 index 000000000..5eedc7978 --- /dev/null +++ b/dist/icons/mono/device/lg.svg @@ -0,0 +1 @@ +LG \ No newline at end of file diff --git a/dist/icons/mono/device/meizu.svg b/dist/icons/mono/device/meizu.svg new file mode 100644 index 000000000..8f903f6dc --- /dev/null +++ b/dist/icons/mono/device/meizu.svg @@ -0,0 +1 @@ +Meizu \ No newline at end of file diff --git a/dist/icons/mono/device/microsoft.svg b/dist/icons/mono/device/microsoft.svg new file mode 100644 index 000000000..eeacf2520 --- /dev/null +++ b/dist/icons/mono/device/microsoft.svg @@ -0,0 +1 @@ +Microsoft \ No newline at end of file diff --git a/dist/icons/mono/device/motorola.svg b/dist/icons/mono/device/motorola.svg new file mode 100644 index 000000000..3e744bda2 --- /dev/null +++ b/dist/icons/mono/device/motorola.svg @@ -0,0 +1 @@ +Motorola \ No newline at end of file diff --git a/dist/icons/mono/device/nintendo.svg b/dist/icons/mono/device/nintendo.svg new file mode 100644 index 000000000..5cad9642f --- /dev/null +++ b/dist/icons/mono/device/nintendo.svg @@ -0,0 +1 @@ +Nintendo \ No newline at end of file diff --git a/dist/icons/mono/device/nokia.svg b/dist/icons/mono/device/nokia.svg new file mode 100644 index 000000000..a4a21137e --- /dev/null +++ b/dist/icons/mono/device/nokia.svg @@ -0,0 +1 @@ +Nokia \ No newline at end of file diff --git a/dist/icons/mono/device/nvidia.svg b/dist/icons/mono/device/nvidia.svg new file mode 100644 index 000000000..2c7ff66f8 --- /dev/null +++ b/dist/icons/mono/device/nvidia.svg @@ -0,0 +1 @@ +NVIDIA \ No newline at end of file diff --git a/dist/icons/mono/device/oneplus.svg b/dist/icons/mono/device/oneplus.svg new file mode 100644 index 000000000..11c3131ad --- /dev/null +++ b/dist/icons/mono/device/oneplus.svg @@ -0,0 +1 @@ +OnePlus \ No newline at end of file diff --git a/dist/icons/mono/device/oppo.svg b/dist/icons/mono/device/oppo.svg new file mode 100644 index 000000000..b886af3e7 --- /dev/null +++ b/dist/icons/mono/device/oppo.svg @@ -0,0 +1 @@ +OPPO \ No newline at end of file diff --git a/dist/icons/mono/device/panasonic.svg b/dist/icons/mono/device/panasonic.svg new file mode 100644 index 000000000..e5902436c --- /dev/null +++ b/dist/icons/mono/device/panasonic.svg @@ -0,0 +1 @@ +Panasonic \ No newline at end of file diff --git a/dist/icons/mono/device/roku.svg b/dist/icons/mono/device/roku.svg new file mode 100644 index 000000000..17dde5e36 --- /dev/null +++ b/dist/icons/mono/device/roku.svg @@ -0,0 +1 @@ +Roku \ No newline at end of file diff --git a/dist/icons/mono/device/samsung.svg b/dist/icons/mono/device/samsung.svg new file mode 100644 index 000000000..627cbf2c1 --- /dev/null +++ b/dist/icons/mono/device/samsung.svg @@ -0,0 +1 @@ +Samsung \ No newline at end of file diff --git a/dist/icons/mono/device/siemens.svg b/dist/icons/mono/device/siemens.svg new file mode 100644 index 000000000..ec60e7b17 --- /dev/null +++ b/dist/icons/mono/device/siemens.svg @@ -0,0 +1 @@ +Siemens \ No newline at end of file diff --git a/dist/icons/mono/device/sony.svg b/dist/icons/mono/device/sony.svg new file mode 100644 index 000000000..38d17e13d --- /dev/null +++ b/dist/icons/mono/device/sony.svg @@ -0,0 +1 @@ +Sony \ No newline at end of file diff --git a/dist/icons/mono/device/tesla.svg b/dist/icons/mono/device/tesla.svg new file mode 100644 index 000000000..77e66012d --- /dev/null +++ b/dist/icons/mono/device/tesla.svg @@ -0,0 +1 @@ +Tesla \ No newline at end of file diff --git a/dist/icons/mono/device/vivo.svg b/dist/icons/mono/device/vivo.svg new file mode 100644 index 000000000..0591ac425 --- /dev/null +++ b/dist/icons/mono/device/vivo.svg @@ -0,0 +1 @@ +vivo \ No newline at end of file diff --git a/dist/icons/mono/device/vodafone.svg b/dist/icons/mono/device/vodafone.svg new file mode 100644 index 000000000..f882e2fe0 --- /dev/null +++ b/dist/icons/mono/device/vodafone.svg @@ -0,0 +1 @@ +Vodafone \ No newline at end of file diff --git a/dist/icons/mono/device/xiaomi.svg b/dist/icons/mono/device/xiaomi.svg new file mode 100644 index 000000000..f08eae5bd --- /dev/null +++ b/dist/icons/mono/device/xiaomi.svg @@ -0,0 +1 @@ +Xiaomi \ No newline at end of file diff --git a/dist/icons/mono/device/zebra.svg b/dist/icons/mono/device/zebra.svg new file mode 100644 index 000000000..8282e7744 --- /dev/null +++ b/dist/icons/mono/device/zebra.svg @@ -0,0 +1 @@ +Zebra \ No newline at end of file diff --git a/dist/icons/mono/os/android.svg b/dist/icons/mono/os/android.svg new file mode 100644 index 000000000..3f44ef6ba --- /dev/null +++ b/dist/icons/mono/os/android.svg @@ -0,0 +1 @@ +Android \ No newline at end of file diff --git a/dist/icons/mono/os/arch.svg b/dist/icons/mono/os/arch.svg new file mode 100644 index 000000000..a331ef91d --- /dev/null +++ b/dist/icons/mono/os/arch.svg @@ -0,0 +1 @@ +Arch \ No newline at end of file diff --git a/dist/icons/mono/os/blackberry.svg b/dist/icons/mono/os/blackberry.svg new file mode 100644 index 000000000..b0c55837a --- /dev/null +++ b/dist/icons/mono/os/blackberry.svg @@ -0,0 +1 @@ +Blackberry \ No newline at end of file diff --git a/dist/icons/mono/os/centos.svg b/dist/icons/mono/os/centos.svg new file mode 100644 index 000000000..3f16a9e00 --- /dev/null +++ b/dist/icons/mono/os/centos.svg @@ -0,0 +1 @@ +CentOS \ No newline at end of file diff --git a/dist/icons/mono/os/chrome-os.svg b/dist/icons/mono/os/chrome-os.svg new file mode 100644 index 000000000..0a282625e --- /dev/null +++ b/dist/icons/mono/os/chrome-os.svg @@ -0,0 +1 @@ +Chrome OS \ No newline at end of file diff --git a/dist/icons/mono/os/chromecast.svg b/dist/icons/mono/os/chromecast.svg new file mode 100644 index 000000000..6b251ed8a --- /dev/null +++ b/dist/icons/mono/os/chromecast.svg @@ -0,0 +1 @@ +Chromecast \ No newline at end of file diff --git a/dist/icons/mono/os/debian.svg b/dist/icons/mono/os/debian.svg new file mode 100644 index 000000000..c983b3245 --- /dev/null +++ b/dist/icons/mono/os/debian.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/mono/os/deepin.svg b/dist/icons/mono/os/deepin.svg new file mode 100644 index 000000000..2a5fbca2d --- /dev/null +++ b/dist/icons/mono/os/deepin.svg @@ -0,0 +1 @@ +deepin \ No newline at end of file diff --git a/dist/icons/mono/os/elementary-os.svg b/dist/icons/mono/os/elementary-os.svg new file mode 100644 index 000000000..2b4c9515b --- /dev/null +++ b/dist/icons/mono/os/elementary-os.svg @@ -0,0 +1 @@ +elementary OS \ No newline at end of file diff --git a/dist/icons/mono/os/fedora.svg b/dist/icons/mono/os/fedora.svg new file mode 100644 index 000000000..7f461d0e6 --- /dev/null +++ b/dist/icons/mono/os/fedora.svg @@ -0,0 +1 @@ +Fedora \ No newline at end of file diff --git a/dist/icons/mono/os/firefox.svg b/dist/icons/mono/os/firefox.svg new file mode 100644 index 000000000..e7c9fff23 --- /dev/null +++ b/dist/icons/mono/os/firefox.svg @@ -0,0 +1 @@ +Firefox \ No newline at end of file diff --git a/dist/icons/mono/os/freebsd.svg b/dist/icons/mono/os/freebsd.svg new file mode 100644 index 000000000..8febe9d4c --- /dev/null +++ b/dist/icons/mono/os/freebsd.svg @@ -0,0 +1 @@ +FreeBSD \ No newline at end of file diff --git a/dist/icons/mono/os/gentoo.svg b/dist/icons/mono/os/gentoo.svg new file mode 100644 index 000000000..4c48a0227 --- /dev/null +++ b/dist/icons/mono/os/gentoo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/mono/os/gnu.svg b/dist/icons/mono/os/gnu.svg new file mode 100644 index 000000000..f29f4d7e4 --- /dev/null +++ b/dist/icons/mono/os/gnu.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dist/icons/mono/os/harmonyos.svg b/dist/icons/mono/os/harmonyos.svg new file mode 100644 index 000000000..99b8d7e62 --- /dev/null +++ b/dist/icons/mono/os/harmonyos.svg @@ -0,0 +1 @@ +HarmonyOS \ No newline at end of file diff --git a/dist/icons/mono/os/ios.svg b/dist/icons/mono/os/ios.svg new file mode 100644 index 000000000..a16369768 --- /dev/null +++ b/dist/icons/mono/os/ios.svg @@ -0,0 +1 @@ +iOS \ No newline at end of file diff --git a/dist/icons/mono/os/kaios.svg b/dist/icons/mono/os/kaios.svg new file mode 100644 index 000000000..27a0ec770 --- /dev/null +++ b/dist/icons/mono/os/kaios.svg @@ -0,0 +1 @@ +KaiOS \ No newline at end of file diff --git a/dist/icons/mono/os/linux.svg b/dist/icons/mono/os/linux.svg new file mode 100644 index 000000000..381d3d8a8 --- /dev/null +++ b/dist/icons/mono/os/linux.svg @@ -0,0 +1 @@ +Linux \ No newline at end of file diff --git a/dist/icons/mono/os/macos.svg b/dist/icons/mono/os/macos.svg new file mode 100644 index 000000000..87630c456 --- /dev/null +++ b/dist/icons/mono/os/macos.svg @@ -0,0 +1 @@ +macOS \ No newline at end of file diff --git a/dist/icons/mono/os/manjaro.svg b/dist/icons/mono/os/manjaro.svg new file mode 100644 index 000000000..d99972e2a --- /dev/null +++ b/dist/icons/mono/os/manjaro.svg @@ -0,0 +1 @@ +Manjaro \ No newline at end of file diff --git a/dist/icons/mono/os/mint.svg b/dist/icons/mono/os/mint.svg new file mode 100644 index 000000000..442c8b2d5 --- /dev/null +++ b/dist/icons/mono/os/mint.svg @@ -0,0 +1 @@ +Mint \ No newline at end of file diff --git a/dist/icons/mono/os/netbsd.svg b/dist/icons/mono/os/netbsd.svg new file mode 100644 index 000000000..511f066ed --- /dev/null +++ b/dist/icons/mono/os/netbsd.svg @@ -0,0 +1 @@ +NetBSD \ No newline at end of file diff --git a/dist/icons/mono/os/nintendo.svg b/dist/icons/mono/os/nintendo.svg new file mode 100644 index 000000000..5cad9642f --- /dev/null +++ b/dist/icons/mono/os/nintendo.svg @@ -0,0 +1 @@ +Nintendo \ No newline at end of file diff --git a/dist/icons/mono/os/openbsd.svg b/dist/icons/mono/os/openbsd.svg new file mode 100644 index 000000000..e78fbc7e6 --- /dev/null +++ b/dist/icons/mono/os/openbsd.svg @@ -0,0 +1 @@ +OpenBSD \ No newline at end of file diff --git a/dist/icons/mono/os/playstation.svg b/dist/icons/mono/os/playstation.svg new file mode 100644 index 000000000..be3370316 --- /dev/null +++ b/dist/icons/mono/os/playstation.svg @@ -0,0 +1 @@ +PlayStation \ No newline at end of file diff --git a/dist/icons/mono/os/raspbian.svg b/dist/icons/mono/os/raspbian.svg new file mode 100644 index 000000000..de6feed52 --- /dev/null +++ b/dist/icons/mono/os/raspbian.svg @@ -0,0 +1 @@ +Raspbian \ No newline at end of file diff --git a/dist/icons/mono/os/redhat.svg b/dist/icons/mono/os/redhat.svg new file mode 100644 index 000000000..b68e41333 --- /dev/null +++ b/dist/icons/mono/os/redhat.svg @@ -0,0 +1 @@ +RedHat \ No newline at end of file diff --git a/dist/icons/mono/os/sailfish.svg b/dist/icons/mono/os/sailfish.svg new file mode 100644 index 000000000..b695e10b4 --- /dev/null +++ b/dist/icons/mono/os/sailfish.svg @@ -0,0 +1 @@ +Sailfish \ No newline at end of file diff --git a/dist/icons/mono/os/slackware.svg b/dist/icons/mono/os/slackware.svg new file mode 100644 index 000000000..611bf809d --- /dev/null +++ b/dist/icons/mono/os/slackware.svg @@ -0,0 +1 @@ +Slackware \ No newline at end of file diff --git a/dist/icons/mono/os/suse.svg b/dist/icons/mono/os/suse.svg new file mode 100644 index 000000000..a4e474a1d --- /dev/null +++ b/dist/icons/mono/os/suse.svg @@ -0,0 +1 @@ +SUSE \ No newline at end of file diff --git a/dist/icons/mono/os/ubuntu.svg b/dist/icons/mono/os/ubuntu.svg new file mode 100644 index 000000000..227f86aa3 --- /dev/null +++ b/dist/icons/mono/os/ubuntu.svg @@ -0,0 +1 @@ +Ubuntu \ No newline at end of file diff --git a/dist/icons/mono/os/windows.svg b/dist/icons/mono/os/windows.svg new file mode 100644 index 000000000..9c79dffdb --- /dev/null +++ b/dist/icons/mono/os/windows.svg @@ -0,0 +1 @@ +Windows 10 \ No newline at end of file diff --git a/dist/icons/mono/os/xbox.svg b/dist/icons/mono/os/xbox.svg new file mode 100644 index 000000000..85a9689a6 --- /dev/null +++ b/dist/icons/mono/os/xbox.svg @@ -0,0 +1 @@ +Xbox \ No newline at end of file diff --git a/dist/ua-parser.min.js b/dist/ua-parser.min.js index 3089db6ca..22782dfc5 100644 --- a/dist/ua-parser.min.js +++ b/dist/ua-parser.min.js @@ -1,4 +1,4 @@ -/* UAParser.js v0.7.33 - Copyright Š 2012-2021 Faisal Salman - MIT License */ -(function(window,undefined){"use strict";var LIBVERSION="0.7.33",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded",UA_MAX_LENGTH=350;var AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",BROWSER="Browser",CHROME="Chrome",EDGE="Edge",FIREFOX="Firefox",GOOGLE="Google",HUAWEI="Huawei",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",OPERA="Opera",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",FACEBOOK="Facebook";var extend=function(regexes,extensions){var mergedRegexes={};for(var i in regexes){if(extensions[i]&&extensions[i].length%2===0){mergedRegexes[i]=extensions[i].concat(regexes[i])}else{mergedRegexes[i]=regexes[i]}}return mergedRegexes},enumerize=function(arr){var enums={};for(var i=0;i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length===3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length===4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;jUA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this};this.setUA(_ua);return this};UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(typeof window!==UNDEF_TYPE){window.UAParser=UAParser}}var $=typeof window!==UNDEF_TYPE&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file +/* UAParser.js v2.0.4 + Copyright Š 2012-2025 Faisal Salman + AGPLv3 License */ +(function(window,undefined){"use strict";var LIBVERSION="2.0.4",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;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]:[])}}}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){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str2)==lowerize(str1[i]))return true}return false}return isString(str1)?lowerize(str2)==lowerize(str1):false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){if(!arr.hasOwnProperty(i))continue;var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length>=3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){if(q.length>3){this[q[0]]=match?q[1].apply(this,q.slice(2)):undefined}else{this[q[0]]=match?q[1].call(this,match,q[2]):undefined}}else{if(q.length==3){this[q[0]]=match?match.replace(q[1],q[2]):undefined}else if(q.length==4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}else if(q.length>4){this[q[0]]=match?q[3].apply(this,[match.replace(q[1],q[2])].concat(q.slice(4))):undefined}}}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i=0;i=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);if(typeof exports!==UNDEF_TYPE){if(typeof module!==UNDEF_TYPE&&module.exports){exports=module.exports=UAParser}exports.UAParser=UAParser}else{if(typeof define===FUNC_TYPE&&define.amd){define(function(){return UAParser})}else if(isWindow){window.UAParser=UAParser}}var $=isWindow&&(window.jQuery||window.Zepto);if($&&!$.ua){var parser=new UAParser;$.ua=parser.getResult();$.ua.get=function(){return parser.getUA()};$.ua.set=function(ua){parser.setUA(ua);var result=parser.getResult();for(var prop in result){$.ua[prop]=result[prop]}}}})(typeof window==="object"?window:this); \ No newline at end of file diff --git a/dist/ua-parser.min.mjs b/dist/ua-parser.min.mjs new file mode 100644 index 000000000..c8afed4a9 --- /dev/null +++ b/dist/ua-parser.min.mjs @@ -0,0 +1,4 @@ +/* UAParser.js v2.0.4 + Copyright Š 2012-2025 Faisal Salman + AGPLv3 License */ +var LIBVERSION="2.0.4",UA_MAX_LENGTH=500,USER_AGENT="user-agent",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",UA_BROWSER="browser",UA_CPU="cpu",UA_DEVICE="device",UA_ENGINE="engine",UA_OS="os",UA_RESULT="result",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",MAJOR="major",MODEL="model",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",XR="xr",EMBEDDED="embedded",INAPP="inapp",BRANDS="brands",FORMFACTORS="formFactors",FULLVERLIST="fullVersionList",PLATFORM="platform",PLATFORMVER="platformVersion",BITNESS="bitness",CH_HEADER="sec-ch-ua",CH_HEADER_FULL_VER_LIST=CH_HEADER+"-full-version-list",CH_HEADER_ARCH=CH_HEADER+"-arch",CH_HEADER_BITNESS=CH_HEADER+"-"+BITNESS,CH_HEADER_FORM_FACTORS=CH_HEADER+"-form-factors",CH_HEADER_MOBILE=CH_HEADER+"-"+MOBILE,CH_HEADER_MODEL=CH_HEADER+"-"+MODEL,CH_HEADER_PLATFORM=CH_HEADER+"-"+PLATFORM,CH_HEADER_PLATFORM_VER=CH_HEADER_PLATFORM+"-version",CH_ALL_VALUES=[BRANDS,FULLVERLIST,MOBILE,MODEL,PLATFORM,PLATFORMVER,ARCHITECTURE,FORMFACTORS,BITNESS],AMAZON="Amazon",APPLE="Apple",ASUS="ASUS",BLACKBERRY="BlackBerry",GOOGLE="Google",HUAWEI="Huawei",LENOVO="Lenovo",HONOR="Honor",LG="LG",MICROSOFT="Microsoft",MOTOROLA="Motorola",NVIDIA="Nvidia",ONEPLUS="OnePlus",OPPO="OPPO",SAMSUNG="Samsung",SHARP="Sharp",SONY="Sony",XIAOMI="Xiaomi",ZEBRA="Zebra",CHROME="Chrome",CHROMIUM="Chromium",CHROMECAST="Chromecast",EDGE="Edge",FIREFOX="Firefox",OPERA="Opera",FACEBOOK="Facebook",SOGOU="Sogou",PREFIX_MOBILE="Mobile ",SUFFIX_BROWSER=" Browser",WINDOWS="Windows";var isWindow=typeof window!==UNDEF_TYPE,NAVIGATOR=isWindow&&window.navigator?window.navigator:undefined,NAVIGATOR_UADATA=NAVIGATOR&&NAVIGATOR.userAgentData?NAVIGATOR.userAgentData:undefined;var extend=function(defaultRgx,extensions){var mergedRgx={};var extraRgx=extensions;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]:[])}}}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){var enums={};for(var i=0;i0){for(var i in str1){if(lowerize(str2)==lowerize(str1[i]))return true}return false}return isString(str1)?lowerize(str2)==lowerize(str1):false},isExtensions=function(obj,deep){for(var prop in obj){return/^(browser|cpu|device|engine|os)$/.test(prop)||(deep?isExtensions(obj[prop]):false)}},isString=function(val){return typeof val===STR_TYPE},itemListToArray=function(header){if(!header)return undefined;var arr=[];var tokens=strip(/\\?\"/g,header).split(",");for(var i=0;i-1){var token=trim(tokens[i]).split(";v=");arr[i]={brand:token[0],version:token[1]}}else{arr[i]=trim(tokens[i])}}return arr},lowerize=function(str){return isString(str)?str.toLowerCase():str},majorize=function(version){return isString(version)?strip(/[^\d\.]/g,version).split(".")[0]:undefined},setProps=function(arr){for(var i in arr){if(!arr.hasOwnProperty(i))continue;var propName=arr[i];if(typeof propName==OBJ_TYPE&&propName.length==2){this[propName[0]]=propName[1]}else{this[propName]=undefined}}return this},strip=function(pattern,str){return isString(str)?str.replace(pattern,EMPTY):str},stripQuotes=function(str){return strip(/\\?\"/g,str)},trim=function(str,len){if(isString(str)){str=strip(/^\s\s*/,str);return typeof len===UNDEF_TYPE?str:str.substring(0,UA_MAX_LENGTH)}};var rgxMapper=function(ua,arrays){if(!ua||!arrays)return;var i=0,j,k,p,q,matches,match;while(i0){if(q.length===2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length>=3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){if(q.length>3){this[q[0]]=match?q[1].apply(this,q.slice(2)):undefined}else{this[q[0]]=match?q[1].call(this,match,q[2]):undefined}}else{if(q.length==3){this[q[0]]=match?match.replace(q[1],q[2]):undefined}else if(q.length==4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}else if(q.length>4){this[q[0]]=match?q[3].apply(this,[match.replace(q[1],q[2])].concat(q.slice(4))):undefined}}}}else{this[q]=match?match:undefined}}}}i+=2}},strMapper=function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j2){this.set(MODEL,"iPad").set(TYPE,TABLET)}break;case UA_OS:if(!this.get(NAME)&&NAVIGATOR_UADATA&&NAVIGATOR_UADATA[PLATFORM]){this.set(NAME,NAVIGATOR_UADATA[PLATFORM])}break;case UA_RESULT:var data=this.data;var detect=function(itemType){return data[itemType].getItem().detectFeature().get()};this.set(UA_BROWSER,detect(UA_BROWSER)).set(UA_CPU,detect(UA_CPU)).set(UA_DEVICE,detect(UA_DEVICE)).set(UA_ENGINE,detect(UA_ENGINE)).set(UA_OS,detect(UA_OS))}}return this};this.parseUA=function(){if(this.itemType!=UA_RESULT){rgxMapper.call(this.data,this.ua,this.rgxMap)}if(this.itemType==UA_BROWSER){this.set(MAJOR,majorize(this.get(VERSION)))}return this};this.parseCH=function(){var uaCH=this.uaCH,rgxMap=this.rgxMap;switch(this.itemType){case UA_BROWSER:case UA_ENGINE:var brands=uaCH[FULLVERLIST]||uaCH[BRANDS],prevName;if(brands){for(var i=0;i=13?"11":"10";this.set(NAME,osName).set(VERSION,osVersion)}if(this.get(NAME)==WINDOWS&&uaCH[MODEL]=="Xbox"){this.set(NAME,"Xbox").set(VERSION,undefined)}break;case UA_RESULT:var data=this.data;var parse=function(itemType){return data[itemType].getItem().setCH(uaCH).parseCH().get()};this.set(UA_BROWSER,parse(UA_BROWSER)).set(UA_CPU,parse(UA_CPU)).set(UA_DEVICE,parse(UA_DEVICE)).set(UA_ENGINE,parse(UA_ENGINE)).set(UA_OS,parse(UA_OS))}return this};setProps.call(this,[["itemType",itemType],["ua",ua],["uaCH",uaCH],["rgxMap",rgxMap],["data",createIData(this,itemType)]]);return this}function UAParser(ua,extensions,headers){if(typeof ua===OBJ_TYPE){if(isExtensions(ua,true)){if(typeof extensions===OBJ_TYPE){headers=extensions}extensions=ua}else{headers=ua;extensions=undefined}ua=undefined}else if(typeof ua===STR_TYPE&&!isExtensions(extensions,true)){headers=extensions;extensions=undefined}if(headers&&typeof headers.append===FUNC_TYPE){var kv={};headers.forEach(function(v,k){kv[k]=v});headers=kv}if(!(this instanceof UAParser)){return new UAParser(ua,extensions,headers).getResult()}var userAgent=typeof ua===STR_TYPE?ua:headers&&headers[USER_AGENT]?headers[USER_AGENT]:NAVIGATOR&&NAVIGATOR.userAgent?NAVIGATOR.userAgent:EMPTY,httpUACH=new UACHData(headers,true),regexMap=extensions?extend(defaultRegexes,extensions):defaultRegexes,createItemFunc=function(itemType){if(itemType==UA_RESULT){return function(){return new UAItem(itemType,userAgent,regexMap,httpUACH).set("ua",userAgent).set(UA_BROWSER,this.getBrowser()).set(UA_CPU,this.getCPU()).set(UA_DEVICE,this.getDevice()).set(UA_ENGINE,this.getEngine()).set(UA_OS,this.getOS()).get()}}else{return function(){return new UAItem(itemType,userAgent,regexMap[itemType],httpUACH).parseUA().get()}}};setProps.call(this,[["getBrowser",createItemFunc(UA_BROWSER)],["getCPU",createItemFunc(UA_CPU)],["getDevice",createItemFunc(UA_DEVICE)],["getEngine",createItemFunc(UA_ENGINE)],["getOS",createItemFunc(UA_OS)],["getResult",createItemFunc(UA_RESULT)],["getUA",function(){return userAgent}],["setUA",function(ua){if(isString(ua))userAgent=ua.length>UA_MAX_LENGTH?trim(ua,UA_MAX_LENGTH):ua;return this}]]).setUA(userAgent);return this}UAParser.VERSION=LIBVERSION;UAParser.BROWSER=enumerize([NAME,VERSION,MAJOR,TYPE]);UAParser.CPU=enumerize([ARCHITECTURE]);UAParser.DEVICE=enumerize([MODEL,VENDOR,TYPE,CONSOLE,MOBILE,SMARTTV,TABLET,WEARABLE,EMBEDDED]);UAParser.ENGINE=UAParser.OS=enumerize([NAME,VERSION]);export{UAParser}; \ No newline at end of file diff --git a/dist/ua-parser.pack.js b/dist/ua-parser.pack.js index 1d94f6052..fec42a72e 100644 --- a/dist/ua-parser.pack.js +++ b/dist/ua-parser.pack.js @@ -1,4 +1,4 @@ -/* UAParser.js v0.7.33 - Copyright Š 2012-2021 Faisal Salman - MIT License */ -!function(r,l){"use strict";function i(i){for(var e={},o=0;o + AGPLv3 License */ +((i,c)=>{function I(i){for(var e={},t=0;t{var t,o={},r=e;if(!Ti(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(t in i)o[t]=r[t]&&r[t].length%2==0?r[t].concat(i[t]):i[t];return o})(Ui,e):Ui,M.call(this,[["getBrowser",(n=function(i){return i==g?function(){return new Gi(i,r,s,a).set("ua",r).set(u,this.getBrowser()).set(h,this.getCPU()).set(p,this.getDevice()).set(m,this.getEngine()).set(f,this.getOS()).get()}:function(){return new Gi(i,r,s[i],a).parseUA().get()}})(u)],["getCPU",n(h)],["getDevice",n(p)],["getEngine",n(m)],["getOS",n(f)],["getResult",n(g)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>P?Mi(i,P):i),this}]]).setUA(r),this):new V(i,e,t).getResult()}V.VERSION="2.0.4",V.BROWSER=I([v,y,G,k]),V.CPU=I([C]),V.DEVICE=I([S,x,k,W,_,e,r,t,F]),V.ENGINE=V.OS=I([v,y]),typeof exports!==n?(exports=typeof module!==n&&module.exports?module.exports=V:exports).UAParser=V:typeof define===R&&define.amd?define(function(){return V}):qi&&(i.UAParser=V);var Wi,Ni=qi&&(i.jQuery||i.Zepto);Ni&&!Ni.ua&&(Wi=new V,Ni.ua=Wi.getResult(),Ni.ua.get=function(){return Wi.getUA()},Ni.ua.set=function(i){Wi.setUA(i);var e,t=Wi.getResult();for(e in t)Ni.ua[e]=t[e]})})("object"==typeof window?window:this); \ No newline at end of file diff --git a/dist/ua-parser.pack.mjs b/dist/ua-parser.pack.mjs new file mode 100644 index 000000000..8ab1e0ee1 --- /dev/null +++ b/dist/ua-parser.pack.mjs @@ -0,0 +1,4 @@ +/* UAParser.js v2.0.4 + Copyright Š 2012-2025 Faisal Salman + AGPLv3 License */ +function I(i){for(var e={},o=0;o{var o,t={},r=e;if(!zi(e))for(var a in r={},e)for(var s in e[a])r[s]=e[a][s].concat(r[s]||[]);for(o in i)t[o]=r[o]&&r[o].length%2==0?r[o].concat(i[o]):i[o];return t})(Ii,e):Ii,A.call(this,[["getBrowser",(n=function(i){return i==f?function(){return new Li(i,r,s,a).set("ua",r).set(c,this.getBrowser()).set(h,this.getCPU()).set(u,this.getDevice()).set(p,this.getEngine()).set(m,this.getOS()).get()}:function(){return new Li(i,r,s[i],a).parseUA().get()}})(c)],["getCPU",n(h)],["getDevice",n(u)],["getEngine",n(p)],["getOS",n(m)],["getResult",n(f)],["getUA",function(){return r}],["setUA",function(i){return H(i)&&(r=i.length>P?Mi(i,P):i),this}]]).setUA(r),this):new V(i,e,o).getResult()}V.VERSION="2.0.4",V.BROWSER=I([g,x,C,v]),V.CPU=I([y]),V.DEVICE=I([S,k,v,G,_,i,r,e,N]),V.ENGINE=V.OS=I([g,x]);export{V as UAParser}; \ No newline at end of file diff --git a/license.md b/license.md deleted file mode 100644 index 01d840a46..000000000 --- a/license.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2012-2021 Faisal Salman <> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..1265e3dd9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2913 @@ +{ + "name": "ua-parser-js", + "version": "2.0.4", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ua-parser-js", + "version": "2.0.4", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "AGPL-3.0-or-later", + "dependencies": { + "detect-europe-js": "^0.1.2", + "is-standalone-pwa": "^0.1.1", + "ua-is-frozen": "^0.1.2", + "undici": "^7.12.0" + }, + "bin": { + "ua-parser-js": "script/cli.js" + }, + "devDependencies": { + "@babel/parser": "7.15.8", + "@babel/traverse": "7.23.2", + "@playwright/test": "^1.49.0", + "jshint": "~2.13.6", + "mocha": "~8.2.0", + "requirejs": "2.3.2", + "safe-regex": "^2.1.1", + "tsd": "^0.29.0", + "uglify-js": "~3.19.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", + "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", + "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template/node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@playwright/test": { + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0.tgz", + "integrity": "sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==", + "dev": true, + "dependencies": { + "playwright": "1.49.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@tsd/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@tsd/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-VtjHPAKJqLJoHHKBDNofzvQB2+ZVxjXU/Gw6INAS9aINLQYVsxfzrQ2s84huCeYWZRTtrr7R0J7XgpZHjNwBCw==", + "dev": true, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@types/eslint": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz", + "integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.1.2" + } + }, + "node_modules/cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha512-41U72MB56TfUMGndAKK8vJ78eooOD4Z5NOL4xEfjc0c23s+6EYKXlXsmACBVclLP1yOfWCgEganVzddVrSNoTg==", + "dev": true, + "dependencies": { + "exit": "0.1.2", + "glob": "^7.1.1" + }, + "engines": { + "node": ">=0.2.5" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha512-duS7VP5pvfsNLDvL1O4VOEbw37AI3A4ZUQYemvDlnpGrNu9tprR7BYWpDYwC0Xia0Zxz5ZupdiIrUp0GH1aXfg==", + "dev": true, + "dependencies": { + "date-now": "^0.1.4" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha512-AsElvov3LoNB7tf5k37H2jYSB+ZZPMT5sG2QjJCcdlV5chIv6htBUBUui2IKRjgtKAKtCBN7Zbwa+MtwLjSeNw==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-europe-js": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/detect-europe-js/-/detect-europe-js-0.1.2.tgz", + "integrity": "sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ] + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "node_modules/domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha512-q9bUwjfp7Eif8jWxxxPSykdRZAb6GkguBGSgvvCrhI9wB71W2K/Kvv4E61CF/mcCfnVJDeDWx/Vb/uAqbDj6UQ==", + "dev": true, + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha512-LbLqfXgJMmy81t+7c14mnulFHJ170cM6E+0vMXR9k/ZiZwgX8i5pNgjTCX3SO4VeUsFLV+8InixoretwU+MjBQ==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-formatter-pretty": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-formatter-pretty/-/eslint-formatter-pretty-4.1.0.tgz", + "integrity": "sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==", + "dev": true, + "dependencies": { + "@types/eslint": "^7.2.13", + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "eslint-rule-docs": "^1.1.5", + "log-symbols": "^4.0.0", + "plur": "^4.0.0", + "string-width": "^4.2.0", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-rule-docs": { + "version": "1.1.235", + "resolved": "https://registry.npmjs.org/eslint-rule-docs/-/eslint-rule-docs-1.1.235.tgz", + "integrity": "sha512-+TQ+x4JdTnDoFEXXb3fDvfGOwnyNV7duH8fXWTPD1ieaBmB8omj7Gw/pMBBu4uI2uJCCU8APDaQJzWuXnTsH4A==", + "dev": true + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha512-hBxEg3CYXe+rPIua8ETe7tmG3XDn9B0edOE/e9wH2nLczxzgdu0m0aNHY+5wFZiviLWLdANPJTssa92dMcXQ5Q==", + "dev": true, + "dependencies": { + "domelementtype": "1", + "domhandler": "2.3", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/irregular-plurals": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", + "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-standalone-pwa": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-standalone-pwa/-/is-standalone-pwa-0.1.1.tgz", + "integrity": "sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ] + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jshint": { + "version": "2.13.6", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.13.6.tgz", + "integrity": "sha512-IVdB4G0NTTeQZrBoM8C5JFVLjV2KtZ9APgybDA1MK73xb09qFs0jCXyQLnCOp1cSZZZbvhq/6mfXHUTaDkffuQ==", + "dev": true, + "dependencies": { + "cli": "~1.0.0", + "console-browserify": "1.1.x", + "exit": "0.1.x", + "htmlparser2": "3.8.x", + "lodash": "~4.17.21", + "minimatch": "~3.0.2", + "strip-json-comments": "1.0.x" + }, + "bin": { + "jshint": "bin/jshint" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", + "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mocha": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", + "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.4.3", + "debug": "4.2.0", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.2", + "nanoid": "3.1.12", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "7.2.0", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.0.2", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/mocha/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/mocha/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", + "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/mocha/node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/mocha/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "node_modules/mocha/node_modules/yargs/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", + "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || >=13.7" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0.tgz", + "integrity": "sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==", + "dev": true, + "dependencies": { + "playwright-core": "1.49.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.49.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0.tgz", + "integrity": "sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/plur": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-4.0.0.tgz", + "integrity": "sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==", + "dev": true, + "dependencies": { + "irregular-plurals": "^3.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/requirejs": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.2.tgz", + "integrity": "sha512-rGSBJSIWfUEkMTztpZlyGIpRIwuMau6gZYxQhxH1jCggR3ddWsI8+/aWh9yUscMB1O0pEgZrJiXy2hJerzMszA==", + "dev": true, + "bin": { + "r_js": "bin/r.js", + "r.js": "bin/r.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "dev": true, + "dependencies": { + "regexp-tree": "~0.1.1" + } + }, + "node_modules/serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha512-AOPG8EBc5wAikaG1/7uFCNFJwnKOuQwFTpYBdTW6OvWHeZBQBrAA/amefHGrEiOnCPcLFZK6FUPtWVKpQVIRgg==", + "dev": true, + "bin": { + "strip-json-comments": "cli.js" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tsd": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/tsd/-/tsd-0.29.0.tgz", + "integrity": "sha512-5B7jbTj+XLMg6rb9sXRBGwzv7h8KJlGOkTHxY63eWpZJiQ5vJbXEjL0u7JkIxwi5EsrRE1kRVUWmy6buK/ii8A==", + "dev": true, + "dependencies": { + "@tsd/typescript": "~5.2.2", + "eslint-formatter-pretty": "^4.1.0", + "globby": "^11.0.1", + "jest-diff": "^29.0.3", + "meow": "^9.0.0", + "path-exists": "^4.0.0", + "read-pkg-up": "^7.0.0" + }, + "bin": { + "tsd": "dist/cli.js" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ua-is-frozen": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ua-is-frozen/-/ua-is-frozen-0.1.2.tgz", + "integrity": "sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ] + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz", + "integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/workerpool": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", + "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.js b/package.js deleted file mode 100644 index a3bcdb348..000000000 --- a/package.js +++ /dev/null @@ -1,12 +0,0 @@ -Package.describe({ - name: 'faisalman:ua-parser-js', - version: '0.7.33', - summary: 'Lightweight JavaScript-based user-agent string parser', - git: 'https://github.com/faisalman/ua-parser-js.git', - documentation: 'readme.md' -}); - -Package.onUse(function (api) { - api.addFiles("src/ua-parser.js"); - api.export("UAParser"); -}); diff --git a/package.json b/package.json old mode 100644 new mode 100755 index 096a43fa0..0f21208a1 --- a/package.json +++ b/package.json @@ -1,11 +1,12 @@ { "title": "UAParser.js", "name": "@amplitude/ua-parser-js", - "version": "0.7.33", + "version": "2.0.4", "author": "Faisal Salman (http://faisalman.com)", - "description": "Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent 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": [ "user-agent", + "client-hints", "parser", "browser", "engine", @@ -13,20 +14,28 @@ "device", "cpu", "jquery-plugin", - "ecosystem:jquery" + "ecosystem:jquery", + "ua-parser-js", + "browser-detection", + "device-detection", + "os-detection", + "bot-detection" ], "homepage": "http://github.com/amplitude/ua-parser-js", "contributors": [ "Aamir Poonawalla ", "Admas ", + "Aiyush ", "algenon ", "Alvin Portillo ", "Amumu ", + "Andreas Kogler ", "Andrea Vaghi ", "Anton Zhiyanov ", "Arturo Mejia ", "Arun Rama Reddy ", "Austin Pray ", + "Beat YT <66485277+Beat-YT@users.noreply.github.com>", "Bendeguz ", "Benjamin Bertrand ", "Benjamin Urban ", @@ -34,8 +43,13 @@ "Carl C Von Lewin ", "CESAR RAMOS ", "Chad Killingsworth ", + "chenhui9279 ", + "chenyuan-new <53860479+chenyuan-new@users.noreply.github.com>", "Christopher De Cairos ", "Cyrille David ", + "Dai Jie ", + "Danila Rodichkin ", + "Dante ", "Dario Vladovic ", "David Annez ", "Davit Barbakadze ", @@ -58,17 +72,24 @@ "Faisal Salman ", "FrÊdÊric Camblor ", "Frederik Ring ", + "Garrit Franke ", "Gerald Host ", "GermÃĄn M. Bravo ", "Grigory Dmitrenko ", "gulpin ", + "Hans Ott ", + "Harald Reingruber <74898239+haraldreingruber-dedalus@users.noreply.github.com>", "Hendrik Helwich ", "Hermann Ebert ", "hr6r ", + "Hyewon Kang ", + "Hyunbin <47051820+hyunbinseo@users.noreply.github.com>", "Igor Topal ", "Ildar Kamalov ", + "Ilya Daraseliya ", "insanehong ", "jackpoll ", + "Jacky Choo ", "Jake Mc ", "JBYoshi <12983479+JBYoshi@users.noreply.github.com>", "Joey Parrish ", @@ -78,11 +99,15 @@ "Josh Goldberg ", "Junki-Ishida ", "Kendall Buchanan ", + "KnifeLemon ", + "kNoAPP ", "Lee Treveil ", "leonardo ", "Levente Balogh ", "Liam Quinn ", "Lithin ", + "liujunlve ", + "lj0812 ", "ll-syber <670159357@qq.com>", "Loris Guignard ", "Lukas Drgon ", @@ -96,7 +121,10 @@ "Max Maurer ", "Max Nordlund ", "Michael Hess ", + "Mike ", "MimyyK ", + "Mok ", + "nabetama ", "naoh ", "Nicholas Ionata ", "Nikhil Motiani ", @@ -105,7 +133,10 @@ "niris ", "Nobuo Okada ", "o.drapeza ", + "Oscar Becerra ", "otakuSiD ", + "Pablo OsÊs ", + "Paris Morgan ", "patrick-nurt ", "Pavel Studeny ", "Peter Dave Hello ", @@ -115,19 +146,25 @@ "Queen Vinyl Darkscratch ", "Raine Makelainen ", "Raman Savaryn ", + "Riley Shaw ", "Robert Tod ", "roman.savarin ", "Ron Korland ", "Ross Noble ", "ruicong <466403866@qq.com>", + "Runar Heggset ", + "Ryohei Shima ", + "Ryan Haines ", "Sandro Sonntag ", "sgautrea ", + "shaharmor ", "Shane Gautreau ", "Shane Thacker ", "Shreedhar ", "Simon Eisenmann ", "Simon Lang ", "Stiekel ", + "sunny-mwx <30586210+sunny-mwx@users.noreply.github.com>", "sUP ", "Sylvain Gizard ", "szchenghuang ", @@ -135,60 +172,92 @@ "Tony Tomarchio ", "Ulrich Schmidt ", "Vadim Kurachevsky ", + "Varun Sharma ", + "XhmikosR ", "YÄąlmaz ", "yuanyang ", "Yun Young-jin ", - "Zach Bjornson " + "Zach Bjornson ", + "Ziding Zhang " ], - "main": "src/ua-parser.js", + "type": "commonjs", + "types": "src/main/ua-parser.d.ts", + "main": "src/main/ua-parser.js", + "module": "src/main/ua-parser.mjs", + "browser": "dist/ua-parser.pack.js", + "exports": { + ".": { + "require": "./src/main/ua-parser.js", + "import": "./src/main/ua-parser.mjs", + "types": "./src/main/ua-parser.d.ts" + }, + "./enums": { + "require": "./src/enums/ua-parser-enums.js", + "import": "./src/enums/ua-parser-enums.mjs", + "types": "./src/enums/ua-parser-enums.d.ts" + }, + "./extensions": { + "require": "./src/extensions/ua-parser-extensions.js", + "import": "./src/extensions/ua-parser-extensions.mjs", + "types": "./src/extensions/ua-parser-extensions.d.ts" + }, + "./helpers": { + "require": "./src/helpers/ua-parser-helpers.js", + "import": "./src/helpers/ua-parser-helpers.mjs", + "types": "./src/helpers/ua-parser-helpers.d.ts" + } + }, "files": [ "dist", "src" ], + "bin": "./script/cli.js", "scripts": { - "build": "uglifyjs src/ua-parser.js -o dist/ua-parser.min.js --comments '/^ UA/' && uglifyjs src/ua-parser.js -o dist/ua-parser.pack.js --comments '/^ UA/' --compress --mangle", - "test": "jshint src/ua-parser.js && mocha -R nyan test/test.js", - "test-ci": "jshint src/ua-parser.js && mocha -R spec test/test.js", - "verup": "node ./node_modules/verup", - "version": "node ./node_modules/verup 0" + "build": "./script/build-dist.sh && ./script/build-esm.js", + "build+test": "npm run build && npm run test", + "fuzz": "jazzer ./test/fuzz/redos.js --sync", + "test": "./script/test-all.sh", + "test:dts-lint": "tsd --typings src/main/ua-parser.d.ts --files test/static/dts-lint.ts", + "test:eslint": "eslint src && eslint script", + "test:jshint": "jshint src/main", + "test:lockfile-lint": "npx lockfile-lint -p package-lock.json", + "test:mocha": "mocha test/unit", + "test:playwright": "npx playwright install && playwright test test/e2e --browser all" }, - "verup": { - "files": [ - "bower.json", - "package.js", - "src/ua-parser.js" - ], - "regs": [ - "^((?:\\$|(\\s*\\*\\s*@)|(\\s*(?:var|,)?\\s+))(?:LIBVERSION|version)[\\s\\:='\"]+)([0-9]+(?:\\.[0-9]+){2,2})", - "^(\\/?\\s?\\*.*v)([0-9]+(?:\\.[0-9]+){2,2})" - ] + "dependencies": { + "detect-europe-js": "^0.1.2", + "is-standalone-pwa": "^0.1.1", + "ua-is-frozen": "^0.1.2", + "undici": "^7.12.0" }, "devDependencies": { "@babel/parser": "7.15.8", - "@babel/traverse": "7.15.4", - "jshint": "~2.12.0", + "@babel/traverse": "7.23.2", + "@playwright/test": "^1.49.0", + "jshint": "~2.13.6", "mocha": "~8.2.0", - "requirejs": "^2.3.2", + "requirejs": "2.3.2", "safe-regex": "^2.1.1", - "uglify-js": "~3.12.0", - "verup": "^1.3.x" + "tsd": "^0.29.0", + "uglify-js": "~3.19.3" }, "repository": { "type": "git", "url": "https://github.com/amplitude/ua-parser-js.git" }, - "license": "MIT", + "license": "AGPL-3.0-or-later", "engines": { "node": "*" }, "directories": { "dist": "dist", + "script": "script", "src": "src", "test": "test" }, "bugs": "https://github.com/amplitude/ua-parser-js/issues", - "demo": "https://faisalman.github.io/ua-parser-js", - "download": "https://raw.github.com/faisalman/ua-parser-js/master/dist/ua-parser.min.js", + "demo": "https://uaparser.dev", + "download": "https://raw.github.com/faisalman/ua-parser-js/master/dist/ua-parser.pack.js", "funding": [ { "type": "opencollective", @@ -197,6 +266,10 @@ { "type": "paypal", "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" } ] } diff --git a/readme.md b/readme.md deleted file mode 100644 index 71bb1879d..000000000 --- a/readme.md +++ /dev/null @@ -1,359 +0,0 @@ -

- -

- -

- - - - - -

- -# UAParser.js - -JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data with relatively small footprint (~17KB minified, ~6KB gzipped) that can be used either in browser (client-side) or node.js (server-side). - -* Author : Faisal Salman <> -* Demo : https://faisalman.github.io/ua-parser-js -* Source : https://github.com/faisalman/ua-parser-js - -# Documentation -### UAParser([user-agent][,extensions]) -typeof `user-agent` "string". - -typeof `extensions` "array". - -In The Browser environment you dont need to pass the user-agent string to the function, you can just call the funtion and it should automatically get the string from the `window.navigator.userAgent`, but that is not the case in nodejs. The user-agent string must be passed in nodejs for the function to work. -Usually you can find the user agent in: -`request.headers["user-agent"]`. - - -## Constructor -When you call `UAParser` with the `new` keyword `UAParser` will return a new instance with an empty result object, you have to call one of the available methods to get the information from the user-agent string. -Like so: -* `new UAParser([uastring][,extensions])` -```js -let parser = new UAParser("user-agent"); // you need to pass the user-agent for nodejs -console.log(parser); // {} -let parserResults = parser.getResult(); -console.log(parserResults); -/** { - "ua": "", - "browser": {}, - "engine": {}, - "os": {}, - "device": {}, - "cpu": {} -} */ -``` - -When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results. -* `UAParser([uastring][,extensions])` - * returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` - -## Methods - -#### Methods table -The methods are self explanatory, here's a small overview on all the available methods: -* `getResult()` - returns all function object calls, user-agent string, browser info, cpu, device, engine, os: -`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`. - - * `getBrowser()` - returns the browser name and version. - * `getDevice()` - returns the device model, type, vendor. - * `getEngine()` - returns the current browser engine name and version. - * `getOS()` - returns the running operating system name and version. - * `getCPU()` - returns CPU architectural design name. - * `getUA()` - returns the user-agent string. - * `setUA(user-agent)` - set a custom user-agent to be parsed. - - ---- - -* `getResult()` - * returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }` - -* `getBrowser()` - * returns `{ name: '', version: '' }` - -```sh -# Possible 'browser.name': -2345Explorer, 360 Browser, Amaya, Android Browser, Arora, Avant, Avast, AVG, -BIDUBrowser, Baidu, Basilisk, Blazer, Bolt, Brave, Bowser, Camino, Chimera, -Chrome Headless, Chrome WebView, Chrome, Chromium, Comodo Dragon, Dillo, -Dolphin, Doris, DuckDuckGo, Edge, Electron, Epiphany, Facebook, Falkon, Fennec, -Firebird, Firefox [Focus/Reality], Flock, Flow, GSA, GoBrowser, Huawei Browser, -ICE Browser, IE, IEMobile, IceApe, IceCat, IceDragon, Iceweasel, Instagram, -Iridium, Iron, Jasmine, K-Meleon, Kindle, Klar, Konqueror, LBBROWSER, Line, -LinkedIn, Links, Lunascape, Lynx, MIUI Browser, Maemo Browser, Maemo, Maxthon, -MetaSr Midori, Minimo, Mobile Safari, Mosaic, Mozilla, NetFront, NetSurf, Netfront, -Netscape, NokiaBrowser, Obigo, Oculus Browser, OmniWeb, Opera Coast, -Opera [Mini/Mobi/Tablet], PaleMoon, PhantomJS, Phoenix, Polaris, Puffin, QQ, -QQBrowser, QQBrowserLite, Quark, QupZilla, RockMelt, Safari, Sailfish Browser, -Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim, SlimBrowser, Swiftfox, -Tesla, Tizen Browser, UCBrowser, UP.Browser, Vivaldi, Waterfox, WeChat, Weibo, -Yandex, baidu, iCab, w3m, Whale Browser... - -# 'browser.version' determined dynamically -``` - -* `getDevice()` - * returns `{ model: '', type: '', vendor: '' }` - -```sh -# Possible 'device.type': -console, mobile, tablet, smarttv, wearable, embedded - -########## -# NOTE: 'desktop' is not a possible device type. -# UAParser only reports info directly available from the UA string, which is not the case for 'desktop' device type. -# If you wish to detect desktop devices, you must handle the needed logic yourself. -# You can read more about it in this issue: https://github.com/faisalman/ua-parser-js/issues/182 -########## - -# Possible 'device.vendor': -Acer, Alcatel, Amazon, Apple, Archos, ASUS, AT&T, BenQ, BlackBerry, Dell, -Essential, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla, Lenovo, LG, -Meizu, Microsoft, Motorola, Nexian, Nintendo, Nokia, Nvidia, OnePlus, OPPO, Ouya, -Palm, Panasonic, Pebble, Polytron, Realme, RIM, Roku, Samsung, Sharp, Siemens, -Sony[Ericsson], Sprint, Tesla, Vivo, Vodafone, Xbox, Xiaomi, Zebra, ZTE, ... - -# 'device.model' determined dynamically -``` - -* `getEngine()` - * returns `{ name: '', version: '' }` - -```sh -# Possible 'engine.name' -Amaya, Blink, EdgeHTML, Flow, Gecko, Goanna, iCab, KHTML, Links, Lynx, NetFront, -NetSurf, Presto, Tasman, Trident, w3m, WebKit - -# 'engine.version' determined dynamically -``` - -* `getOS()` - * returns `{ name: '', version: '' }` - -```sh -# Possible 'os.name' -AIX, Amiga OS, Android[-x86], Arch, Bada, BeOS, BlackBerry, CentOS, Chromium OS, -Contiki, Fedora, Firefox OS, FreeBSD, Debian, Deepin, DragonFly, elementary OS, -Fuchsia, Gentoo, GhostBSD, GNU, Haiku, HarmonyOS, HP-UX, Hurd, iOS, Joli, KaiOS, -Linpus, Linspire,Linux, Mac OS, Maemo, Mageia, Mandriva, Manjaro, MeeGo, Minix, -Mint, Morph OS, NetBSD, Nintendo, OpenBSD, OpenVMS, OS/2, Palm, PC-BSD, PCLinuxOS, -Plan9, PlayStation, QNX, Raspbian, RedHat, RIM Tablet OS, RISC OS, Sabayon, -Sailfish, Series40, Slackware, Solaris, SUSE, Symbian, Tizen, Ubuntu, Unix, -VectorLinux, WebOS, Windows [Phone/Mobile], Zenwalk, ... - -# 'os.version' determined dynamically -``` - -* `getCPU()` - * returns `{ architecture: '' }` - -```sh -# Possible 'cpu.architecture' -68k, amd64, arm[64/hf], avr, ia[32/64], irix[64], mips[64], pa-risc, ppc, sparc[64] -``` - -* `getUA()` - * returns UA string of current instance - -* `setUA(uastring)` - * set UA string to be parsed - * returns current instance - -# Usage - -## Using HTML - -```html - - - - - - - - - -``` - -## Using node.js - -Note: Device information is not available in the NodeJS environment. - -```sh -$ npm install ua-parser-js -``` - -```js -var http = require('http'); -var parser = require('ua-parser-js'); - -http.createServer(function (req, res) { - // get user-agent header - var ua = parser(req.headers['user-agent']); - // write the result as response - res.end(JSON.stringify(ua, null, ' ')); -}) -.listen(1337, '127.0.0.1'); - -console.log('Server running at http://127.0.0.1:1337/'); -``` - -## Using TypeScript - -```sh -$ npm install --save @types/ua-parser-js -# Download TS type definition from DefinitelyTyped repository: -# https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/ua-parser-js -``` - -## Using jQuery/Zepto ($.ua) - -Although written in vanilla js, this library will automatically detect if jQuery/Zepto is present and create `$.ua` object (with values based on its User-Agent) along with `window.UAParser` constructor. To get/set user-agent you can use: `$.ua.get()` / `$.ua.set(uastring)`. - -```js -// Say we are in a browser with default user-agent: 'Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Sprint APA7373KT Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0': - -// Get the details -console.log($.ua.device); // {vendor: "HTC", model: "Evo Shift 4G", type: "mobile"} -console.log($.ua.os); // {name: "Android", version: "2.3.4"} -console.log($.ua.os.name); // "Android" -console.log($.ua.get()); // "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Sprint APA7373KT Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0" - -// Now lets try to reset to another custom user-agent -$.ua.set('Mozilla/5.0 (Linux; U; Android 3.0.1; en-us; Xoom Build/HWI69) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13'); - -// Test again -console.log($.ua.browser.name); // "Safari" -console.log($.ua.engine.name); // "Webkit" -console.log($.ua.device); // {vendor: "Motorola", model: "Xoom", type: "tablet"} -console.log(parseInt($.ua.browser.version.split('.')[0], 10)); // 4 - -// Add class to tag -// -$('body').addClass('ua-browser-' + $.ua.browser.name + ' ua-devicetype-' + $.ua.device.type); -``` - -## Using Extension - -* `UAParser([uastring,] extensions)` - -```js -// Example: -var myOwnListOfBrowsers = [ - [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION] -]; -var myParser = new UAParser({ browser: myOwnListOfBrowsers }); -var myUA = 'Mozilla/5.0 MyBrowser/1.3'; -console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3"} -``` - -# Development - -## Sponsors - - - - - -## Contributors - - - - - -Made with [contributors-img](https://contrib.rocks). - -## How To Contribute - -* Fork and clone this repository -* Make some changes as required -* Write unit test to showcase its functionality -* Run the test suites to make sure it's not breaking anything `$ npm test` -* Submit a pull request under `develop` branch - -# License - -MIT License - -Copyright (c) 2012-2021 Faisal Salman <> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/script/build-dist.sh b/script/build-dist.sh new file mode 100755 index 000000000..0648a12df --- /dev/null +++ b/script/build-dist.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +SRC_PATH="src/main/ua-parser.js" +MIN_PATH="dist/ua-parser.min.js" +PACK_PATH="dist/ua-parser.pack.js" + +SRC_PATH_MJS="src/main/ua-parser.mjs" +MIN_PATH_MJS="dist/ua-parser.min.mjs" +PACK_PATH_MJS="dist/ua-parser.pack.mjs" + +# minified +echo "Generate ${MIN_PATH}" +uglifyjs $SRC_PATH -o $MIN_PATH --comments "/^ UA/" + +echo "Generate ${MIN_PATH_MJS}" +uglifyjs $SRC_PATH_MJS -o $MIN_PATH_MJS --comments "/^ UA/" --module + +# packed +echo "Generate ${PACK_PATH}" +uglifyjs $SRC_PATH -o $PACK_PATH --comments "/^ UA/" --compress --mangle + +echo "Generate ${PACK_PATH_MJS}" +uglifyjs $SRC_PATH_MJS -o $PACK_PATH_MJS --comments "/^ UA/" --compress --mangle --module \ No newline at end of file diff --git a/script/build-esm.js b/script/build-esm.js new file mode 100755 index 000000000..98f7ffb8b --- /dev/null +++ b/script/build-esm.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* jshint esversion: 6 */ +const fs = require('fs'); + +const generateMJS = (module) => { + let { src, dest, title, replacements } = module; + let text = fs.readFileSync(src, 'utf-8'); + + replacements.push( + [/const (.+?)\s*=\s*require\(\'\.(.+)\'\)/ig, 'import $1 from \'\.$2.mjs\''], + [/const (.+?)\s*=\s*require\(\'(.+)\'\)/ig, 'import $1 from \'$2\''], + [/module\.exports =/ig, 'export'] + ); + replacements.forEach(rep => { + text = text.replace(rep[0], rep[1]); + }); + + console.log(`Generate ${dest}`); + + fs.writeFileSync(dest, +`// Generated ESM version of ${title} +// DO NOT EDIT THIS FILE! +// Source: /${src} + +${text}`, 'utf-8'); + +}; + +const modules = [ + { + src : 'src/main/ua-parser.js', + dest : 'src/main/ua-parser.mjs', + title : 'ua-parser-js', + replacements : [ + [/\(func[\s\S]+strict\';/ig, ''], + [/esversion\: 3/ig, 'esversion: 6'], + [/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};'] + ] + },{ + src : 'src/enums/ua-parser-enums.js', + dest :'src/enums/ua-parser-enums.mjs', + title : 'ua-parser-js/enums', + replacements : [] + }, + { + src : 'src/extensions/ua-parser-extensions.js', + dest : 'src/extensions/ua-parser-extensions.mjs', + title : 'ua-parser-js/extensions', + replacements : [] + }, + { + src : 'src/helpers/ua-parser-helpers.js', + dest : 'src/helpers/ua-parser-helpers.mjs', + title : 'ua-parser-js/helpers', + replacements : [] + } +]; + +modules.forEach(module => generateMJS(module)); \ No newline at end of file diff --git a/script/cli.js b/script/cli.js new file mode 100755 index 000000000..785a10426 --- /dev/null +++ b/script/cli.js @@ -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)); \ No newline at end of file diff --git a/script/test-all.sh b/script/test-all.sh new file mode 100755 index 000000000..f00fea2ed --- /dev/null +++ b/script/test-all.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +echo ' +- run build +' +npm run build || exit 1 + +echo ' +- lint js code +' +npm run test:jshint || exit 1 +#npm run test:eslint || exit 1 + +echo ' +- test using mocha +' +npm run test:mocha || exit 1 + +echo ' +- test using playwright +' +npm run test:playwright || exit 1 + +echo ' +- lint lockfile +' +npm run test:lockfile-lint || exit 1 + +echo ' +- lint d.ts files +' +npm run test:dts-lint || exit 1 \ No newline at end of file diff --git a/src/enums/ua-parser-enums.d.ts b/src/enums/ua-parser-enums.d.ts new file mode 100644 index 000000000..aa40b50f6 --- /dev/null +++ b/src/enums/ua-parser-enums.d.ts @@ -0,0 +1,387 @@ +// Type definitions for Enums submodule of UAParser.js v2.0.4 +// Project: https://github.com/faisalman/ua-parser-js +// Definitions by: Faisal Salman + +export const Browser: Readonly<{ + '115': "115"; + '2345': "2345"; + '360': "360"; + ALIPAY: "Alipay"; + AMAYA: "Amaya"; + ANDROID: "Android Browser"; + ARORA: "Arora"; + AVANT: "Avant"; + AVAST: "Avast Secure Browser"; + AVG: "AVG Secure Browser"; + BAIDU: "Baidu Browser"; + BASILISK: "Basilisk"; + BLAZER: "Blazer"; + BOLT: "Bolt"; + BOWSER: "Bowser"; + BRAVE: "Brave"; + CAMINO: "Camino"; + CHIMERA: "Chimera"; + CHROME: "Chrome"; + CHROME_HEADLESS: "Chrome Headless"; + CHROME_MOBILE: "Mobile Chrome"; + CHROME_WEBVIEW: "Chrome WebView"; + CHROMIUM: "Chromium"; + COBALT: "Cobalt"; + COC_COC: "Coc Coc"; + CONKEROR: "Conkeror"; + DAUM: "Daum"; + DILLO: "Dillo"; + DOLPHIN: "Dolphin"; + DOOBLE: 'Dooble', + DORIS: "Doris"; + DRAGON: "Dragon"; + DUCKDUCKGO: "DuckDuckGo"; + ECOSIA: "Ecosia"; + EDGE: "Edge"; + EDGE_WEBVIEW: "Edge WebView"; + EDGE_WEBVIEW2: "Edge WebView2"; + EPIPHANY: "Epiphany"; + FACEBOOK: "Facebook"; + FALKON: "Falkon"; + FIREBIRD: "Firebird"; + FIREFOX: "Firefox"; + FIREFOX_FOCUS: "Firefox Focus"; + FIREFOX_MOBILE: "Mobile Firefox"; + FIREFOX_REALITY: "Firefox Reality"; + FENNEC: "Fennec"; + FLOCK: "Flock"; + FLOW: "Flow"; + GO: "GoBrowser"; + GOOGLE_SEARCH: "GSA"; + HELIO: "Helio"; + HEYTAP: "HeyTap"; + HONOR: "Honor"; + HUAWEI: "Huawei Browser"; + ICAB: "iCab"; + ICE: "ICE Browser"; + ICEAPE: "IceApe"; + ICECAT: "IceCat"; + ICEDRAGON: "IceDragon"; + ICEWEASEL: "IceWeasel"; + IE: "IE"; + INSTAGRAM: "Instagram"; + IRIDIUM: "Iridium"; + IRON: "Iron"; + JASMINE: "Jasmine"; + KONQUEROR: "Konqueror"; + KAKAO: "KakaoTalk"; + KHTML: "KHTML"; + K_MELEON: "K-Meleon"; + KLAR: "Klar"; + KLARNA: "Klarna"; + KINDLE: "Kindle"; + LENOVO: "Smart Lenovo Browser"; + LADYBIRD: "Ladybird"; + LG: "LG Browser"; + LIBREWOLF: "LibreWolf"; + LIEBAO: "LBBROWSER"; + LINE: "Line"; + LINKEDIN: "LinkedIn"; + LINKS: "Links"; + LUNASCAPE: "Lunascape"; + LYNX: "Lynx"; + MAEMO: "Maemo Browser"; + MAXTHON: "Maxthon"; + MIDORI: "Midori"; + MINIMO: "Minimo"; + MIUI: "MIUI Browser"; + MOZILLA: "Mozilla"; + MOSAIC: "Mosaic"; + NAVER: "Naver"; + NETFRONT: "NetFront"; + NETSCAPE: "Netscape"; + NETSURF: "Netsurf"; + NOKIA: "Nokia Browser"; + OBIGO: "Obigo"; + OCULUS: "Oculus Browser"; + OMNIWEB: "OmniWeb"; + OPERA: "Opera"; + OPERA_COAST: "Opera Coast"; + OPERA_GX: "Opera GX", + OPERA_MINI: "Opera Mini"; + OPERA_MOBI: "Opera Mobi"; + OPERA_TABLET: "Opera Tablet"; + OPERA_TOUCH: "Opera Touch"; + OTTER: "Otter"; + OVI: "OviBrowser"; + PALEMOON: "PaleMoon"; + PHANTOMJS: "PhantomJS"; + PHOENIX: "Phoenix"; + PICOBROWSER: "Pico Browser"; + POLARIS: "Polaris"; + PUFFIN: "Puffin"; + QQ: "QQBrowser"; + QQ_LITE: "QQBrowserLite"; + QUARK: "Quark"; + QUPZILLA: "QupZilla"; + QUTEBROWSER: "qutebrowser"; + REKONQ: "rekonq"; + ROCKMELT: "Rockmelt"; + SAFARI: "Safari"; + SAFARI_MOBILE: "Mobile Safari"; + SAILFISH: "Sailfish Browser"; + SAMSUNG: "Samsung Internet"; + SEAMONKEY: "SeaMonkey"; + SILK: "Silk"; + SKYFIRE: "Skyfire"; + SLEIPNIR: "Sleipnir"; + SLIMBOAT: "SlimBoat"; + SLIMBROWSER: "SlimBrowser"; + SLIMJET: "Slimjet"; + SNAPCHAT: "Snapchat"; + SOGOU_EXPLORER: "Sogou Explorer"; + SOGOU_MOBILE: "Sogou Mobile"; + SURF: "Surf"; + SWIFTFOX: "Swiftfox"; + TESLA: "Tesla"; + TIKTOK: "TikTok"; + TIZEN: "Tizen Browser"; + TWITTER: "Twitter"; + UC: "UCBrowser"; + UP: "UP.Browser"; + VIVALDI: "Vivaldi"; + VIVO: "Vivo Browser"; + W3M: "w3m"; + WATERFOX: "Waterfox"; + WEBKIT: "WebKit"; + WECHAT: "WeChat"; + WEIBO: "Weibo"; + WHALE: "Whale"; + WOLVIC: "Wolvic"; + YANDEX: "Yandex"; + ZALO: "Zalo"; +}>; +export const BrowserType: Readonly<{ + CRAWLER: "crawler"; + CLI: "cli"; + EMAIL: "email"; + FETCHER: "fetcher"; + INAPP: "inapp"; + MEDIAPLAYER: "mediaplayer"; + LIBRARY: "library"; +}>; +export const CPU: Readonly<{ + '68K': "68k"; + ALPHA: "alpha"; + ARM: "arm"; + ARM_64: "arm64"; + ARM_HF: "armhf"; + AVR: "avr"; + AVR_32: "avr32"; + IA64: "ia64"; + IRIX: "irix"; + IRIX_64: "irix64"; + MIPS: "mips"; + MIPS_64: "mips64"; + PA_RISC: "pa-risc"; + PPC: "ppc"; + SPARC: "sparc"; + SPARC_64: "sparc64"; + X86: "ia32"; + X86_64: "amd64"; +}>; +export const Device: Readonly<{ + CONSOLE: "console"; + DESKTOP: "desktop"; + EMBEDDED: "embedded"; + MOBILE: "mobile"; + SMARTTV: "smarttv"; + TABLET: "tablet"; + WEARABLE: "wearable"; + XR: "xr"; +}>; +export const Vendor: Readonly<{ + ACER: "Acer"; + ADVAN: "Advan"; + ALCATEL: "Alcatel"; + APPLE: "Apple"; + AMAZON: "Amazon"; + ARCHOS: "Archos"; + ASUS: "ASUS"; + ATT: "AT&T"; + BENQ: "BenQ"; + BLACKBERRY: "BlackBerry"; + BLU: "BLU"; + CAT: "Cat"; + DELL: "Dell"; + ENERGIZER: "Energizer"; + ESSENTIAL: "Essential"; + FACEBOOK: "Facebook"; + FAIRPHONE: "Fairphone"; + GEEKSPHONE: "GeeksPhone"; + GENERIC: "Generic"; + GOOGLE: "Google"; + HMD: "HMD"; + HP: "HP"; + HTC: "HTC"; + HUAWEI: "Huawei"; + IMO: "IMO"; + INFINIX: "Infinix"; + ITEL: "itel"; + JOLLA: "Jolla"; + KOBO: "Kobo"; + LAVA: "Lava"; + LENOVO: "Lenovo"; + LG: "LG"; + MEIZU: "Meizu"; + MICROMAX: "Micromax"; + MICROSOFT: "Microsoft"; + MOTOROLA: "Motorola"; + NEXIAN: "Nexian"; + NINTENDO: "Nintendo"; + NOKIA: "Nokia"; + NOTHING: "Nothing"; + NVIDIA: "Nvidia"; + ONEPLUS: "OnePlus"; + OPPO: "OPPO"; + OUYA: "Ouya"; + PALM: "Palm"; + PANASONIC: "Panasonic"; + PEBBLE: "Pebble"; + PHILIPS: "Philips"; + PICO: "Pico"; + POLYTRON: "Polytron"; + REALME: "Realme"; + RETROID: "Retroid"; + RIM: "RIM"; + ROKU: "Roku"; + SAMSUNG: "Samsung"; + SHARP: "Sharp"; + SIEMENS: "Siemens"; + SMARTFREN: "Smartfren"; + SONY: "Sony"; + SPRINT: "Sprint"; + TCL: "TCL"; + TECHNISAT: "TechniSAT"; + TECNO: "Tecno"; + TESLA: "Tesla"; + ULEFONE: "Ulefone"; + VIVO: "Vivo"; + VIZIO: "Vizio"; + VODAFONE: "Vodafone"; + XBOX: "Xbox"; + XIAOMI: "Xiaomi"; + ZEBRA: "Zebra"; + ZTE: "ZTE"; +}>; +export const Engine: Readonly<{ + AMAYA: "Amaya"; + ARKWEB: "ArkWeb"; + BLINK: "Blink"; + EDGEHTML: "EdgeHTML"; + FLOW: "Flow"; + GECKO: "Gecko"; + GOANNA: "Goanna"; + ICAB: "iCab"; + KHTML: "KHTML"; + LIBWEB: "LibWeb"; + LINKS: "Links"; + LYNX: "Lynx"; + NETFRONT: "NetFront"; + NETSURF: "NetSurf"; + PRESTO: "Presto"; + SERVO: "Servo"; + TASMAN: "Tasman"; + TRIDENT: "Trident"; + W3M: "w3m"; + WEBKIT: "WebKit"; +}>; +export const OS: Readonly<{ + AIX: "AIX"; + AMIGA_OS: "Amiga OS"; + ANDROID: "Android"; + ANDROID_X86: "Android-x86"; + ARCAOS: "ArcaOS"; + ARCH: "Arch"; + BADA: "Bada"; + BEOS: "BeOS"; + BLACKBERRY: "BlackBerry"; + CENTOS: "CentOS"; + CHROME_OS: "Chrome OS"; + CHROMECAST: "Chromecast"; + CHROMECAST_ANDROID: "Chromecast Android"; + CHROMECAST_FUCHSIA: "Chromecast Fuchsia"; + CHROMECAST_LINUX: "Chromecast Linux"; + CHROMECAST_SMARTSPEAKER: "Chromecast SmartSpeaker"; + CONTIKI: "Contiki"; + DEBIAN: "Debian"; + DEEPIN: "Deepin"; + DRAGONFLY: "DragonFly"; + ELEMENTARY_OS: "elementary OS"; + FEDORA: "Fedora"; + FIREFOX_OS: "Firefox OS"; + FREEBSD: "FreeBSD"; + FUCHSIA: "Fuchsia"; + GENTOO: "Gentoo"; + GHOSTBSD: "GhostBSD"; + GNU: "GNU"; + HAIKU: "Haiku"; + HARMONYOS: "HarmonyOS"; + HP_UX: "HP-UX"; + HURD: "Hurd"; + IOS: "iOS"; + JOLI: "Joli"; + KAIOS: "KaiOS"; + KNOPPIX: "Knoppix"; + KUBUNTU: "Kubuntu"; + LINPUS: "Linpus"; + LINSPIRE: "Linspire"; + LINUX: "Linux"; + MACOS: "macOS"; + MAEMO: "Maemo"; + MAGEIA: "Mageia"; + MANDRIVA: "Mandriva"; + MANJARO: "Manjaro"; + MEEGO: "MeeGo"; + MINIX: "Minix"; + MINT: "Mint"; + MORPH_OS: "Morph OS"; + NETBSD: "NetBSD"; + NETRANGE: "NetRange"; + NETTV: "NetTV"; + NINTENDO: "Nintendo"; + OPENHARMONY: "OpenHarmony"; + OPENBSD: "OpenBSD"; + OPENVMS: "OpenVMS"; + OS2: "OS/2"; + PALM: "Palm"; + PC_BSD: "PC-BSD"; + PCLINUXOS: "PCLinuxOS"; + PICO: "Pico"; + PLAN9: "Plan9"; + PLAYSTATION: "PlayStation"; + QNX: "QNX"; + RASPBIAN: "Raspbian"; + REDHAT: "RedHat"; + RIM_TABLET_OS: "RIM Tablet OS"; + RISC_OS: "RISC OS"; + SABAYON: "Sabayon"; + SAILFISH: "Sailfish"; + SERENITYOS: "SerenityOS"; + SERIES40: "Series40"; + SLACKWARE: "Slackware"; + SOLARIS: "Solaris"; + SUSE: "SUSE"; + SYMBIAN: "Symbian"; + TIZEN: "Tizen"; + UBUNTU: "Ubuntu"; + UBUNTU_TOUCH: "Ubuntu Touch", + UNIX: "Unix"; + VECTORLINUX: "VectorLinux"; + WATCHOS: "watchOS"; + WEBOS: "WebOS"; + WINDOWS: "Windows"; + WINDOWS_CE: "Windows CE"; + WINDOWS_IOT: "Windows IoT"; + WINDOWS_MOBILE: "Windows Mobile"; + WINDOWS_PHONE: "Windows Phone"; + WINDOWS_RT: "Windows RT"; + XBOX: "Xbox"; + XUBUNTU: "Xubuntu"; + ZENWALK: "Zenwalk"; +}>; diff --git a/src/enums/ua-parser-enums.js b/src/enums/ua-parser-enums.js new file mode 100644 index 000000000..9aa28e6c3 --- /dev/null +++ b/src/enums/ua-parser-enums.js @@ -0,0 +1,414 @@ +/////////////////////////////////////////////// +/* Enums for UAParser.js v2.0.4 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + AGPLv3 License */ +////////////////////////////////////////////// + +/*jshint esversion: 6 */ + +const Browser = Object.freeze({ + '115': '115', + '2345': '2345', + '360': '360', + ALIPAY: 'Alipay', + AMAYA: 'Amaya', + ANDROID: 'Android Browser', + ARORA: 'Arora', + AVANT: 'Avant', + AVAST: 'Avast Secure Browser', + AVG: 'AVG Secure Browser', + BAIDU: 'Baidu Browser', + BASILISK: 'Basilisk', + BLAZER: 'Blazer', + BOLT: 'Bolt', + BOWSER: 'Bowser', + BRAVE: 'Brave', + CAMINO: 'Camino', + CHIMERA: 'Chimera', + CHROME: 'Chrome', + CHROME_HEADLESS: 'Chrome Headless', + CHROME_MOBILE: 'Mobile Chrome', + CHROME_WEBVIEW: 'Chrome WebView', + CHROMIUM: 'Chromium', + COBALT: 'Cobalt', + COC_COC: 'Coc Coc', + CONKEROR: 'Conkeror', + DAUM: 'Daum', + DILLO: 'Dillo', + DOLPHIN: 'Dolphin', + DOOBLE: 'Dooble', + DORIS: 'Doris', + DRAGON: 'Dragon', + DUCKDUCKGO: 'DuckDuckGo', + ECOSIA: 'Ecosia', + EDGE: 'Edge', + EDGE_WEBVIEW: 'Edge WebView', + EDGE_WEBVIEW2: 'Edge WebView2', + EPIPHANY: 'Epiphany', + FACEBOOK: 'Facebook', + FALKON: 'Falkon', + FIREBIRD: 'Firebird', + FIREFOX: 'Firefox', + FIREFOX_FOCUS: 'Firefox Focus', + FIREFOX_MOBILE: 'Mobile Firefox', + FIREFOX_REALITY: 'Firefox Reality', + FENNEC: 'Fennec', + FLOCK: 'Flock', + FLOW: 'Flow', + GO: 'GoBrowser', + GOOGLE_SEARCH: 'GSA', + HELIO: 'Helio', + HEYTAP: 'HeyTap', + HONOR: 'Honor', + HUAWEI: 'Huawei Browser', + ICAB: 'iCab', + ICE: 'ICE Browser', + ICEAPE: 'IceApe', + ICECAT: 'IceCat', + ICEDRAGON: 'IceDragon', + ICEWEASEL: 'IceWeasel', + IE: 'IE', + INSTAGRAM: 'Instagram', + IRIDIUM: 'Iridium', + IRON: 'Iron', + JASMINE: 'Jasmine', + KONQUEROR: 'Konqueror', + KAKAO: 'KakaoTalk', + KHTML: 'KHTML', + K_MELEON: 'K-Meleon', + KLAR: 'Klar', + KLARNA: 'Klarna', + KINDLE: 'Kindle', + LENOVO: 'Smart Lenovo Browser', + LADYBIRD: 'Ladybird', + LG: 'LG Browser', + LIBREWOLF: 'LibreWolf', + LIEBAO: 'LBBROWSER', + LINE: 'Line', + LINKEDIN: 'LinkedIn', + LINKS: 'Links', + LUNASCAPE: 'Lunascape', + LYNX: 'Lynx', + MAEMO: 'Maemo Browser', + MAXTHON: 'Maxthon', + MIDORI: 'Midori', + MINIMO: 'Minimo', + MIUI: 'MIUI Browser', + MOZILLA: 'Mozilla', + MOSAIC: 'Mosaic', + NAVER: 'Naver', + NETFRONT: 'NetFront', + NETSCAPE: 'Netscape', + NETSURF: 'Netsurf', + NOKIA: 'Nokia Browser', + OBIGO: 'Obigo', + OCULUS: 'Oculus Browser', + OMNIWEB: 'OmniWeb', + OPERA: 'Opera', + OPERA_COAST: 'Opera Coast', + OPERA_GX: 'Opera GX', + OPERA_MINI: 'Opera Mini', + OPERA_MOBI: 'Opera Mobi', + OPERA_TABLET: 'Opera Tablet', + OPERA_TOUCH: 'Opera Touch', + OTTER: 'Otter', + OVI: 'OviBrowser', + PALEMOON: 'PaleMoon', + PHANTOMJS: 'PhantomJS', + PHOENIX: 'Phoenix', + PICOBROWSER: 'Pico Browser', + POLARIS: 'Polaris', + PUFFIN: 'Puffin', + QQ: 'QQBrowser', + QQ_LITE: 'QQBrowserLite', + QUARK: 'Quark', + QUPZILLA: 'QupZilla', + QUTEBROWSER: 'qutebrowser', + REKONQ: 'rekonq', + ROCKMELT: 'Rockmelt', + SAFARI: 'Safari', + SAFARI_MOBILE: 'Mobile Safari', + SAILFISH: 'Sailfish Browser', + SAMSUNG: 'Samsung Internet', + SEAMONKEY: 'SeaMonkey', + SILK: 'Silk', + SKYFIRE: 'Skyfire', + SLEIPNIR: 'Sleipnir', + SLIMBOAT: 'SlimBoat', + SLIMBROWSER: 'SlimBrowser', + SLIMJET: 'Slimjet', + SNAPCHAT: 'Snapchat', + SOGOU_EXPLORER: 'Sogou Explorer', + SOGOU_MOBILE: 'Sogou Mobile', + SURF: 'Surf', + SWIFTFOX: 'Swiftfox', + TESLA: 'Tesla', + TIKTOK: 'TikTok', + TIZEN: 'Tizen Browser', + TWITTER: 'Twitter', + UC: 'UCBrowser', + UP: 'UP.Browser', + VIVALDI: 'Vivaldi', + VIVO: 'Vivo Browser', + W3M: 'w3m', + WATERFOX: 'Waterfox', + WEBKIT: 'WebKit', + WECHAT: 'WeChat', + WEIBO: 'Weibo', + WHALE: 'Whale', + WOLVIC: 'Wolvic', + YANDEX: 'Yandex', + ZALO: 'Zalo' + + // TODO : test! +}); + +const BrowserType = Object.freeze({ + CRAWLER: 'crawler', + CLI: 'cli', + EMAIL: 'email', + FETCHER: 'fetcher', + INAPP: 'inapp', + MEDIAPLAYER: 'mediaplayer', + LIBRARY: 'library' +}); + +const CPU = Object.freeze({ + '68K': '68k', + ALPHA: 'alpha', + ARM : 'arm', + ARM_64: 'arm64', + ARM_HF: 'armhf', + AVR: 'avr', + AVR_32: 'avr32', + IA64: 'ia64', + IRIX: 'irix', + IRIX_64: 'irix64', + MIPS: 'mips', + MIPS_64: 'mips64', + PA_RISC: 'pa-risc', + PPC: 'ppc', + SPARC: 'sparc', + SPARC_64: 'sparc64', + X86: 'ia32', + X86_64: 'amd64' +}); + +const Device = Object.freeze({ + CONSOLE: 'console', + DESKTOP: 'desktop', + EMBEDDED: 'embedded', + MOBILE: 'mobile', + SMARTTV: 'smarttv', + TABLET: 'tablet', + WEARABLE: 'wearable', + XR: 'xr' +}); + +const Vendor = Object.freeze({ + ACER: 'Acer', + ADVAN: 'Advan', + ALCATEL: 'Alcatel', + APPLE: 'Apple', + AMAZON: 'Amazon', + ARCHOS: 'Archos', + ASUS: 'ASUS', + ATT: 'AT&T', + BENQ: 'BenQ', + BLACKBERRY: 'BlackBerry', + BLU: 'BLU', + CAT: 'Cat', + DELL: 'Dell', + ENERGIZER: 'Energizer', + ESSENTIAL: 'Essential', + FACEBOOK: 'Facebook', + FAIRPHONE: 'Fairphone', + GEEKSPHONE: 'GeeksPhone', + GENERIC: 'Generic', + GOOGLE: 'Google', + HMD: 'HMD', + HP: 'HP', + HTC: 'HTC', + HUAWEI: 'Huawei', + IMO: 'IMO', + INFINIX: 'Infinix', + ITEL: 'itel', + JOLLA: 'Jolla', + KOBO: 'Kobo', + LAVA: 'Lava', + LENOVO: 'Lenovo', + LG: 'LG', + MEIZU: 'Meizu', + MICROMAX: 'Micromax', + MICROSOFT: 'Microsoft', + MOTOROLA: 'Motorola', + NEXIAN: 'Nexian', + NINTENDO: 'Nintendo', + NOKIA: 'Nokia', + NOTHING: 'Nothing', + NVIDIA: 'Nvidia', + ONEPLUS: 'OnePlus', + OPPO: 'OPPO', + OUYA: 'Ouya', + PALM: 'Palm', + PANASONIC: 'Panasonic', + PEBBLE: 'Pebble', + PHILIPS: 'Philips', + PICO: 'Pico', + POLYTRON: 'Polytron', + REALME: 'Realme', + RETROID: 'Retroid', + RIM: 'RIM', + ROKU: 'Roku', + SAMSUNG: 'Samsung', + SHARP: 'Sharp', + SIEMENS: 'Siemens', + SMARTFREN: 'Smartfren', + SONY: 'Sony', + SPRINT: 'Sprint', + TCL: 'TCL', + TECHNISAT: 'TechniSAT', + TECNO: 'Tecno', + TESLA: 'Tesla', + ULEFONE: 'Ulefone', + VIVO: 'Vivo', + VIZIO: 'Vizio', + VODAFONE: 'Vodafone', + XBOX: 'Xbox', + XIAOMI: 'Xiaomi', + ZEBRA: 'Zebra', + ZTE: 'ZTE', + + // TODO : test! +}); + +const Engine = Object.freeze({ + AMAYA: 'Amaya', + ARKWEB: 'ArkWeb', + BLINK: 'Blink', + EDGEHTML: 'EdgeHTML', + FLOW: 'Flow', + GECKO: 'Gecko', + GOANNA: 'Goanna', + ICAB: 'iCab', + KHTML: 'KHTML', + LIBWEB: 'LibWeb', + LINKS: 'Links', + LYNX: 'Lynx', + NETFRONT: 'NetFront', + NETSURF: 'NetSurf', + PRESTO: 'Presto', + SERVO: 'Servo', + TASMAN: 'Tasman', + TRIDENT: 'Trident', + W3M: 'w3m', + WEBKIT: 'WebKit' +}); + +const OS = Object.freeze({ + AIX: 'AIX', + AMIGA_OS: 'Amiga OS', + ANDROID: 'Android', + ANDROID_X86: 'Android-x86', + ARCAOS: 'ArcaOS', + ARCH: 'Arch', + BADA: 'Bada', + BEOS: 'BeOS', + BLACKBERRY: 'BlackBerry', + CENTOS: 'CentOS', + CHROME_OS: 'Chrome OS', + CHROMECAST: 'Chromecast', + CHROMECAST_ANDROID: 'Chromecast Android', + CHROMECAST_FUCHSIA: 'Chromecast Fuchsia', + CHROMECAST_LINUX: 'Chromecast Linux', + CHROMECAST_SMARTSPEAKER: 'Chromecast SmartSpeaker', + CONTIKI: 'Contiki', + DEBIAN: 'Debian', + DEEPIN: 'Deepin', + DRAGONFLY: 'DragonFly', + ELEMENTARY_OS: 'elementary OS', + FEDORA: 'Fedora', + FIREFOX_OS: 'Firefox OS', + FREEBSD: 'FreeBSD', + FUCHSIA: 'Fuchsia', + GENTOO: 'Gentoo', + GHOSTBSD: 'GhostBSD', + GNU: 'GNU', + HAIKU: 'Haiku', + HARMONYOS: 'HarmonyOS', + HP_UX: 'HP-UX', + HURD: 'Hurd', + IOS: 'iOS', + JOLI: 'Joli', + KAIOS: 'KaiOS', + KNOPPIX: 'Knoppix', + KUBUNTU: 'Kubuntu', + LINPUS: 'Linpus', + LINSPIRE: 'Linspire', + LINUX: 'Linux', + MACOS: 'macOS', + MAEMO: 'Maemo', + MAGEIA: 'Mageia', + MANDRIVA: 'Mandriva', + MANJARO: 'Manjaro', + MEEGO: 'MeeGo', + MINIX: 'Minix', + MINT: 'Mint', + MORPH_OS: 'Morph OS', + NETBSD: 'NetBSD', + NETRANGE: 'NetRange', + NETTV: 'NetTV', + NINTENDO: 'Nintendo', + OPENHARMONY: 'OpenHarmony', + OPENBSD: 'OpenBSD', + OPENVMS: 'OpenVMS', + OS2: 'OS/2', + PALM: 'Palm', + PC_BSD: 'PC-BSD', + PCLINUXOS: 'PCLinuxOS', + PICO: 'Pico', + PLAN9: 'Plan9', + PLAYSTATION: 'PlayStation', + QNX: 'QNX', + RASPBIAN: 'Raspbian', + REDHAT: 'RedHat', + RIM_TABLET_OS: 'RIM Tablet OS', + RISC_OS: 'RISC OS', + SABAYON: 'Sabayon', + SAILFISH: 'Sailfish', + SERENITYOS: 'SerenityOS', + SERIES40: 'Series40', + SLACKWARE: 'Slackware', + SOLARIS: 'Solaris', + SUSE: 'SUSE', + SYMBIAN: 'Symbian', + TIZEN: 'Tizen', + UBUNTU: 'Ubuntu', + UBUNTU_TOUCH: 'Ubuntu Touch', + UNIX: 'Unix', + VECTORLINUX: 'VectorLinux', + WATCHOS: 'watchOS', + WEBOS: 'WebOS', + WINDOWS: 'Windows', + WINDOWS_CE: 'Windows CE', + WINDOWS_IOT: 'Windows IoT', + WINDOWS_MOBILE: 'Windows Mobile', + WINDOWS_PHONE: 'Windows Phone', + WINDOWS_RT: 'Windows RT', + XBOX: 'Xbox', + XUBUNTU: 'Xubuntu', + ZENWALK: 'Zenwalk' + + // TODO : test! +}); + +module.exports = { + Browser, + BrowserType, + CPU, + Device, + Vendor, + Engine, + OS +}; \ No newline at end of file diff --git a/src/enums/ua-parser-enums.mjs b/src/enums/ua-parser-enums.mjs new file mode 100644 index 000000000..089af9573 --- /dev/null +++ b/src/enums/ua-parser-enums.mjs @@ -0,0 +1,415 @@ +// Generated ESM version of ua-parser-js/enums +// DO NOT EDIT THIS FILE! +// Source: /src/enums/ua-parser-enums.js + +/////////////////////////////////////////////// +/* Enums for UAParser.js v2.0.4 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + AGPLv3 License */ +////////////////////////////////////////////// + +/*jshint esversion: 6 */ + +const Browser = Object.freeze({ + '115': '115', + '2345': '2345', + '360': '360', + ALIPAY: 'Alipay', + AMAYA: 'Amaya', + ANDROID: 'Android Browser', + ARORA: 'Arora', + AVANT: 'Avant', + AVAST: 'Avast Secure Browser', + AVG: 'AVG Secure Browser', + BAIDU: 'Baidu Browser', + BASILISK: 'Basilisk', + BLAZER: 'Blazer', + BOLT: 'Bolt', + BOWSER: 'Bowser', + BRAVE: 'Brave', + CAMINO: 'Camino', + CHIMERA: 'Chimera', + CHROME: 'Chrome', + CHROME_HEADLESS: 'Chrome Headless', + CHROME_MOBILE: 'Mobile Chrome', + CHROME_WEBVIEW: 'Chrome WebView', + CHROMIUM: 'Chromium', + COBALT: 'Cobalt', + COC_COC: 'Coc Coc', + CONKEROR: 'Conkeror', + DAUM: 'Daum', + DILLO: 'Dillo', + DOLPHIN: 'Dolphin', + DOOBLE: 'Dooble', + DORIS: 'Doris', + DRAGON: 'Dragon', + DUCKDUCKGO: 'DuckDuckGo', + ECOSIA: 'Ecosia', + EDGE: 'Edge', + EDGE_WEBVIEW: 'Edge WebView', + EDGE_WEBVIEW2: 'Edge WebView2', + EPIPHANY: 'Epiphany', + FACEBOOK: 'Facebook', + FALKON: 'Falkon', + FIREBIRD: 'Firebird', + FIREFOX: 'Firefox', + FIREFOX_FOCUS: 'Firefox Focus', + FIREFOX_MOBILE: 'Mobile Firefox', + FIREFOX_REALITY: 'Firefox Reality', + FENNEC: 'Fennec', + FLOCK: 'Flock', + FLOW: 'Flow', + GO: 'GoBrowser', + GOOGLE_SEARCH: 'GSA', + HELIO: 'Helio', + HEYTAP: 'HeyTap', + HONOR: 'Honor', + HUAWEI: 'Huawei Browser', + ICAB: 'iCab', + ICE: 'ICE Browser', + ICEAPE: 'IceApe', + ICECAT: 'IceCat', + ICEDRAGON: 'IceDragon', + ICEWEASEL: 'IceWeasel', + IE: 'IE', + INSTAGRAM: 'Instagram', + IRIDIUM: 'Iridium', + IRON: 'Iron', + JASMINE: 'Jasmine', + KONQUEROR: 'Konqueror', + KAKAO: 'KakaoTalk', + KHTML: 'KHTML', + K_MELEON: 'K-Meleon', + KLAR: 'Klar', + KLARNA: 'Klarna', + KINDLE: 'Kindle', + LENOVO: 'Smart Lenovo Browser', + LADYBIRD: 'Ladybird', + LG: 'LG Browser', + LIBREWOLF: 'LibreWolf', + LIEBAO: 'LBBROWSER', + LINE: 'Line', + LINKEDIN: 'LinkedIn', + LINKS: 'Links', + LUNASCAPE: 'Lunascape', + LYNX: 'Lynx', + MAEMO: 'Maemo Browser', + MAXTHON: 'Maxthon', + MIDORI: 'Midori', + MINIMO: 'Minimo', + MIUI: 'MIUI Browser', + MOZILLA: 'Mozilla', + MOSAIC: 'Mosaic', + NAVER: 'Naver', + NETFRONT: 'NetFront', + NETSCAPE: 'Netscape', + NETSURF: 'Netsurf', + NOKIA: 'Nokia Browser', + OBIGO: 'Obigo', + OCULUS: 'Oculus Browser', + OMNIWEB: 'OmniWeb', + OPERA: 'Opera', + OPERA_COAST: 'Opera Coast', + OPERA_GX: 'Opera GX', + OPERA_MINI: 'Opera Mini', + OPERA_MOBI: 'Opera Mobi', + OPERA_TABLET: 'Opera Tablet', + OPERA_TOUCH: 'Opera Touch', + OTTER: 'Otter', + OVI: 'OviBrowser', + PALEMOON: 'PaleMoon', + PHANTOMJS: 'PhantomJS', + PHOENIX: 'Phoenix', + PICOBROWSER: 'Pico Browser', + POLARIS: 'Polaris', + PUFFIN: 'Puffin', + QQ: 'QQBrowser', + QQ_LITE: 'QQBrowserLite', + QUARK: 'Quark', + QUPZILLA: 'QupZilla', + QUTEBROWSER: 'qutebrowser', + REKONQ: 'rekonq', + ROCKMELT: 'Rockmelt', + SAFARI: 'Safari', + SAFARI_MOBILE: 'Mobile Safari', + SAILFISH: 'Sailfish Browser', + SAMSUNG: 'Samsung Internet', + SEAMONKEY: 'SeaMonkey', + SILK: 'Silk', + SKYFIRE: 'Skyfire', + SLEIPNIR: 'Sleipnir', + SLIMBOAT: 'SlimBoat', + SLIMBROWSER: 'SlimBrowser', + SLIMJET: 'Slimjet', + SNAPCHAT: 'Snapchat', + SOGOU_EXPLORER: 'Sogou Explorer', + SOGOU_MOBILE: 'Sogou Mobile', + SURF: 'Surf', + SWIFTFOX: 'Swiftfox', + TESLA: 'Tesla', + TIKTOK: 'TikTok', + TIZEN: 'Tizen Browser', + TWITTER: 'Twitter', + UC: 'UCBrowser', + UP: 'UP.Browser', + VIVALDI: 'Vivaldi', + VIVO: 'Vivo Browser', + W3M: 'w3m', + WATERFOX: 'Waterfox', + WEBKIT: 'WebKit', + WECHAT: 'WeChat', + WEIBO: 'Weibo', + WHALE: 'Whale', + WOLVIC: 'Wolvic', + YANDEX: 'Yandex' + + // TODO : test! +}); + +const BrowserType = Object.freeze({ + CRAWLER: 'crawler', + CLI: 'cli', + EMAIL: 'email', + FETCHER: 'fetcher', + INAPP: 'inapp', + MEDIAPLAYER: 'mediaplayer', + LIBRARY: 'library' +}); + +const CPU = Object.freeze({ + '68K': '68k', + ARM : 'arm', + ARM_64: 'arm64', + ARM_HF: 'armhf', + AVR: 'avr', + AVR_32: 'avr32', + IA64: 'ia64', + IRIX: 'irix', + IRIX_64: 'irix64', + MIPS: 'mips', + MIPS_64: 'mips64', + PA_RISC: 'pa-risc', + PPC: 'ppc', + SPARC: 'sparc', + SPARC_64: 'sparc64', + X86: 'ia32', + X86_64: 'amd64' +}); + +const Device = Object.freeze({ + CONSOLE: 'console', + DESKTOP: 'desktop', + EMBEDDED: 'embedded', + MOBILE: 'mobile', + SMARTTV: 'smarttv', + TABLET: 'tablet', + WEARABLE: 'wearable', + XR: 'xr' +}); + +const Vendor = Object.freeze({ + ACER: 'Acer', + ADVAN: 'Advan', + ALCATEL: 'Alcatel', + APPLE: 'Apple', + AMAZON: 'Amazon', + ARCHOS: 'Archos', + ASUS: 'ASUS', + ATT: 'AT&T', + BENQ: 'BenQ', + BLACKBERRY: 'BlackBerry', + BLU: 'BLU', + CAT: 'Cat', + DELL: 'Dell', + ENERGIZER: 'Energizer', + ESSENTIAL: 'Essential', + FACEBOOK: 'Facebook', + FAIRPHONE: 'Fairphone', + GEEKSPHONE: 'GeeksPhone', + GENERIC: 'Generic', + GOOGLE: 'Google', + HMD: 'HMD', + HP: 'HP', + HTC: 'HTC', + HUAWEI: 'Huawei', + IMO: 'IMO', + INFINIX: 'Infinix', + ITEL: 'itel', + JOLLA: 'Jolla', + KOBO: 'Kobo', + LAVA: 'Lava', + LENOVO: 'Lenovo', + LG: 'LG', + MEIZU: 'Meizu', + MICROMAX: 'Micromax', + MICROSOFT: 'Microsoft', + MOTOROLA: 'Motorola', + NEXIAN: 'Nexian', + NINTENDO: 'Nintendo', + NOKIA: 'Nokia', + NOTHING: 'Nothing', + NVIDIA: 'Nvidia', + ONEPLUS: 'OnePlus', + OPPO: 'OPPO', + OUYA: 'Ouya', + PALM: 'Palm', + PANASONIC: 'Panasonic', + PEBBLE: 'Pebble', + PICO: 'Pico', + POLYTRON: 'Polytron', + REALME: 'Realme', + RETROID: 'Retroid', + RIM: 'RIM', + ROKU: 'Roku', + SAMSUNG: 'Samsung', + SHARP: 'Sharp', + SIEMENS: 'Siemens', + SMARTFREN: 'Smartfren', + SONY: 'Sony', + SPRINT: 'Sprint', + TCL: 'TCL', + TECHNISAT: 'TechniSAT', + TECNO: 'Tecno', + TESLA: 'Tesla', + ULEFONE: 'Ulefone', + VIVO: 'Vivo', + VIZIO: 'Vizio', + VODAFONE: 'Vodafone', + XBOX: 'Xbox', + XIAOMI: 'Xiaomi', + ZEBRA: 'Zebra', + ZTE: 'ZTE', + + // TODO : test! +}); + +const Engine = Object.freeze({ + AMAYA: 'Amaya', + ARKWEB: 'ArkWeb', + BLINK: 'Blink', + EDGEHTML: 'EdgeHTML', + FLOW: 'Flow', + GECKO: 'Gecko', + GOANNA: 'Goanna', + ICAB: 'iCab', + KHTML: 'KHTML', + LIBWEB: 'LibWeb', + LINKS: 'Links', + LYNX: 'Lynx', + NETFRONT: 'NetFront', + NETSURF: 'NetSurf', + PRESTO: 'Presto', + SERVO: 'Servo', + TASMAN: 'Tasman', + TRIDENT: 'Trident', + W3M: 'w3m', + WEBKIT: 'WebKit' +}); + +const OS = Object.freeze({ + AIX: 'AIX', + AMIGA_OS: 'Amiga OS', + ANDROID: 'Android', + ANDROID_X86: 'Android-x86', + ARCAOS: 'ArcaOS', + ARCH: 'Arch', + BADA: 'Bada', + BEOS: 'BeOS', + BLACKBERRY: 'BlackBerry', + CENTOS: 'CentOS', + CHROME_OS: 'Chrome OS', + CHROMECAST: 'Chromecast', + CHROMECAST_ANDROID: 'Chromecast Android', + CHROMECAST_FUCHSIA: 'Chromecast Fuchsia', + CHROMECAST_LINUX: 'Chromecast Linux', + CHROMECAST_SMARTSPEAKER: 'Chromecast SmartSpeaker', + CONTIKI: 'Contiki', + DEBIAN: 'Debian', + DEEPIN: 'Deepin', + DRAGONFLY: 'DragonFly', + ELEMENTARY_OS: 'elementary OS', + FEDORA: 'Fedora', + FIREFOX_OS: 'Firefox OS', + FREEBSD: 'FreeBSD', + FUCHSIA: 'Fuchsia', + GENTOO: 'Gentoo', + GHOSTBSD: 'GhostBSD', + GNU: 'GNU', + HAIKU: 'Haiku', + HARMONYOS: 'HarmonyOS', + HP_UX: 'HP-UX', + HURD: 'Hurd', + IOS: 'iOS', + JOLI: 'Joli', + KAIOS: 'KaiOS', + KNOPPIX: 'Knoppix', + KUBUNTU: 'Kubuntu', + LINPUS: 'Linpus', + LINSPIRE: 'Linspire', + LINUX: 'Linux', + MACOS: 'macOS', + MAEMO: 'Maemo', + MAGEIA: 'Mageia', + MANDRIVA: 'Mandriva', + MANJARO: 'Manjaro', + MEEGO: 'MeeGo', + MINIX: 'Minix', + MINT: 'Mint', + MORPH_OS: 'Morph OS', + NETBSD: 'NetBSD', + NETRANGE: 'NetRange', + NETTV: 'NetTV', + NINTENDO: 'Nintendo', + OPENHARMONY: 'OpenHarmony', + OPENBSD: 'OpenBSD', + OPENVMS: 'OpenVMS', + OS2: 'OS/2', + PALM: 'Palm', + PC_BSD: 'PC-BSD', + PCLINUXOS: 'PCLinuxOS', + PICO: 'Pico', + PLAN9: 'Plan9', + PLAYSTATION: 'PlayStation', + QNX: 'QNX', + RASPBIAN: 'Raspbian', + REDHAT: 'RedHat', + RIM_TABLET_OS: 'RIM Tablet OS', + RISC_OS: 'RISC OS', + SABAYON: 'Sabayon', + SAILFISH: 'Sailfish', + SERENITYOS: 'SerenityOS', + SERIES40: 'Series40', + SLACKWARE: 'Slackware', + SOLARIS: 'Solaris', + SUSE: 'SUSE', + SYMBIAN: 'Symbian', + TIZEN: 'Tizen', + UBUNTU: 'Ubuntu', + UBUNTU_TOUCH: 'Ubuntu Touch', + UNIX: 'Unix', + VECTORLINUX: 'VectorLinux', + WATCHOS: 'watchOS', + WEBOS: 'WebOS', + WINDOWS: 'Windows', + WINDOWS_CE: 'Windows CE', + WINDOWS_IOT: 'Windows IoT', + WINDOWS_MOBILE: 'Windows Mobile', + WINDOWS_PHONE: 'Windows Phone', + WINDOWS_RT: 'Windows RT', + XBOX: 'Xbox', + XUBUNTU: 'Xubuntu', + ZENWALK: 'Zenwalk' + + // TODO : test! +}); + +export { + Browser, + BrowserType, + CPU, + Device, + Vendor, + Engine, + OS +}; \ No newline at end of file diff --git a/src/extensions/ua-parser-extensions.d.ts b/src/extensions/ua-parser-extensions.d.ts new file mode 100644 index 000000000..4ee31dd86 --- /dev/null +++ b/src/extensions/ua-parser-extensions.d.ts @@ -0,0 +1,16 @@ +// Type definitions for Helpers submodule of UAParser.js v2.0.4 +// Project: https://github.com/faisalman/ua-parser-js +// Definitions by: Faisal Salman + +import type { UAParserExt } from "../main/ua-parser"; + +export const Bots: UAParserExt; +export const CLIs: UAParserExt; +export const Crawlers: UAParserExt; +export const ExtraDevices: UAParserExt; +export const Emails: UAParserExt; +export const Fetchers: UAParserExt; +export const InApps: UAParserExt; +export const Libraries: UAParserExt; +export const MediaPlayers: UAParserExt; +export const Vehicles: UAParserExt; \ No newline at end of file diff --git a/src/extensions/ua-parser-extensions.js b/src/extensions/ua-parser-extensions.js new file mode 100644 index 000000000..4e93b00fe --- /dev/null +++ b/src/extensions/ua-parser-extensions.js @@ -0,0 +1,435 @@ +/////////////////////////////////////////////// +/* Extensions for UAParser.js v2.0.4 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + AGPLv3 License */ +////////////////////////////////////////////// + +/*jshint esversion: 6 */ + +const MODEL = 'model'; +const NAME = 'name'; +const TYPE = 'type'; +const VENDOR = 'vendor'; +const VERSION = 'version'; +const MOBILE = 'mobile'; +const TABLET = 'tablet'; +const CRAWLER = 'crawler'; +const CLI = 'cli'; +const EMAIL = 'email'; +const FETCHER = 'fetcher'; +const INAPP = 'inapp'; +const MEDIAPLAYER = 'mediaplayer'; +const LIBRARY = 'library'; + +////////////////////// +// COMMAND LINE APPS +///////////////////// + +const CLIs = Object.freeze({ + browser : [ + // wget / curl / Lynx / ELinks / HTTPie + [/(wget|curl|lynx|elinks|httpie)[\/ ]\(?([\w\.-]+)/i], [NAME, VERSION, [TYPE, CLI]] + ] +}); + +//////////////////////// +// CRAWLERS / SPIDERS +/////////////////////// + +const Crawlers = Object.freeze({ + browser : [ + [ + // AhrefsBot - https://ahrefs.com/robot + // Amazonbot - https://developer.amazon.com/amazonbot + // Bingbot / AdIdxBot - https://www.bing.com/webmasters/help/which-crawlers-does-bing-use-8c184ec0 + // Bravebot - https://search.brave.com/help/brave-search-crawler + // CCBot - https://commoncrawl.org/faq + // Coveobot - https://connect.coveo.com/s/article/19648 + // CriteoBot - https://www.criteo.com/criteo-crawler/ + // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot + // DuckDuckBot - http://duckduckgo.com/duckduckbot.html + // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ + // GPTBot - https://platform.openai.com/docs/gptbot + // iAskBot - https://iask.ai + // Kangaroo Bot - https://kangaroollm.com.au/kangaroo-bot/ + // LinkedInBot - http://www.linkedin.com + // MJ12bot - https://mj12bot.com/ + // MojeekBot - https://www.mojeek.com/bot.html + // Onespot - https://www.onespot.com/identifying-traffic.html + // OpenAI's SearchGPT - https://platform.openai.com/docs/bots + // PerplexityBot - https://perplexity.ai/perplexitybot + // SeznamBot - http://napoveda.seznam.cz/seznambot-intro + /((?:adidx|ahrefs|amazon|bing|brave|cc|coveo|criteo|dot|duckduck(?:go-favicons-)?|exa|facebook|gpt|iask|kangaroo |linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + + // Applebot - http://apple.com/go/applebot + /(applebot(?:-extended)?)\/?([\w\.]*)/i, + + // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 + /(baiduspider[-imagevdonwsfcpr]{0,7})\/?([\w\.]*)/i, + + // ClaudeBot (Anthropic) + /(claude(?:bot|-searchbot|-web)|anthropic-ai)\/?([\w\.]*)/i, + + // Coc Coc Bot - https://help.coccoc.com/en/search-engine + /(coccocbot-(?:image|web))\/([\w\.]+)/i, + + // Daum + /(daum(?:oa)?(?:-image)?)[ \/]([\w\.]+)/i, + + // Facebook / Meta + // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers + /(facebook(?:externalhit|catalog)|meta-externalagent)\/([\w\.]+)/i, + + // Googlebot - http://www.google.com/bot.html + /(google(?:bot|other|-inspectiontool)(?:-image|-video|-news)?|storebot-google)\/?([\w\.]*)/i, + + // Internet Archive (archive.org) + /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, + + // Qwantbot - https://help.qwant.com/bot + /(qwantbot)[-\w]*\/?([\w\.]*)/i, + + // SemrushBot - http://www.semrush.com/bot.html + /((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i, + + // Sogou Spider + /(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i, + + // Yahoo! Japan - https://support.yahoo-net.jp/PccSearch/s/article/H000007955 + /(y!?j-(?:asr|br[uw]|dscv|mmp|vsidx|wsc))\/([\w\.]+)/i, + + // 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, + + // Yeti (Naver) + /(yeti)\/([\w\.]+)/i, + + // aiHitBot / Diffbot / FirecrawlAgent / HuggingFace-Bot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / PanguBot / Replicate-Bot / RunPod-Bot / Webzio-Extended / Screaming Frog SEO Spider / Startpage / Timpibot / Together-Bot / VelenPublicWebCrawler / xAI-Bot / YisouSpider / YouBot + // Cotoyogi - https://ds.rois.ac.jp/en_center8/en_crawler/ + /((?:aihit|diff|huggingface-|pangu|replicate-|runpod-|timpi|together-|xai-|you)bot|omgili(?:bot)?|cotoyogi|firecrawlagent|openai image downloader|(?:magpie-|velenpublicweb)crawler|startpageprivateimageproxy|webzio-extended|(?:chatglm-|line|screaming frog seo |yisou)spider)\/?([\w\.]*)/i + ], + + [NAME, VERSION, [TYPE, CRAWLER]], + + [ + // Google Bots + /((?:adsbot|apis|mediapartners)-google(?:-mobile)?|google-?(?:other|cloudvertexbot|extended|safety))/i, + + // AI2Bot - https://allenai.org/crawler + // Bytespider + // DataForSeoBot - https://dataforseo.com/dataforseo-bot + // DeepSeekBot + // Huawei AspiegelBot / PetalBot https://aspiegel.com/petalbot + // ImagesiftBot - https://imagesift.com/about + // Qihoo 360Spider + // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html + // v0bot - https://vercel.com/docs/bot-management + // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp + /\b(360spider-?(?:image|video)?|bytespider|cohere-training-data-crawler|elastic(?=\/s)|(?:ai2|aspiegel|dataforseo|deepseek|imagesift|petal|turnitin|v0)bot|teoma|yahoo! slurp)/i + ], + [NAME, [TYPE, CRAWLER]] + ] +}); + +////////////////// +// EXTRA DEVICES +///////////////// + +const ExtraDevices = Object.freeze({ + device : [[ + /(nook)[\w ]+build\/(\w+)/i, // Nook + /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak + /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets + /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets + /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets + /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + + /(u304aa)/i // AT&T + ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [ + + /\bsie-(\w*)/i // Siemens + ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ + + /\b(rct\w+) b/i // RCA Tablets + ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ + + /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets + ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ + + /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet + ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [ + + /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet + ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [ + + /\b(tm\d{3}\w+) b/i + ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ + + /\b(k88) b/i // ZTE K Series Tablet + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [ + + /\b(nx\d{3}j) b/i // ZTE Nubia + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [ + + /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [ + + /\b(zur\d{3}) b/i // Swiss ZUR Tablet + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [ + + /^((zeki)?tb.*\b) b/i // Zeki Tablets + ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ + + /\b([yr]\d{2}) b/i, + /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet + ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [ + + /\b(ns-?\w{0,9}) b/i // Insignia Tablets + ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ + + /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets + ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ + + /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones + ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ + + /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones + ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ + + /\b(ph-1) /i // Essential PH-1 + ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ + + /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets + ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ + + /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets + ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [ + + /\btu_(1491) b/i // Rotor Tablets + ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]] + ] +}); + +/////////////// +// EMAIL APPS +////////////// + +const Emails = Object.freeze({ + browser : [ + [ + // Evolution / Kontact/KMail[2] / [Microsoft/Mac] Outlook / Thunderbird + // Airmail / BlueMail / DaumMail / eMClient / Foxmail / NaverMailApp / Polymail + // ProtonMail / SparkDesktop / Sparrow / Yahoo! Mail / Zimbra / ZohoMail-Desktop + /((?:air|blue|daum|fox|poly|proton)mail|emclient|evolution|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|sparkdesktop|thunderbird|yahoo|zohomail-desktop)(?:m.+ail; |[\/ ])([\w\.]+)/i, + + // Apple's Mail + /(mail)\/([\w\.]+) cf/i + ], [NAME, VERSION, [TYPE, EMAIL]], [ + + // Zimbra + /zdesktop\/([\w\.]+)/i + ], [VERSION, [NAME, 'Zimbra'], [TYPE, EMAIL]] + ] +}); + +/////////////////////// +// ON-DEMAND SCRAPERS +////////////////////// + +const Fetchers = Object.freeze({ + browser : [ + [ + // Asana / Bitlybot / Better Uptime / BingPreview / Blueno / kakaotalk-scrap / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot + // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit + // Buffer Link Preview Bot - https://scraper.buffer.com/about/bots/link-preview-bot + // ChatGPT-User - https://platform.openai.com/docs/plugins/bot + // DuckAssistBot - https://duckduckgo.com/duckassistbot/ + // Google Site Verifier / Meta / Yahoo! Japan + // Iframely - https://iframely.com/docs/about + // Perplexity-User - https://docs.perplexity.ai/guides/bots + // MistralAI-User - https://docs.mistral.ai/robots/ + // Yandex Bots - https://yandex.com/bots + /(asana|ahrefssiteaudit|(?:bing|microsoft)preview|blueno|(?:chatgpt|claude|mistralai|perplexity)-user|mastodon|(?:bitly|bufferlinkpreview|discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero|zoom)bot|google-site-verification|iframely|kakaotalk-scrap|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + + // Bluesky + /(bluesky) cardyb\/([\w\.]+)/i, + + // Skype + /(skypeuripreview) preview\/([\w\.]+)/i, + + // Slackbot - https://api.slack.com/robots + /(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i, + + // WhatsApp + /(whatsapp)\/([\w\.]+)/i + ], + [NAME, VERSION, [TYPE, FETCHER]], + + [ + // Google Bots / Chrome-Lighthouse / Cohere / Gemini-Deep-Research / Snapchat / TikTokSpider / Vercelbot / Yandex Bots + /((?:better uptime |telegram|vercel)bot|chrome-lighthouse|cohere-ai|feedfetcher-google|gemini-deep-research|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|tiktokspider|vercel(flags|tracing|-(favicon|screenshot)-bot)|yandex(?:sitelinks|userproxy))/i + ], + [NAME, [TYPE, FETCHER]], + ], + + os : [ + [/whatsapp\/[\d\.]+ (a|i)/i], + [[NAME, os => os == 'A' ? 'Android' : 'iOS' ]] + ] +}); + +//////////////////// +// IN-APP BROWSERS +/////////////////// + +const InApps = Object.freeze({ + browser : [[ + // Discord/Figma/Flipboard/Mattermost/Notion/Postman/Rambox/Rocket.Chat/Slack/Teams + /\b(discord|figma|mattermost|notion|postman|rambox|rocket.chat|slack|teams)\/([\w\.]+).+(electron\/|; ios)/i, + /(flipboard)\/([\w\.]+)/i + ], [NAME, VERSION, [TYPE, INAPP]], [ + + // Evernote/Teams on mobile + /(evernote) win/i, + /(teams)mobile-(ios|and)/i + ], [NAME, [TYPE, INAPP]], [ + + // Slack + /chatlyio\/([\d\.]+)/i], + [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], [ + + // TikTok Lite + /ultralite app_version\/([\w\.]+)/i], + [VERSION, [NAME, 'TikTok Lite'], [TYPE, INAPP]], [ + + // VS Code + /\) code\/([\d\.]+).+electron\//i], + [VERSION, [NAME, 'VS Code'], [TYPE, INAPP]], [ + + // Yahoo! Japan + /jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], + [VERSION, [NAME, 'Yahoo! Japan'], [TYPE, INAPP]] + ] +}); + +////////////////////// +// MEDIA PLAYER APPS +///////////////////// + +const MediaPlayers = Object.freeze({ + browser : [[ + /(apple(?:coremedia|tv))\/([\w\._]+)/i, // Generic Apple CoreMedia + /(coremedia) v([\w\._]+)/i, + // Ares/Nexplayer/OSSProxy + /(ares|clementine|music player daemon|nexplayer|ossproxy) ([\w\.-]+)/i, + // Aqualung/Lyssna/BSPlayer/Clementine/MPD + // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/GnomeMplayer/MoC + // NSPlayer/PSP-InternetRadioPlayer/Videos + // Nero Home/Nero Scout/Nokia + // QuickTime/RealMedia/RadioApp/RadioClientApplication/ + // SoundTap/Totem/Stagefright/Streamium + // XBMC/gvfs/Xine/XMMS/irapp + /^(aqualung|audacious|audimusicstream|amarok|bass|bsplayer|core|gnomemplayer|gvfs|irapp|lyssna|music on console|nero (?:home|scout)|nokia\d+|nsplayer|psp-internetradioplayer|quicktime|rma|radioapp|radioclientapplication|soundtap|stagefright|streamium|totem|videos|xbmc|xine|xmms)\/([\w\.-]+)/i, + /(lg player|nexplayer) ([\d\.]+)/i, + /player\/(nexplayer|lg player) ([\w\.-]+)/i, // NexPlayer/LG Player + /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i, // Gstreamer + /(htc streaming player) [\w_]+ \/ ([\d\.]+)/i, // HTC Streaming Player + /(lavf)([\d\.]+)/i, // Lavf (FFMPEG) + // MPlayer SVN + /(mplayer)(?: |\/)(?:(?:sherpya-){0,1}svn)(?:-| )(r\d+(?:-\d+[\w\.-]+))/i, + / (songbird)\/([\w\.-]+)/i, // Songbird/Philips-Songbird + /(winamp)(?:3 version|mpeg| ) ([\w\.-]+)/i, // Winamp + /(vlc)(?:\/| media player - version )([\w\.-]+)/i, // VLC Videolan + /^(foobar2000|itunes|smp)\/([\d\.]+)/i, // Foobar2000/iTunes/SMP + /com\.(riseupradioalarm)\/([\d\.]*)/i, // RiseUP Radio Alarm + /(mplayer)(?:\s|\/| unknown-)([\w\.\-]+)/i, // MPlayer + // Windows Media Server + /(windows)\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ home media server/i + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ + + /(flrp)\/([\w\.-]+)/i // Flip Player + ], [[NAME, 'Flip Player'], VERSION, [TYPE, MEDIAPLAYER]], [ + // FStream/NativeHost/QuerySeekSpider + // MPlayer (no other info)/Media Player Classic/Nero ShowTime + // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info) + // inlight radio / YourMuze + /(fstream|media player classic|inlight radio|mplayer|nativehost|nero showtime|ocms-bot|queryseekspider|tapinradio|tunein radio|winamp|yourmuze)/i + ], [NAME, [TYPE, MEDIAPLAYER]], [ + + /(htc_one_s|windows-media-player|wmplayer)\/([\w\.-]+)/i, // HTC One S / Windows Media Player + ], [[NAME, /[_-]/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ + + /(rad.io|radio.(?:de|at|fr)) ([\d\.]+)/i // Rad.io + ], [[NAME, 'rad.io'], VERSION, [TYPE, MEDIAPLAYER]] + ] +}); + +///////////// +// LIBRARIES +////////////// + +const Libraries = Object.freeze({ + browser : [ + // Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent + [ + /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|scrapy)\/([\w\.]+)/i, + /(adobeair|aiohttp|jsdom)\/([\w\.]+)/i, + /(nutch)-([\w\.-]+)(\(|$)/i, + /\((java)\/([\w\.]+)/i + ], [NAME, VERSION, [TYPE, LIBRARY]] + ] +}); + +///////////// +// VEHICLES +//////////// + +const Vehicles = Object.freeze({ + device : [ + [/aftlbt962e2/i], // BMW + [[VENDOR, 'BMW']], + + [/dilink.+(byd) auto/i], // BYD + [VENDOR], + + [/aftlft962x3/i], // Jeep + [[VENDOR, 'Jeep'], [MODEL, 'Wagooner']], + + [/(rivian) (r1t)/i], // Rivian + [VENDOR, MODEL], + + [/vcc.+netfront/i], // Volvo + [[VENDOR, 'Volvo']] + ] +}); + +////////// +// BOTS +///////// + +const Bots = Object.freeze({ + browser : [ + ...CLIs.browser, + ...Fetchers.browser, + ...Crawlers.browser, + ...Libraries.browser + ], + os : [ + ...Fetchers.os + ] +}); + +module.exports = { + Bots, + CLIs, + Crawlers, + ExtraDevices, + Emails, + Fetchers, + InApps, + Libraries, + MediaPlayers, + Vehicles +}; \ No newline at end of file diff --git a/src/extensions/ua-parser-extensions.mjs b/src/extensions/ua-parser-extensions.mjs new file mode 100644 index 000000000..9401493e4 --- /dev/null +++ b/src/extensions/ua-parser-extensions.mjs @@ -0,0 +1,431 @@ +// Generated ESM version of ua-parser-js/extensions +// DO NOT EDIT THIS FILE! +// Source: /src/extensions/ua-parser-extensions.js + +/////////////////////////////////////////////// +/* Extensions for UAParser.js v2.0.4 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + AGPLv3 License */ +////////////////////////////////////////////// + +/*jshint esversion: 6 */ + +const MODEL = 'model'; +const NAME = 'name'; +const TYPE = 'type'; +const VENDOR = 'vendor'; +const VERSION = 'version'; +const MOBILE = 'mobile'; +const TABLET = 'tablet'; +const CRAWLER = 'crawler'; +const CLI = 'cli'; +const EMAIL = 'email'; +const FETCHER = 'fetcher'; +const INAPP = 'inapp'; +const MEDIAPLAYER = 'mediaplayer'; +const LIBRARY = 'library'; + +////////////////////// +// COMMAND LINE APPS +///////////////////// + +const CLIs = Object.freeze({ + browser : [ + // wget / curl / Lynx / ELinks / HTTPie + [/(wget|curl|lynx|elinks|httpie)[\/ ]\(?([\w\.-]+)/i], [NAME, VERSION, [TYPE, CLI]] + ] +}); + +//////////////////////// +// CRAWLERS / SPIDERS +/////////////////////// + +const Crawlers = Object.freeze({ + browser : [ + [ + // AhrefsBot - https://ahrefs.com/robot + // Amazonbot - https://developer.amazon.com/amazonbot + // Bingbot / AdIdxBot - https://www.bing.com/webmasters/help/which-crawlers-does-bing-use-8c184ec0 + // CCBot - https://commoncrawl.org/faq + // Dotbot - https://moz.com/help/moz-procedures/crawlers/dotbot + // DuckDuckBot - http://duckduckgo.com/duckduckbot.html + // FacebookBot - https://developers.facebook.com/docs/sharing/bot/ + // GPTBot - https://platform.openai.com/docs/gptbot + // iAskBot - https://iask.ai + // LinkedInBot - http://www.linkedin.com + // MJ12bot - https://mj12bot.com/ + // MojeekBot - https://www.mojeek.com/bot.html + // Onespot - https://www.onespot.com/identifying-traffic.html + // OpenAI's SearchGPT - https://platform.openai.com/docs/bots + // PerplexityBot - https://perplexity.ai/perplexitybot + // SeznamBot - http://napoveda.seznam.cz/seznambot-intro + /((?:adidx|ahrefs|amazon|bing|cc|dot|duckduck|exa|facebook|gpt|iask|linkedin|mj12|mojeek|oai-search|onespot-scraper|perplexity|semrush|seznam)bot)\/([\w\.-]+)/i, + + // Applebot - http://apple.com/go/applebot + /(applebot(?:-extended)?)\/?([\w\.]*)/i, + + // Baiduspider https://help.baidu.com/question?prod_id=99&class=0&id=3001 + /(baiduspider[-imagevdonwsfcpr]{0,7})\/?([\w\.]*)/i, + + // ClaudeBot (Anthropic) + /(claude(?:bot|-web)|anthropic-ai)\/?([\w\.]*)/i, + + // Coc Coc Bot - https://help.coccoc.com/en/search-engine + /(coccocbot-(?:image|web))\/([\w\.]+)/i, + + // Daum + /(daum(?:oa)?(?:-image)?)[ \/]([\w\.]+)/i, + + // Facebook / Meta + // https://developers.facebook.com/docs/sharing/webmasters/web-crawlers + /(facebook(?:externalhit|catalog)|meta-externalagent)\/([\w\.]+)/i, + + // Googlebot - http://www.google.com/bot.html + /(google(?:bot|other|-inspectiontool)(?:-image|-video|-news)?|storebot-google)\/?([\w\.]*)/i, + + // Internet Archive (archive.org) + /(ia_archiver|archive\.org_bot)\/?([\w\.]*)/i, + + // Qwantbot - https://help.qwant.com/bot + /(qwantbot)[-\w]*\/?([\w\.]*)/i, + + // SemrushBot - http://www.semrush.com/bot.html + /((?:semrush|splitsignal)bot[-abcfimostw]*)\/?([\w\.-]*)/i, + + // Sogou Spider + /(sogou (?:pic|head|web|orion|news) spider)\/([\w\.]+)/i, + + // Yahoo! Japan - https://support.yahoo-net.jp/PccSearch/s/article/H000007955 + /(y!?j-(?:asr|br[uw]|dscv|mmp|vsidx|wsc))\/([\w\.]+)/i, + + // 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, + + // Yeti (Naver) + /(yeti)\/([\w\.]+)/i, + + // aiHitBot / Diffbot / Linespider / Magpie-Crawler / Omgilibot / OpenAI Image Downloader / Webzio-Extended / Screaming Frog SEO Spider / Startpage / Timpibot / VelenPublicWebCrawler / YisouSpider / YouBot + /((?:aihit|diff|timpi|you)bot|omgili(?:bot)?|openai image downloader|(?:magpie-|velenpublicweb)crawler|startpageprivateimageproxy|webzio-extended|(?:chatglm-|line|screaming frog seo |yisou)spider)\/?([\w\.]*)/i + ], + + [NAME, VERSION, [TYPE, CRAWLER]], + + [ + // Google Bots + /((?:adsbot|apis|mediapartners)-google(?:-mobile)?|google-?(?:other|cloudvertexbot|extended|safety))/i, + + // AI2Bot - https://allenai.org/crawler + // Bytespider + // DataForSeoBot - https://dataforseo.com/dataforseo-bot + // Huawei AspiegelBot / PetalBot https://aspiegel.com/petalbot + // ImagesiftBot - https://imagesift.com/about + // Qihoo 360Spider + // TurnitinBot - https://www.turnitin.com/robot/crawlerinfo.html + // Yahoo! Slurp - http://help.yahoo.com/help/us/ysearch/slurp + /\b(360spider-?(?:image|video)?|bytespider|(?:ai2|aspiegel|dataforseo|imagesift|petal|turnitin)bot|teoma|yahoo! slurp)/i + ], + [NAME, [TYPE, CRAWLER]] + ] +}); + +////////////////// +// EXTRA DEVICES +///////////////// + +const ExtraDevices = Object.freeze({ + device : [[ + /(nook)[\w ]+build\/(\w+)/i, // Nook + /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak + /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets + /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets + /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets + /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + + /(u304aa)/i // AT&T + ], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [ + + /\bsie-(\w*)/i // Siemens + ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ + + /\b(rct\w+) b/i // RCA Tablets + ], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [ + + /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets + ], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [ + + /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet + ], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [ + + /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet + ], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [ + + /\b(tm\d{3}\w+) b/i + ], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [ + + /\b(k88) b/i // ZTE K Series Tablet + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [ + + /\b(nx\d{3}j) b/i // ZTE Nubia + ], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [ + + /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [ + + /\b(zur\d{3}) b/i // Swiss ZUR Tablet + ], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [ + + /^((zeki)?tb.*\b) b/i // Zeki Tablets + ], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [ + + /\b([yr]\d{2}) b/i, + /\b(?:dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet + ], [MODEL, [VENDOR, 'Dragon Touch'], [TYPE, TABLET]], [ + + /\b(ns-?\w{0,9}) b/i // Insignia Tablets + ], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [ + + /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets + ], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [ + + /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones + ], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [ + + /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones + ], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [ + + /\b(ph-1) /i // Essential PH-1 + ], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [ + + /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets + ], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [ + + /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets + ], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [ + + /\btu_(1491) b/i // Rotor Tablets + ], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]] + ] +}); + +/////////////// +// EMAIL APPS +////////////// + +const Emails = Object.freeze({ + browser : [ + [ + // Evolution / Kontact/KMail[2] / [Microsoft/Mac] Outlook / Thunderbird + // Airmail / BlueMail / DaumMail / eMClient / Foxmail / NaverMailApp / Polymail + // ProtonMail / SparkDesktop / Sparrow / Yahoo! Mail / Zimbra / ZohoMail-Desktop + /((?:air|blue|daum|fox|poly|proton)mail|emclient|evolution|kmail2?|kontact|(?:microsoft |mac)?outlook(?:-express)?|navermailapp|(?!chrom.+)sparrow|sparkdesktop|thunderbird|yahoo|zohomail-desktop)(?:m.+ail; |[\/ ])([\w\.]+)/i, + + // Apple's Mail + /(mail)\/([\w\.]+) cf/i + ], [NAME, VERSION, [TYPE, EMAIL]], [ + + // Zimbra + /zdesktop\/([\w\.]+)/i + ], [VERSION, [NAME, 'Zimbra'], [TYPE, EMAIL]] + ] +}); + +/////////////////////// +// ON-DEMAND SCRAPERS +////////////////////// + +const Fetchers = Object.freeze({ + browser : [ + [ + // AhrefsSiteAudit - https://ahrefs.com/robot/site-audit + // ChatGPT-User - https://platform.openai.com/docs/plugins/bot + // DuckAssistBot - https://duckduckgo.com/duckassistbot/ + // Better Uptime / BingPreview / Mastodon / MicrosoftPreview / Pinterestbot / Redditbot / Rogerbot / SiteAuditBot / Telegrambot / Twitterbot / UptimeRobot + // Google Site Verifier / Meta / Yahoo! Japan + // Iframely - https://iframely.com/docs/about + // Perplexity-User - https://docs.perplexity.ai/guides/bots + // MistralAI-User - https://docs.mistral.ai/robots/ + // Yandex Bots - https://yandex.com/bots + /(ahrefssiteaudit|(?:bing|microsoft)preview|(?:chatgpt|mistralai|perplexity)-user|mastodon|(?:discord|duckassist|linkedin|pinterest|reddit|roger|siteaudit|twitter|uptimero)bot|google-site-verification|iframely|meta-externalfetcher|y!?j-dlc|yandex(?:calendar|direct(?:dyn)?|searchshop)|yadirectfetcher)\/([\w\.]+)/i, + + // Bluesky + /(bluesky) cardyb\/([\w\.]+)/i, + + // Skype + /(skypeuripreview) preview\/([\w\.]+)/i, + + // Slackbot - https://api.slack.com/robots + /(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i, + + // WhatsApp + /(whatsapp)\/([\w\.]+)/i + ], + [NAME, VERSION, [TYPE, FETCHER]], + + [ + // Google Bots / Cohere / Snapchat / Vercelbot / Yandex Bots + /((?:better uptime |telegram|vercel)bot|cohere-ai|feedfetcher-google|google(?:imageproxy|-read-aloud|-pagerenderer|producer)|snap url preview|yandex(?:sitelinks|userproxy))/i + ], + [NAME, [TYPE, FETCHER]], + ], + + os : [ + [/whatsapp\/[\d\.]+ (a|i)/i], + [[NAME, os => os == 'A' ? 'Android' : 'iOS' ]] + ] +}); + +//////////////////// +// IN-APP BROWSERS +/////////////////// + +const InApps = Object.freeze({ + browser : [[ + // Discord/Figma/Flipboard/Mattermost/Notion/Postman/Rambox/Rocket.Chat/Slack/Teams + /\b(discord|figma|mattermost|notion|postman|rambox|rocket.chat|slack|teams)\/([\w\.]+).+(electron\/|; ios)/i, + /(flipboard)\/([\w\.]+)/i + ], [NAME, VERSION, [TYPE, INAPP]], [ + + // Evernote/Teams on mobile + /(evernote) win/i, + /(teams)mobile-(ios|and)/i + ], [NAME, [TYPE, INAPP]], [ + + // Slack + /chatlyio\/([\d\.]+)/i], + [VERSION, [NAME, 'Slack'], [TYPE, INAPP]], [ + + // TikTok Lite + /ultralite app_version\/([\w\.]+)/i], + [VERSION, [NAME, 'TikTok Lite'], [TYPE, INAPP]], [ + + // VS Code + /\) code\/([\d\.]+).+electron\//i], + [VERSION, [NAME, 'VS Code'], [TYPE, INAPP]], [ + + // Yahoo! Japan + /jp\.co\.yahoo\.(?:android\.yjtop|ipn\.appli)\/([\d\.]+)/i], + [VERSION, [NAME, 'Yahoo! Japan'], [TYPE, INAPP]] + ] +}); + +////////////////////// +// MEDIA PLAYER APPS +///////////////////// + +const MediaPlayers = Object.freeze({ + browser : [[ + /(apple(?:coremedia|tv))\/([\w\._]+)/i, // Generic Apple CoreMedia + /(coremedia) v([\w\._]+)/i, + // Ares/Nexplayer/OSSProxy + /(ares|clementine|music player daemon|nexplayer|ossproxy) ([\w\.-]+)/i, + // Aqualung/Lyssna/BSPlayer/Clementine/MPD + // Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/GnomeMplayer/MoC + // NSPlayer/PSP-InternetRadioPlayer/Videos + // Nero Home/Nero Scout/Nokia + // QuickTime/RealMedia/RadioApp/RadioClientApplication/ + // SoundTap/Totem/Stagefright/Streamium + // XBMC/gvfs/Xine/XMMS/irapp + /^(aqualung|audacious|audimusicstream|amarok|bass|bsplayer|core|gnomemplayer|gvfs|irapp|lyssna|music on console|nero (?:home|scout)|nokia\d+|nsplayer|psp-internetradioplayer|quicktime|rma|radioapp|radioclientapplication|soundtap|stagefright|streamium|totem|videos|xbmc|xine|xmms)\/([\w\.-]+)/i, + /(lg player|nexplayer) ([\d\.]+)/i, + /player\/(nexplayer|lg player) ([\w\.-]+)/i, // NexPlayer/LG Player + /(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i, // Gstreamer + /(htc streaming player) [\w_]+ \/ ([\d\.]+)/i, // HTC Streaming Player + /(lavf)([\d\.]+)/i, // Lavf (FFMPEG) + // MPlayer SVN + /(mplayer)(?: |\/)(?:(?:sherpya-){0,1}svn)(?:-| )(r\d+(?:-\d+[\w\.-]+))/i, + / (songbird)\/([\w\.-]+)/i, // Songbird/Philips-Songbird + /(winamp)(?:3 version|mpeg| ) ([\w\.-]+)/i, // Winamp + /(vlc)(?:\/| media player - version )([\w\.-]+)/i, // VLC Videolan + /^(foobar2000|itunes|smp)\/([\d\.]+)/i, // Foobar2000/iTunes/SMP + /com\.(riseupradioalarm)\/([\d\.]*)/i, // RiseUP Radio Alarm + /(mplayer)(?:\s|\/| unknown-)([\w\.\-]+)/i, // MPlayer + // Windows Media Server + /(windows)\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ home media server/i + ], [NAME, VERSION, [TYPE, MEDIAPLAYER]], [ + + /(flrp)\/([\w\.-]+)/i // Flip Player + ], [[NAME, 'Flip Player'], VERSION, [TYPE, MEDIAPLAYER]], [ + // FStream/NativeHost/QuerySeekSpider + // MPlayer (no other info)/Media Player Classic/Nero ShowTime + // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info) + // inlight radio / YourMuze + /(fstream|media player classic|inlight radio|mplayer|nativehost|nero showtime|ocms-bot|queryseekspider|tapinradio|tunein radio|winamp|yourmuze)/i + ], [NAME, [TYPE, MEDIAPLAYER]], [ + + /(htc_one_s|windows-media-player|wmplayer)\/([\w\.-]+)/i, // HTC One S / Windows Media Player + ], [[NAME, /[_-]/g, ' '], VERSION, [TYPE, MEDIAPLAYER]], [ + + /(rad.io|radio.(?:de|at|fr)) ([\d\.]+)/i // Rad.io + ], [[NAME, 'rad.io'], VERSION, [TYPE, MEDIAPLAYER]] + ] +}); + +///////////// +// LIBRARIES +////////////// + +const Libraries = Object.freeze({ + browser : [ + // Apache-HttpClient/Axios/go-http-client/got/GuzzleHttp/Java[-HttpClient]/jsdom/libwww-perl/lua-resty-http/Needle/node-fetch/OkHttp/PHP-SOAP/PostmanRuntime/python-urllib/python-requests/Scrapy/superagent + [ + /^(apache-httpclient|axios|(?:go|java)-http-client|got|guzzlehttp|java|libwww-perl|lua-resty-http|needle|node-(?:fetch|superagent)|okhttp|php-soap|postmanruntime|python-(?:httpx|urllib[23]?|requests)|scrapy)\/([\w\.]+)/i, + /(adobeair|aiohttp|jsdom)\/([\w\.]+)/i, + /(nutch)-([\w\.-]+)(\(|$)/i, + /\((java)\/([\w\.]+)/i + ], [NAME, VERSION, [TYPE, LIBRARY]] + ] +}); + +///////////// +// VEHICLES +//////////// + +const Vehicles = Object.freeze({ + device : [ + [/aftlbt962e2/i], // BMW + [[VENDOR, 'BMW']], + + [/dilink.+(byd) auto/i], // BYD + [VENDOR], + + [/aftlft962x3/i], // Jeep + [[VENDOR, 'Jeep'], [MODEL, 'Wagooner']], + + [/(rivian) (r1t)/i], // Rivian + [VENDOR, MODEL], + + [/vcc.+netfront/i], // Volvo + [[VENDOR, 'Volvo']] + ] +}); + +////////// +// BOTS +///////// + +const Bots = Object.freeze({ + browser : [ + ...CLIs.browser, + ...Crawlers.browser, + ...Fetchers.browser, + ...Libraries.browser + ], + os : [ + ...Fetchers.os + ] +}); + +export { + Bots, + CLIs, + Crawlers, + ExtraDevices, + Emails, + Fetchers, + InApps, + Libraries, + MediaPlayers, + Vehicles +}; \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.d.ts b/src/helpers/ua-parser-helpers.d.ts new file mode 100644 index 000000000..fc53c614f --- /dev/null +++ b/src/helpers/ua-parser-helpers.d.ts @@ -0,0 +1,27 @@ +// Type definitions for Helpers submodule of UAParser.js v2.0.4 +// Project: https://github.com/faisalman/ua-parser-js +// Definitions by: Faisal Salman + +import type { IResult } from "../main/ua-parser"; + +declare function getDeviceVendor(model: string): string | undefined; +declare function isAppleSilicon(resultOrUA: IResult | string): boolean; +declare function isAIBot(resultOrUA: IResult | string): boolean; +declare function isBot(resultOrUA: IResult | string): boolean; +declare function isChromeFamily(resultOrUA: IResult | string): boolean; +declare function isElectron(): boolean; +declare function isFromEU(): boolean; +declare function isFrozenUA(ua: string): boolean; +declare function isStandalonePWA(): boolean; + +export { + getDeviceVendor, + isAppleSilicon, + isAIBot, + isBot, + isChromeFamily, + isElectron, + isFromEU, + isFrozenUA, + isStandalonePWA +} \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.js b/src/helpers/ua-parser-helpers.js new file mode 100644 index 000000000..912ccdb53 --- /dev/null +++ b/src/helpers/ua-parser-helpers.js @@ -0,0 +1,182 @@ +/////////////////////////////////////////////// +/* Helpers for UAParser.js v2.0.4 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + AGPLv3 License */ +////////////////////////////////////////////// + +/*jshint esversion: 6 */ + +const { UAParser } = require('../main/ua-parser'); +const { CPU, OS, Engine } = require('../enums/ua-parser-enums'); +const { Bots } = require('../extensions/ua-parser-extensions'); +const { isFromEU } = require('detect-europe-js'); +const { isFrozenUA } = require('ua-is-frozen'); +const { isStandalonePWA } = require('is-standalone-pwa'); + +const toResult = (value, head, ext) => typeof value === 'string' ? UAParser(value, head, ext) : value; + +const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; + +const isAppleSilicon = (resultOrUA) => { + const res = toResult(resultOrUA); + if (res.os.is(OS.MACOS)) { + if (res.cpu.is(CPU.ARM)) { + return true; + } + if (typeof resultOrUA !== 'string' && typeof window !== 'undefined') { + try { + const canvas = document.createElement('canvas'); + const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + const debug = webgl.getExtension('WEBGL_debug_renderer_info'); + const renderer = webgl.getParameter(debug.UNMASKED_RENDERER_WEBGL); + if (renderer.match(/apple m\d/i)) { + return true; + } + } catch { + return false; + } + } + } + return false; +} + +const isAIBot = (resultOrUA) => [ + + // AI2 + 'ai2bot', + + // Amazon + 'amazonbot', + + // Anthropic + 'anthropic-ai', + 'claude-web', + 'claude-searchbot', + 'claudebot', + + // Apple + 'applebot', + 'applebot-extended', + + // Brave + 'bravebot', + + // ByteDance + 'bytespider', + 'tiktokspider', + + // Cohere + 'cohere-training-data-crawler', + + // Common Crawl + 'ccbot', + + // Coveo + 'coveobot', + + // DataForSeo + 'dataforseobot', + + // DeepSeek + 'deepseekbot', + + // Diffbot + 'diffbot', + + // Google + 'googleother', + 'googleother-image', + 'googleother-video', + 'google-extended', + + // Hive AI + 'imagesiftbot', + + // Huawei + 'petalbot', + 'pangubot', + + // Hugging Face + 'huggingface-bot', + + // Kangaroo + 'kangaroo bot', + + // Mendable.ai + 'firecrawlagent', + + // Meta + 'facebookbot', + 'meta-externalagent', + + // OpenAI + 'gptbot', + 'oai-searchbot', + + // Perplexity + 'perplexitybot', + + // Replicate + 'replicate-bot', + + // Runpod + 'runpod-bot', + + // Semrush + 'semrushbot-ocob', + + // Timpi + 'timpibot', + + // Together AI + 'together-bot', + + // Velen.io + 'velenpublicwebcrawler', + + // Vercel + 'v0bot', + + // Webz.io + 'omgili', + 'omgilibot', + 'webzio-extended', + + // X + 'xai-bot', + + // You.com + 'youbot', + + // Zhipu AI + 'chatglm-spider', + + // Zyte + 'scrapy' + + ].includes(String(toResult(resultOrUA, Bots).browser.name).toLowerCase()); + +const isBot = (resultOrUA) => [ + 'cli', + 'crawler', + 'fetcher', + 'library' + ].includes(toResult(resultOrUA, Bots).browser.type); + +const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(Engine.BLINK); + +const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js + / electron\//i.test(navigator?.userAgent)); // browser + +module.exports = { + getDeviceVendor, + isAppleSilicon, + isAIBot, + isBot, + isChromeFamily, + isElectron, + isFromEU, + isFrozenUA, + isStandalonePWA +} \ No newline at end of file diff --git a/src/helpers/ua-parser-helpers.mjs b/src/helpers/ua-parser-helpers.mjs new file mode 100644 index 000000000..3530e7dbc --- /dev/null +++ b/src/helpers/ua-parser-helpers.mjs @@ -0,0 +1,147 @@ +// Generated ESM version of ua-parser-js/helpers +// DO NOT EDIT THIS FILE! +// Source: /src/helpers/ua-parser-helpers.js + +/////////////////////////////////////////////// +/* Helpers for UAParser.js v2.0.4 + https://github.com/faisalman/ua-parser-js + Author: Faisal Salman + AGPLv3 License */ +////////////////////////////////////////////// + +/*jshint esversion: 6 */ + +import { UAParser } from '../main/ua-parser.mjs'; +import { CPU, OS, Engine } from '../enums/ua-parser-enums.mjs'; +import { Bots } from '../extensions/ua-parser-extensions.mjs'; +import { isFromEU } from 'detect-europe-js'; +import { isFrozenUA } from 'ua-is-frozen'; +import { isStandalonePWA } from 'is-standalone-pwa'; + +const toResult = (value, head, ext) => typeof value === 'string' ? UAParser(value, head, ext) : value; + +const getDeviceVendor = (model) => UAParser(`Mozilla/5.0 (Linux; Android 10; ${model}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36`).device.vendor; + +const isAppleSilicon = (resultOrUA) => { + const res = toResult(resultOrUA); + if (res.os.is(OS.MACOS)) { + if (res.cpu.is(CPU.ARM)) { + return true; + } + if (typeof resultOrUA !== 'string' && typeof window !== 'undefined') { + try { + const canvas = document.createElement('canvas'); + const webgl = canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + const debug = webgl.getExtension('WEBGL_debug_renderer_info'); + const renderer = webgl.getParameter(debug.UNMASKED_RENDERER_WEBGL); + if (renderer.match(/apple m\d/i)) { + return true; + } + } catch { + return false; + } + } + } + return false; +} + +const isAIBot = (resultOrUA) => [ + + // AI2 + 'ai2bot', + + // Amazon + 'amazonbot', + + // Anthropic + 'anthropic-ai', + 'claude-web', + 'claudebot', + + // Apple + 'applebot', + 'applebot-extended', + + // ByteDance + 'bytespider', + + // Common Crawl + 'ccbot', + + // DataForSeo + 'dataforseobot', + + // Diffbot + 'diffbot', + + // Google + 'googleother', + 'googleother-image', + 'googleother-video', + 'google-extended', + + // Hive AI + 'imagesiftbot', + + // Huawei + 'petalbot', + + // Meta + 'facebookbot', + 'meta-externalagent', + + // OpenAI + 'gptbot', + 'oai-searchbot', + + // Perplexity + 'perplexitybot', + + // Semrush + 'semrushbot-ocob', + + // Timpi + 'timpibot', + + // Velen.io + 'velenpublicwebcrawler', + + // Webz.io + 'omgili', + 'omgilibot', + 'webzio-extended', + + // You.com + 'youbot', + + // Zhipu AI + 'chatglm-spider', + + // Zyte + 'scrapy' + + ].includes(String(toResult(resultOrUA, Bots).browser.name).toLowerCase()); + +const isBot = (resultOrUA) => [ + 'cli', + 'crawler', + 'fetcher', + 'library' + ].includes(toResult(resultOrUA, Bots).browser.type); + +const isChromeFamily = (resultOrUA) => toResult(resultOrUA).engine.is(Engine.BLINK); + +const isElectron = () => !!(process?.versions?.hasOwnProperty('electron') || // node.js + / electron\//i.test(navigator?.userAgent)); // browser + +export { + getDeviceVendor, + isAppleSilicon, + isAIBot, + isBot, + isChromeFamily, + isElectron, + isFromEU, + isFrozenUA, + isStandalonePWA +} \ No newline at end of file diff --git a/src/main/ua-parser.d.ts b/src/main/ua-parser.d.ts new file mode 100644 index 000000000..d1acf4bf5 --- /dev/null +++ b/src/main/ua-parser.d.ts @@ -0,0 +1,113 @@ +// Type definitions for UAParser.js v2.0.4 +// Project: https://github.com/faisalman/ua-parser-js +// Definitions by: Faisal Salman + +import type { Headers } from "undici"; +import type { IncomingHttpHeaders } from "undici/types/header"; + +declare namespace UAParser { + + interface IData { + is(val: string): boolean; + toString(): string; + withClientHints(): PromiseLike | T; + withFeatureCheck(): PromiseLike | T; + } + + interface IBrowser extends IData { + name?: string; + version?: string; + major?: string; + type?: 'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'mediaplayer' | 'library'; + } + + interface ICPU extends IData { + architecture?: 'ia32' | 'ia64' | 'amd64' | 'arm' | 'arm64' | 'armhf' | 'avr' | 'avr32' | 'irix' | 'irix64' | 'mips' | 'mips64' | '68k' | 'pa-risc' | 'ppc' | 'sparc' | 'sparc64'; + } + + interface IDevice extends IData { + type?: 'mobile' | 'tablet' | 'console' | 'smarttv' | 'wearable' | 'xr' | 'embedded'; + vendor?: string; + model?: string; + } + + interface IEngine extends IData { + name?: 'Amaya' | 'ArkWeb' | 'Blink' | 'EdgeHTML' | 'Flow' | 'Gecko' | 'Goanna' | 'iCab' | 'KHTML' | 'LibWeb' | 'Links' | 'Lynx' | 'NetFront' | 'NetSurf' | 'Presto' | 'Servo' | 'Tasman' | 'Trident' | 'w3m' | 'WebKit'; + version?: string; + } + + interface IOS extends IData { + name?: string; + version?: string; + } + + interface IResult extends IData { + ua: string; + browser: IBrowser; + cpu: ICPU; + device: IDevice; + engine: IEngine; + os: IOS; + } + + type RegexMap = ((RegExp | string | (string | RegExp | Function)[])[])[]; + type UAParserProps = 'browser' | 'cpu' | 'device' | 'engine' | 'os'; + type UAParserExt = Partial> | Partial>[]; + type UAParserHeaders = Record | IncomingHttpHeaders | Headers; + + export function UAParser(uastring?: string, extensions?: UAParserExt, headers?: UAParserHeaders): IResult; + export function UAParser(uastring?: string, headers?: UAParserHeaders): IResult; + export function UAParser(extensions?: UAParserExt, headers?: UAParserHeaders): IResult; + export function UAParser(headers?: UAParserHeaders): IResult; + + export class UAParser { + + static readonly BROWSER: { + NAME: 'name'; + VERSION: 'version'; + MAJOR: 'major'; + TYPE: 'type'; + }; + static readonly CPU: { + ARCHITECTURE: 'architecture'; + }; + static readonly DEVICE: { + TYPE: 'type'; + VENDOR: 'vendor'; + MODEL: 'model'; + CONSOLE: 'console'; + MOBILE: 'mobile'; + SMARTTV: 'smarttv'; + TABLET: 'tablet'; + WEARABLE: 'wearable'; + XR: 'xr'; + EMBEDDED: 'embedded'; + }; + static readonly ENGINE: { + NAME: 'name'; + VERSION: 'version'; + }; + static readonly OS: { + NAME: 'name'; + VERSION: 'version'; + }; + static readonly VERSION: string; + + constructor(uastring?: string, extensions?: UAParserExt, headers?: Record); + constructor(uastring?: string, headers?: Record); + constructor(extensions?: UAParserExt, headers?: Record); + constructor(headers?: Record); + + getUA(): string; + getBrowser(): IBrowser; + getCPU(): ICPU; + getDevice(): IDevice; + getEngine(): IEngine; + getOS(): IOS; + getResult(): IResult; + setUA(uastring: string): UAParser; + } +} + +export as namespace UAParser; +export = UAParser; \ No newline at end of file diff --git a/src/main/ua-parser.js b/src/main/ua-parser.js new file mode 100755 index 000000000..5f2d042b8 --- /dev/null +++ b/src/main/ua-parser.js @@ -0,0 +1,1505 @@ +///////////////////////////////////////////////////////////////////////////////// +/* UAParser.js v2.0.4 + Copyright Š 2012-2025 Faisal Salman + AGPLv3 License *//* + Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. + Supports browser & node.js environment. + Demo : https://uaparser.dev + Source : https://github.com/faisalman/ua-parser-js */ +///////////////////////////////////////////////////////////////////////////////// + +/* jshint esversion: 3 */ +/* globals window */ + +(function (window, undefined) { + + 'use strict'; + + ////////////// + // Constants + ///////////// + + var LIBVERSION = '2.0.4', + UA_MAX_LENGTH = 500, + USER_AGENT = 'user-agent', + EMPTY = '', + UNKNOWN = '?', + + // typeof + FUNC_TYPE = 'function', + UNDEF_TYPE = 'undefined', + OBJ_TYPE = 'object', + STR_TYPE = 'string', + + // properties + UA_BROWSER = 'browser', + UA_CPU = 'cpu', + UA_DEVICE = 'device', + UA_ENGINE = 'engine', + UA_OS = 'os', + UA_RESULT = 'result', + + NAME = 'name', + TYPE = 'type', + VENDOR = 'vendor', + VERSION = 'version', + ARCHITECTURE= 'architecture', + MAJOR = 'major', + MODEL = 'model', + + // device types + CONSOLE = 'console', + MOBILE = 'mobile', + TABLET = 'tablet', + SMARTTV = 'smarttv', + WEARABLE = 'wearable', + XR = 'xr', + EMBEDDED = 'embedded', + + // browser types + INAPP = 'inapp', + + // client hints + BRANDS = 'brands', + FORMFACTORS = 'formFactors', + FULLVERLIST = 'fullVersionList', + PLATFORM = 'platform', + PLATFORMVER = 'platformVersion', + BITNESS = 'bitness', + CH_HEADER = 'sec-ch-ua', + CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', + CH_HEADER_ARCH = CH_HEADER + '-arch', + CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, + CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors', + CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, + CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, + CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, + CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', + CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS], + + // device vendors + AMAZON = 'Amazon', + APPLE = 'Apple', + ASUS = 'ASUS', + BLACKBERRY = 'BlackBerry', + GOOGLE = 'Google', + HUAWEI = 'Huawei', + LENOVO = 'Lenovo', + HONOR = 'Honor', + LG = 'LG', + MICROSOFT = 'Microsoft', + MOTOROLA = 'Motorola', + NVIDIA = 'Nvidia', + ONEPLUS = 'OnePlus', + OPPO = 'OPPO', + SAMSUNG = 'Samsung', + SHARP = 'Sharp', + SONY = 'Sony', + XIAOMI = 'Xiaomi', + ZEBRA = 'Zebra', + + // browsers + CHROME = 'Chrome', + CHROMIUM = 'Chromium', + CHROMECAST = 'Chromecast', + EDGE = 'Edge', + FIREFOX = 'Firefox', + OPERA = 'Opera', + FACEBOOK = 'Facebook', + SOGOU = 'Sogou', + + PREFIX_MOBILE = 'Mobile ', + SUFFIX_BROWSER = ' Browser', + + // os + WINDOWS = 'Windows'; + + var isWindow = typeof window !== UNDEF_TYPE, + NAVIGATOR = (isWindow && window.navigator) ? + window.navigator : + undefined, + NAVIGATOR_UADATA = (NAVIGATOR && NAVIGATOR.userAgentData) ? + NAVIGATOR.userAgentData : + undefined; + + /////////// + // Helper + ////////// + + var extend = function (defaultRgx, extensions) { + var mergedRgx = {}; + var extraRgx = extensions; + 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] : []); + } + } + } + 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) { + var enums = {}; + for (var i=0; i 0) { + for (var i in str1) { + if (lowerize(str2) == lowerize(str1[i])) return true; + } + return false; + } + return isString(str1) ? lowerize(str2) == lowerize(str1) : false; + }, + isExtensions = function (obj, deep) { + for (var prop in obj) { + return /^(browser|cpu|device|engine|os)$/.test(prop) || (deep ? isExtensions(obj[prop]) : false); + } + }, + isString = function (val) { + return typeof val === STR_TYPE; + }, + itemListToArray = function (header) { + if (!header) return undefined; + var arr = []; + var tokens = strip(/\\?\"/g, header).split(','); + for (var i = 0; i < tokens.length; i++) { + if (tokens[i].indexOf(';') > -1) { + var token = trim(tokens[i]).split(';v='); + arr[i] = { brand : token[0], version : token[1] }; + } else { + arr[i] = trim(tokens[i]); + } + } + return arr; + }, + lowerize = function (str) { + return isString(str) ? str.toLowerCase() : str; + }, + majorize = function (version) { + return isString(version) ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; + }, + setProps = function (arr) { + for (var i in arr) { + if (!arr.hasOwnProperty(i)) continue; + + var propName = arr[i]; + if (typeof propName == OBJ_TYPE && propName.length == 2) { + this[propName[0]] = propName[1]; + } else { + this[propName] = undefined; + } + } + return this; + }, + strip = function (pattern, str) { + return isString(str) ? str.replace(pattern, EMPTY) : str; + }, + stripQuotes = function (str) { + return strip(/\\?\"/g, str); + }, + trim = function (str, len) { + if (isString(str)) { + str = strip(/^\s\s*/, str); + return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH); + } + }; + + /////////////// + // Map helper + ////////////// + + var rgxMapper = function (ua, arrays) { + + if(!ua || !arrays) return; + + var i = 0, j, k, p, q, matches, match; + + // loop through all regexes maps + while (i < arrays.length && !matches) { + + var regex = arrays[i], // even sequence (0,2,4,..) + props = arrays[i + 1]; // odd sequence (1,3,5,..) + j = k = 0; + + // try matching uastring with regexes + while (j < regex.length && !matches) { + + if (!regex[j]) { break; } + matches = regex[j++].exec(ua); + + if (!!matches) { + for (p = 0; p < props.length; p++) { + match = matches[++k]; + q = props[p]; + // check if given property is actually array + if (typeof q === OBJ_TYPE && q.length > 0) { + if (q.length === 2) { + if (typeof q[1] == FUNC_TYPE) { + // assign modified match + this[q[0]] = q[1].call(this, match); + } else { + // assign given value, ignore regex match + this[q[0]] = q[1]; + } + } else if (q.length >= 3) { + // Check whether q[1] FUNCTION or REGEX + if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) { + if (q.length > 3) { + this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined; + } else { + // call function (usually string mapper) + this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined; + } + } else { + if (q.length == 3) { + // sanitize match using given regex + this[q[0]] = match ? match.replace(q[1], q[2]) : undefined; + } else if (q.length == 4) { + this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined; + } else if (q.length > 4) { + this[q[0]] = match ? q[3].apply(this, [match.replace(q[1], q[2])].concat(q.slice(4))) : undefined; + } + } + } + } else { + this[q] = match ? match : undefined; + } + } + } + } + i += 2; + } + }, + + strMapper = function (str, map) { + + for (var i in map) { + // check if current value is array + if (typeof map[i] === OBJ_TYPE && map[i].length > 0) { + for (var j = 0; j < map[i].length; j++) { + if (has(map[i][j], str)) { + return (i === UNKNOWN) ? undefined : i; + } + } + } else if (has(map[i], str)) { + return (i === UNKNOWN) ? undefined : i; + } + } + return map.hasOwnProperty('*') ? map['*'] : str; + }; + + /////////////// + // String map + ////////////// + + var windowsVersionMap = { + 'ME' : '4.90', + 'NT 3.51': '3.51', + 'NT 4.0': '4.0', + '2000' : ['5.0', '5.01'], + 'XP' : ['5.1', '5.2'], + 'Vista' : '6.0', + '7' : '6.1', + '8' : '6.2', + '8.1' : '6.3', + '10' : ['6.4', '10.0'], + 'NT' : '' + }, + + formFactorsMap = { + 'embedded' : 'Automotive', + 'mobile' : 'Mobile', + 'tablet' : ['Tablet', 'EInk'], + 'smarttv' : 'TV', + 'wearable' : 'Watch', + 'xr' : ['VR', 'XR'], + '?' : ['Desktop', 'Unknown'], + '*' : undefined + }, + + browserHintsMap = { + 'Chrome' : 'Google Chrome', + 'Edge' : 'Microsoft Edge', + 'Edge WebView2' : 'Microsoft Edge WebView2', + 'Chrome WebView': 'Android WebView', + 'Chrome Headless':'HeadlessChrome', + 'Huawei Browser': 'HuaweiBrowser', + 'MIUI Browser' : 'Miui Browser', + 'Opera Mobi' : 'OperaMobile', + 'Yandex' : 'YaBrowser' + }; + + ////////////// + // Regex map + ///////////// + + var defaultRegexes = { + + browser : [[ + + // Most common regardless engine + /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS + ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ + /webview.+edge\/([\w\.]+)/i // Microsoft Edge + ], [VERSION, [NAME, EDGE+' WebView']], [ + /edg(?:e|ios|a)?\/([\w\.]+)/i + ], [VERSION, [NAME, 'Edge']], [ + + // Presto based + /(opera mini)\/([-\w\.]+)/i, // Opera Mini + /(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i, // Opera Mobi/Tablet + /(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i // Opera + ], [NAME, VERSION], [ + /opios[\/ ]+([\w\.]+)/i // Opera mini on iphone >= 8.0 + ], [VERSION, [NAME, OPERA+' Mini']], [ + /\bop(?:rg)?x\/([\w\.]+)/i // Opera GX + ], [VERSION, [NAME, OPERA+' GX']], [ + /\bopr\/([\w\.]+)/i // Opera Webkit + ], [VERSION, [NAME, OPERA]], [ + + // Mixed + /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu + ], [VERSION, [NAME, 'Baidu']], [ + /\b(?:mxbrowser|mxios|myie2)\/?([-\w\.]*)\b/i // Maxthon + ], [VERSION, [NAME, 'Maxthon']], [ + /(kindle)\/([\w\.]+)/i, // Kindle + /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i, + // Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir + // Trident based + /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet + /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer + + // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:lg |qute)browser)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon + /(heytap|ovi|115|surf)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf + /(ecosia|weibo)(?:__| \w+@)([\d\.]+)/i // Ecosia/Weibo + ], [NAME, VERSION], [ + /quark(?:pc)?\/([-\w\.]+)/i // Quark + ], [VERSION, [NAME, 'Quark']], [ + /\bddg\/([\w\.]+)/i // DuckDuckGo + ], [VERSION, [NAME, 'DuckDuckGo']], [ + /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser + ], [VERSION, [NAME, 'UCBrowser']], [ + /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser + /\bqbcore\/([\w\.]+).+microm/i, + /micromessenger\/([\w\.]+)/i // WeChat + ], [VERSION, [NAME, 'WeChat']], [ + /konqueror\/([\w\.]+)/i // Konqueror + ], [VERSION, [NAME, 'Konqueror']], [ + /trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i // IE11 + ], [VERSION, [NAME, 'IE']], [ + /ya(?:search)?browser\/([\w\.]+)/i // Yandex + ], [VERSION, [NAME, 'Yandex']], [ + /slbrowser\/([\w\.]+)/i // Smart Lenovo Browser + ], [VERSION, [NAME, 'Smart ' + LENOVO + SUFFIX_BROWSER]], [ + /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser + ], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [ + /\bfocus\/([\w\.]+)/i // Firefox Focus + ], [VERSION, [NAME, FIREFOX+' Focus']], [ + /\bopt\/([\w\.]+)/i // Opera Touch + ], [VERSION, [NAME, OPERA+' Touch']], [ + /coc_coc\w+\/([\w\.]+)/i // Coc Coc Browser + ], [VERSION, [NAME, 'Coc Coc']], [ + /dolfin\/([\w\.]+)/i // Dolphin + ], [VERSION, [NAME, 'Dolphin']], [ + /coast\/([\w\.]+)/i // Opera Coast + ], [VERSION, [NAME, OPERA+' Coast']], [ + /miuibrowser\/([\w\.]+)/i // MIUI Browser + ], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [ + /fxios\/([\w\.-]+)/i // Firefox for iOS + ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ + /\bqihoobrowser\/?([\w\.]*)/i // 360 + ], [VERSION, [NAME, '360']], [ + /\b(qq)\/([\w\.]+)/i // QQ + ], [[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 + ], [VERSION, [NAME, SAMSUNG + ' Internet']], [ + /metasr[\/ ]?([\d\.]+)/i // Sogou Explorer + ], [VERSION, [NAME, SOGOU + ' Explorer']], [ + /(sogou)mo\w+\/([\d\.]+)/i // Sogou Mobile + ], [[NAME, SOGOU + ' Mobile'], VERSION], [ + /(electron)\/([\w\.]+) safari/i, // Electron-based App + /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla + /m?(qqbrowser|2345(?=browser|chrome|explorer))\w*[\/ ]?v?([\w\.]+)/i // QQ/2345 + ], [NAME, VERSION], [ + /(lbbrowser|rekonq)/i // LieBao Browser/Rekonq + ], [NAME], [ + /ome\/([\w\.]+) \w* ?(iron) saf/i, // Iron + /ome\/([\w\.]+).+qihu (360)[es]e/i // 360 + ], [VERSION, NAME], [ + + // WebView + /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android + ], [[NAME, FACEBOOK], VERSION, [TYPE, INAPP]], [ + /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App + /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp + /(daum)apps[\/ ]([\w\.]+)/i, // Daum App + /safari (line)\/([\w\.]+)/i, // Line App for iOS + /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android + /(alipay)client\/([\w\.]+)/i, // Alipay + /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter + /(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna + ], [NAME, VERSION, [TYPE, INAPP]], [ + /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS + ], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [ + /musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok + ], [VERSION, [NAME, 'TikTok'], [TYPE, INAPP]], [ + /\[(linkedin)app\]/i // LinkedIn App for iOS & Android + ], [NAME, [TYPE, INAPP]], [ + /(zalo(?:app)?)[\/\sa-z]*([\w\.-]+)/i // Zalo + ], [[NAME, /(.+)/, 'Zalo'], VERSION, [TYPE, INAPP]], [ + + /(chromium)[\/ ]([-\w\.]+)/i // Chromium + ], [NAME, VERSION], [ + + /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless + ], [VERSION, [NAME, CHROME+' Headless']], [ + + /wv\).+chrome\/([\w\.]+).+edgw\//i // Edge WebView2 + ], [VERSION, [NAME, EDGE+' WebView2']], [ + + / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView + ], [[NAME, CHROME+' WebView'], VERSION], [ + + /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser + ], [VERSION, [NAME, 'Android' + SUFFIX_BROWSER]], [ + + /chrome\/([\w\.]+) mobile/i // Chrome Mobile + ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ + + /(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia + ], [NAME, VERSION], [ + + /version\/([\w\.\,]+) .*mobile(?:\/\w+ | ?)safari/i // Safari Mobile + ], [VERSION, [NAME, PREFIX_MOBILE + 'Safari']], [ + /iphone .*mobile(?:\/\w+ | ?)safari/i + ], [[NAME, PREFIX_MOBILE + 'Safari']], [ + /version\/([\w\.\,]+) .*(safari)/i // Safari + ], [VERSION, NAME], [ + /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 + ], [NAME, [VERSION, '1']], [ + + /(webkit|khtml)\/([\w\.]+)/i + ], [NAME, VERSION], [ + + // Gecko based + /(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile + ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ + /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape + ], [[NAME, 'Netscape'], VERSION], [ + /(wolvic|librewolf)\/([\w\.]+)/i // Wolvic/LibreWolf + ], [NAME, VERSION], [ + /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality + ], [VERSION, [NAME, FIREFOX+' Reality']], [ + /ekiohf.+(flow)\/([\w\.]+)/i, // Flow + /(swiftfox)/i, // Swiftfox + /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i, + // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror + /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i, + // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix + /(firefox)\/([\w\.]+)/i, // Other Firefox-based + /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla + + // Other + /(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, + // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser/Ladybird + /\b(links) \(([\w\.]+)/i // Links + ], [NAME, [VERSION, /_/g, '.']], [ + + /(cobalt)\/([\w\.]+)/i // Cobalt + ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]] + ], + + cpu : [[ + + /\b((amd|x|x86[-_]?|wow|win)64)\b/i // AMD64 (x64) + ], [[ARCHITECTURE, 'amd64']], [ + + /(ia32(?=;))/i, // IA32 (quicktime) + /\b((i[346]|x)86)(pc)?\b/i // IA32 (x86) + ], [[ARCHITECTURE, 'ia32']], [ + + /\b(aarch64|arm(v?[89]e?l?|_?64))\b/i // ARM64 + ], [[ARCHITECTURE, 'arm64']], [ + + /\b(arm(v[67])?ht?n?[fl]p?)\b/i // ARMHF + ], [[ARCHITECTURE, 'armhf']], [ + + // PocketPC mistakenly identified as PowerPC + /( (ce|mobile); ppc;|\/[\w\.]+arm\b)/i + ], [[ARCHITECTURE, 'arm']], [ + + / sun4\w[;\)]/i // SPARC + ], [[ARCHITECTURE, 'sparc']], [ + // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC + /\b(avr32|ia64(?=;)|68k(?=\))|\barm(?=v([1-7]|[5-7]1)l?|;|eabi)|(irix|mips|sparc)(64)?\b|pa-risc)/i, + /((ppc|powerpc)(64)?)( mac|;|\))/i, // PowerPC + /(?:osf1|[freopnt]{3,4}bsd) (alpha)/i // Alpha + ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [ + /winnt.+\[axp/i + ], [[ARCHITECTURE, 'alpha']] + ], + + device : [[ + + ////////////////////////// + // MOBILES & TABLETS + ///////////////////////// + + // Samsung + /\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i + ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [ + /\b((?:s[cgp]h|gt|sm)-(?![lr])\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, + /samsung[- ]((?!sm-[lr]|browser)[-\w]+)/i, + /sec-(sgh\w+)/i + ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ + + // Apple + /(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone + ], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [ + /\((ipad);[-\w\),; ]+apple/i, // iPad + /applecoremedia\/[\w\.]+ \((ipad)/i, + /\b(ipad)\d\d?,\d\d?[;\]].+ios/i + ], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [ + /(macintosh);/i + ], [MODEL, [VENDOR, APPLE]], [ + + // Sharp + /\b(sh-?[altvz]?\d\d[a-ekm]?)/i + ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [ + + // Honor + /\b((?:brt|eln|hey2?|gdi|jdn)-a?[lnw]09|(?:ag[rm]3?|jdn2|kob2)-a?[lw]0[09]hn)(?: bui|\)|;)/i + ], [MODEL, [VENDOR, HONOR], [TYPE, TABLET]], [ + /honor([-\w ]+)[;\)]/i + ], [MODEL, [VENDOR, HONOR], [TYPE, MOBILE]], [ + + // Huawei + /\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i + ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [ + /(?:huawei)([-\w ]+)[;\)]/i, + /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i + ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [ + + // Xiaomi + /oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i, + /\b((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets + ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [ + + /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO + /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models + /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi + /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi + /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models + /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i, // Xiaomi Mi + / ([\w ]+) miui\/v?\d/i + ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ + + // OnePlus + /droid.+; (cph2[3-6]\d[13579]|((gm|hd)19|(ac|be|in|kb)20|(d[en]|eb|le|mt)21|ne22)[0-2]\d|p[g-k]\w[1m]10)\b/i, + /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [ + + // OPPO + /; (\w+) bui.+ oppo/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]], [ + /\b(opd2(\d{3}a?))(?: bui|\))/i + ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['203', '304', '403', '404', '413', '415'], '*' : OPPO }], [TYPE, TABLET]], [ + + // BLU + /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i // Vivo series + ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ + + // Vivo + /; vivo (\w+)(?: bui|\))/i, + /\b(v[12]\d{3}\w?[at])(?: bui|;)/i + ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [ + + // Realme + /\b(rmx[1-3]\d{3})(?: bui|;|\))/i + ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [ + + // Lenovo + /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, + /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ + /lenovo[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, MOBILE]], [ + + // Motorola + /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i, + /\bmot(?:orola)?[- ]([\w\s]+)(\)| bui)/i, + /((?:moto(?! 360)[-\w\(\) ]+|xt\d{3,4}[cgkosw\+]?[-\d]*|nexus 6)(?= bui|\)))/i + ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [ + /\b(mz60\d|xoom[2 ]{0,2}) build\//i + ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [ + + // LG + /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i + ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [ + /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i, + /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch|webos))(\w+)/i, + /\blg-?([\d\w]+) bui/i + ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ + + // Nokia + /(nokia) (t[12][01])/i + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + /(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i, + /nokia[-_ ]?(([-\w\. ]*))/i + ], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [ + + // Google + /(pixel (c|tablet))\b/i // Google Pixel C/Tablet + ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [ + // Google Pixel + /droid.+;(?: google)? (g(01[13]a|020[aem]|025[jn]|1b60|1f8f|2ybb|4s1m|576d|5nz6|8hhn|8vou|a02099|c15s|d1yq|e2ae|ec77|gh2x|kv4x|p4bc|pj41|r83y|tt9q|ur25|wvk6)|pixel[\d ]*a?( pro)?( xl)?( fold)?( \(5g\))?)( bui|\))/i + ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ + /(google) (pixelbook( go)?)/i + ], [VENDOR, MODEL], [ + + // Sony + /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-\w\w\d\d)(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i + ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [ + /sony tablet [ps]/i, + /\b(?:sony)?sgp\w+(?: bui|\))/i + ], [[MODEL, 'Xperia Tablet'], [VENDOR, SONY], [TYPE, TABLET]], [ + + // Amazon + /(alexa)webm/i, + /(kf[a-z]{2}wi|aeo(?!bc)\w\w)( bui|\))/i, // Kindle Fire without Silk / Echo Show + /(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD + ], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [ + /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone + ], [[MODEL, /(.+)/g, 'Fire Phone $1'], [VENDOR, AMAZON], [TYPE, MOBILE]], [ + + // BlackBerry + /(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook + ], [MODEL, VENDOR, [TYPE, TABLET]], [ + /\b((?:bb[a-f]|st[hv])100-\d)/i, + /\(bb10; (\w+)/i // BlackBerry 10 + ], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [ + + // Asus + /(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i + ], [MODEL, [VENDOR, ASUS], [TYPE, TABLET]], [ + / (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i + ], [MODEL, [VENDOR, ASUS], [TYPE, MOBILE]], [ + + // HTC + /(nexus 9)/i // HTC Nexus 9 + ], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [ + /(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i, // HTC + + // ZTE + /(zte)[- ]([\w ]+?)(?: bui|\/|\))/i, + /(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony + ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [ + + // TCL + /tcl (xess p17aa)/i, + /droid [\w\.]+; ((?:8[14]9[16]|9(?:0(?:48|60|8[01])|1(?:3[27]|66)|2(?:6[69]|9[56])|466))[gqswx])(_\w(\w|\w\w))?(\)| bui)/i + ], [MODEL, [VENDOR, 'TCL'], [TYPE, TABLET]], [ + /droid [\w\.]+; (418(?:7d|8v)|5087z|5102l|61(?:02[dh]|25[adfh]|27[ai]|56[dh]|59k|65[ah])|a509dl|t(?:43(?:0w|1[adepqu])|50(?:6d|7[adju])|6(?:09dl|10k|12b|71[efho]|76[hjk])|7(?:66[ahju]|67[hw]|7[045][bh]|71[hk]|73o|76[ho]|79w|81[hks]?|82h|90[bhsy]|99b)|810[hs]))(_\w(\w|\w\w))?(\)| bui)/i + ], [MODEL, [VENDOR, 'TCL'], [TYPE, MOBILE]], [ + + // itel + /(itel) ((\w+))/i + ], [[VENDOR, lowerize], MODEL, [TYPE, strMapper, { 'tablet' : ['p10001l', 'w7001'], '*' : 'mobile' }]], [ + + // Acer + /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i + ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [ + + // Meizu + /droid.+; (m[1-5] note) bui/i, + /\bmz-([-\w]{2,})/i + ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [ + + // Ulefone + /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i + ], [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]], [ + + // Energizer + /; (energy ?\w+)(?: bui|\))/i, + /; energizer ([\w ]+)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Energizer'], [TYPE, MOBILE]], [ + + // Cat + /; cat (b35);/i, + /; (b15q?|s22 flip|s48c|s62 pro)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Cat'], [TYPE, MOBILE]], [ + + // Smartfren + /((?:new )?andromax[\w- ]+)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Smartfren'], [TYPE, MOBILE]], [ + + // Nothing + /droid.+; (a(in)?(0(15|59|6[35])|142)p?)/i + ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ + + // Archos + /; (x67 5g|tikeasy \w+|ac[1789]\d\w+)( b|\))/i, + /archos ?(5|gamepad2?|([\w ]*[t1789]|hello) ?\d+[\w ]*)( b|\))/i + ], [MODEL, [VENDOR, 'Archos'], [TYPE, TABLET]], [ + /archos ([\w ]+)( b|\))/i, + /; (ac[3-6]\d\w{2,8})( b|\))/i + ], [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]], [ + + // HMD + /; (n159v)/i + ], [MODEL, [VENDOR, 'HMD'], [TYPE, MOBILE]], [ + + // MIXED + /(imo) (tab \w+)/i, // IMO + /(infinix|tecno) (x1101b?|p904|dp(7c|8d|10a)( pro)?|p70[1-3]a?|p904|t1101)/i // Infinix XPad / Tecno + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i, + // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan + /; (blu|hmd|imo|infinix|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL + /(hp) ([\w ]+\w)/i, // HP iPAQ + /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia + /(oppo) ?([\w ]+) bui/i, // OPPO + /droid[^;]+; (philips)[_ ]([sv-x][\d]{3,4}[xz]?)/i // Philips + ], [VENDOR, MODEL, [TYPE, MOBILE]], [ + + /(kobo)\s(ereader|touch)/i, // Kobo + /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad + /(kindle)\/([\w\.]+)/i // Kindle + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + + /(surface duo)/i // Surface Duo + ], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [ + /droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone + ], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [ + /((?:tegranote|shield t(?!.+d tv))[\w- ]*?)(?: b|\))/i // Nvidia Tablets + ], [MODEL, [VENDOR, NVIDIA], [TYPE, TABLET]], [ + /(sprint) (\w+)/i // Sprint Phones + ], [VENDOR, MODEL, [TYPE, MOBILE]], [ + /(kin\.[onetw]{3})/i // Microsoft Kin + ], [[MODEL, /\./g, ' '], [VENDOR, MICROSOFT], [TYPE, MOBILE]], [ + /droid.+; ([c6]+|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra + ], [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]], [ + /droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i + ], [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], [ + + /////////////////// + // SMARTTVS + /////////////////// + + /(philips)[\w ]+tv/i, // Philips + /smart-tv.+(samsung)/i // Samsung + ], [VENDOR, [TYPE, SMARTTV]], [ + /hbbtv.+maple;(\d+)/i + ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ + /(vizio)(?: |.+model\/)(\w+-\w+)/i, // Vizio + /tcast.+(lg)e?. ([-\w]+)/i // LG SmartTV + ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ + /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i + ], [[VENDOR, LG], [TYPE, SMARTTV]], [ + /(apple) ?tv/i // Apple TV + ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ + /crkey.*devicetype\/chromecast/i // Google Chromecast Third Generation + ], [[MODEL, CHROMECAST+' Third Generation'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /crkey.*devicetype\/([^/]*)/i // Google Chromecast with specific device type + ], [[MODEL, /^/, 'Chromecast '], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /fuchsia.*crkey/i // Google Chromecast Nest Hub + ], [[MODEL, CHROMECAST+' Nest Hub'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /crkey/i // Google Chromecast, Linux-based or unknown + ], [[MODEL, CHROMECAST], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /(portaltv)/i // Facebook Portal TV + ], [MODEL, [VENDOR, FACEBOOK], [TYPE, SMARTTV]], [ + /droid.+aft(\w+)( bui|\))/i // Fire TV + ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ + /(shield \w+ tv)/i // Nvidia Shield TV + ], [MODEL, [VENDOR, NVIDIA], [TYPE, SMARTTV]], [ + /\(dtv[\);].+(aquos)/i, + /(aquos-tv[\w ]+)\)/i // Sharp + ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[ + /(bravia[\w ]+)( bui|\))/i // Sony + ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [ + /(mi(tv|box)-?\w+) bui/i // Xiaomi + ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ + /Hbbtv.*(technisat) (.*);/i // TechniSAT + ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ + /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku + /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices + ], [[VENDOR, /.+\/(\w+)/, '$1', strMapper, {'LG':'lge'}], [MODEL, trim], [TYPE, SMARTTV]], [ + + /////////////////// + // CONSOLES + /////////////////// + + /(playstation \w+)/i // Playstation + ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [ + /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox + ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [ + /(ouya)/i, // Ouya + /(nintendo) (\w+)/i, // Nintendo + /(retroid) (pocket ([^\)]+))/i // Retroid Pocket + ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ + /droid.+; (shield)( bui|\))/i // Nvidia Portable + ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [ + + /////////////////// + // WEARABLES + /////////////////// + + /\b(sm-[lr]\d\d[0156][fnuw]?s?|gear live)\b/i // Samsung Galaxy Watch + ], [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]], [ + /((pebble))app/i, // Pebble + /(asus|google|lg|oppo) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch + ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ + /(ow(?:19|20)?we?[1-3]{1,3})/i // Oppo Watch + ], [MODEL, [VENDOR, OPPO], [TYPE, WEARABLE]], [ + /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch + ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ + /(opwwe\d{3})/i // OnePlus Watch + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, WEARABLE]], [ + /(moto 360)/i // Motorola 360 + ], [MODEL, [VENDOR, MOTOROLA], [TYPE, WEARABLE]], [ + /(smartwatch 3)/i // Sony SmartWatch + ], [MODEL, [VENDOR, SONY], [TYPE, WEARABLE]], [ + /(g watch r)/i // LG G Watch R + ], [MODEL, [VENDOR, LG], [TYPE, WEARABLE]], [ + /droid.+; (wt63?0{2,3})\)/i + ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ + + /////////////////// + // XR + /////////////////// + + /droid.+; (glass) \d/i // Google Glass + ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ + /(pico) ([\w ]+) os\d/i // Pico + ], [VENDOR, MODEL, [TYPE, XR]], [ + /(quest( \d| pro)?s?).+vr/i // Meta Quest + ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ + /mobile vr; rv.+firefox/i // Unidentifiable VR device using Firefox Reality / Wolvic + ], [[TYPE, XR]], [ + + /////////////////// + // EMBEDDED + /////////////////// + + /(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i // Tesla + ], [VENDOR, [TYPE, EMBEDDED]], [ + /(aeobc)\b/i // Echo Dot + ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [ + /(homepod).+mac os/i // Apple HomePod + ], [MODEL, [VENDOR, APPLE], [TYPE, EMBEDDED]], [ + /windows iot/i // Unidentifiable embedded device using Windows IoT + ], [[TYPE, EMBEDDED]], [ + + //////////////////// + // MIXED (GENERIC) + /////////////////// + + /droid.+; ([\w- ]+) (4k|android|smart|google)[- ]?tv/i // Unidentifiable SmartTV + ], [MODEL, [TYPE, SMARTTV]], [ + /\b((4k|android|smart|opera)[- ]?tv|tv; rv:|large screen[\w ]+safari)\b/i + ], [[TYPE, SMARTTV]], [ + /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+?(mobile|vr|\d) safari/i + ], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [ + /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet + ], [[TYPE, TABLET]], [ + /(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i // Unidentifiable Mobile + ], [[TYPE, MOBILE]], [ + /droid .+?; ([\w\. -]+)( bui|\))/i // Generic Android Device + ], [MODEL, [VENDOR, 'Generic']] + ], + + engine : [[ + + /windows.+ edge\/([\w\.]+)/i // EdgeHTML + ], [VERSION, [NAME, EDGE+'HTML']], [ + + /(arkweb)\/([\w\.]+)/i // ArkWeb + ], [NAME, VERSION], [ + + /webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i // Blink + ], [VERSION, [NAME, 'Blink']], [ + + /(presto)\/([\w\.]+)/i, // Presto + /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna|servo)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna/Servo + /ekioh(flow)\/([\w\.]+)/i, // Flow + /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links + /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab + + /\b(libweb)/i // LibWeb + ], [NAME, VERSION], [ + /ladybird\//i + ], [[NAME, 'LibWeb']], [ + + /rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko + ], [VERSION, NAME] + ], + + os : [[ + + // Windows + /(windows nt) (6\.[23]); arm/i // Windows RT + ], [[NAME, /N/, 'R'], [VERSION, strMapper, windowsVersionMap]], [ + /(windows (?:phone|mobile|iot))(?: os)?[\/ ]?([\d\.]*( se)?)/i, // Windows IoT/Mobile/Phone + // Windows NT/3.1/95/98/ME/2000/XP/Vista/7/8/8.1/10/11 + /(windows)[\/ ](1[01]|2000|3\.1|7|8(\.1)?|9[58]|me|server 20\d\d( r2)?|vista|xp)/i + ], [NAME, VERSION], [ + /windows nt ?([\d\.\)]*)(?!.+xbox)/i, + /\bwin(?=3| ?9|n)(?:nt| 9x )?([\d\.;]*)/i + ], [[VERSION, /(;|\))/g, '', strMapper, windowsVersionMap], [NAME, WINDOWS]], [ + /(windows ce)\/?([\d\.]*)/i // Windows CE + ], [NAME, VERSION], [ + + // iOS/macOS + /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS + /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i, + /cfnetwork\/.+darwin/i + ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ + /(mac os x) ?([\w\. ]*)/i, + /(macintosh|mac_powerpc\b)(?!.+(haiku|morphos))/i // Mac OS + ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [ + + // Google Chromecast + /android ([\d\.]+).*crkey/i // Google Chromecast, Android-based + ], [VERSION, [NAME, CHROMECAST + ' Android']], [ + /fuchsia.*crkey\/([\d\.]+)/i // Google Chromecast, Fuchsia-based + ], [VERSION, [NAME, CHROMECAST + ' Fuchsia']], [ + /crkey\/([\d\.]+).*devicetype\/smartspeaker/i // Google Chromecast, Linux-based Smart Speaker + ], [VERSION, [NAME, CHROMECAST + ' SmartSpeaker']], [ + /linux.*crkey\/([\d\.]+)/i // Google Chromecast, Legacy Linux-based + ], [VERSION, [NAME, CHROMECAST + ' Linux']], [ + /crkey\/([\d\.]+)/i // Google Chromecast, unknown + ], [VERSION, [NAME, CHROMECAST]], [ + + // Mobile OSes + /droid ([\w\.]+)\b.+(android[- ]x86)/i // Android-x86 + ], [VERSION, NAME], [ + /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch + ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ + /(harmonyos)[\/ ]?([\d\.]*)/i, // HarmonyOS + // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen + /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen)\w*[-\/\.; ]?([\d\.]*)/i + ], [NAME, VERSION], [ + /\(bb(10);/i // BlackBerry 10 + ], [VERSION, [NAME, BLACKBERRY]], [ + /(?:symbian ?os|symbos|s60(?=;)|series ?60)[-\/ ]?([\w\.]*)/i // Symbian + ], [VERSION, [NAME, 'Symbian']], [ + /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS + ], [VERSION, [NAME, FIREFOX+' OS']], [ + /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS + /webos(?:[ \/]?|\.tv-20(?=2[2-9]))(\d[\d\.]*)/i + ], [VERSION, [NAME, 'webOS']], [ + /web0s;.+?(?:chr[o0]me|safari)\/(\d+)/i + // https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine + ], [[VERSION, strMapper, {'25':'120','24':'108','23':'94','22':'87','6':'79','5':'68','4':'53','3':'38','2':'538','1':'537','*':'TV'}], [NAME, 'webOS']], [ + /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS + ], [VERSION, [NAME, 'watchOS']], [ + + // Google ChromeOS + /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS + ], [[NAME, "Chrome OS"], VERSION],[ + + // Smart TVs + /panasonic;(viera)/i, // Panasonic Viera + /(netrange)mmh/i, // Netrange + /(nettv)\/(\d+\.[\w\.]+)/i, // NetTV + + // Console + /(nintendo|playstation) (\w+)/i, // Nintendo/Playstation + /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S) + /(pico) .+os([\w\.]+)/i, // Pico + + // Other + /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm + /linux.+(mint)[\/\(\) ]?([\w\.]*)/i, // Mint + /(mageia|vectorlinux|fuchsia|arcaos|arch(?= ?linux))[;l ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS/Arch + /([kxln]?ubuntu|debian|suse|opensuse|gentoo|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire|knoppix)(?: gnu[\/ ]linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, + // Ubuntu/Debian/SUSE/Gentoo/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire/Knoppix + /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris + /\b(aix)[; ]([1-9\.]{0,4})/i, // AIX + /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS + /(gnu) ?([\w\.]*)/i, // GNU + /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly + /(haiku) ?(r\d)?/i // Haiku + ], [NAME, VERSION], [ + /(sunos) ?([\d\.]*)/i // Solaris + ], [[NAME, 'Solaris'], VERSION], [ + /\b(beos|os\/2|amigaos|openvms|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/OpenVMS/HP-UX/SerenityOS + /(unix) ?([\w\.]*)/i // UNIX + ], [NAME, VERSION] + ] + }; + + ///////////////// + // Factories + //////////////// + + var defaultProps = (function () { + var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; + setProps.call(props.init, [ + [UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]], + [UA_CPU, [ARCHITECTURE]], + [UA_DEVICE, [TYPE, MODEL, VENDOR]], + [UA_ENGINE, [NAME, VERSION]], + [UA_OS, [NAME, VERSION]] + ]); + setProps.call(props.isIgnore, [ + [UA_BROWSER, [VERSION, MAJOR]], + [UA_ENGINE, [VERSION]], + [UA_OS, [VERSION]] + ]); + setProps.call(props.isIgnoreRgx, [ + [UA_BROWSER, / ?browser$/i], + [UA_OS, / ?os$/i] + ]); + setProps.call(props.toString, [ + [UA_BROWSER, [NAME, VERSION]], + [UA_CPU, [ARCHITECTURE]], + [UA_DEVICE, [VENDOR, MODEL]], + [UA_ENGINE, [NAME, VERSION]], + [UA_OS, [NAME, VERSION]] + ]); + return props; + })(); + + var createIData = function (item, itemType) { + + var init_props = defaultProps.init[itemType], + is_ignoreProps = defaultProps.isIgnore[itemType] || 0, + is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0, + toString_props = defaultProps.toString[itemType] || 0; + + function IData () { + setProps.call(this, init_props); + } + + IData.prototype.getItem = function () { + return item; + }; + + IData.prototype.withClientHints = function () { + + // nodejs / non-client-hints browsers + if (!NAVIGATOR_UADATA) { + return item + .parseCH() + .get(); + } + + // browsers based on chromium 85+ + return NAVIGATOR_UADATA + .getHighEntropyValues(CH_ALL_VALUES) + .then(function (res) { + return item + .setCH(new UACHData(res, false)) + .parseCH() + .get(); + }); + }; + + IData.prototype.withFeatureCheck = function () { + return item.detectFeature().get(); + }; + + if (itemType != UA_RESULT) { + IData.prototype.is = function (strToCheck) { + var is = false; + for (var i in this) { + if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) { + is = true; + if (strToCheck != UNDEF_TYPE) break; + } else if (strToCheck == UNDEF_TYPE && is) { + is = !is; + break; + } + } + return is; + }; + IData.prototype.toString = function () { + var str = EMPTY; + for (var i in toString_props) { + if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { + str += (str ? ' ' : EMPTY) + this[toString_props[i]]; + } + } + return str || UNDEF_TYPE; + }; + } + + if (!NAVIGATOR_UADATA) { + IData.prototype.then = function (cb) { + var that = this; + var IDataResolve = function () { + for (var prop in that) { + if (that.hasOwnProperty(prop)) { + this[prop] = that[prop]; + } + } + }; + IDataResolve.prototype = { + is : IData.prototype.is, + toString : IData.prototype.toString + }; + var resolveData = new IDataResolve(); + cb(resolveData); + return resolveData; + }; + } + + return new IData(); + }; + + ///////////////// + // Constructor + //////////////// + + function UACHData (uach, isHttpUACH) { + uach = uach || {}; + setProps.call(this, CH_ALL_VALUES); + if (isHttpUACH) { + setProps.call(this, [ + [BRANDS, itemListToArray(uach[CH_HEADER])], + [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], + [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])], + [MODEL, stripQuotes(uach[CH_HEADER_MODEL])], + [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], + [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], + [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], + [FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])], + [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] + ]); + } else { + for (var prop in uach) { + if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop]; + } + } + } + + function UAItem (itemType, ua, rgxMap, uaCH) { + + this.get = function (prop) { + if (!prop) return this.data; + return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; + }; + + this.set = function (prop, val) { + this.data[prop] = val; + return this; + }; + + this.setCH = function (ch) { + this.uaCH = ch; + return this; + }; + + this.detectFeature = function () { + if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) { + switch (this.itemType) { + case UA_BROWSER: + // Brave-specific detection + if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + this.set(NAME, 'Brave'); + } + break; + case UA_DEVICE: + // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData + if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { + this.set(TYPE, MOBILE); + } + // iPadOS-specific detection: identified as Mac, but has some iOS-only properties + if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + this.set(MODEL, 'iPad') + .set(TYPE, TABLET); + } + break; + case UA_OS: + // Chrome-specific detection: check for 'platform' value of navigator.userAgentData + if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { + this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); + } + break; + case UA_RESULT: + var data = this.data; + var detect = function (itemType) { + return data[itemType] + .getItem() + .detectFeature() + .get(); + }; + this.set(UA_BROWSER, detect(UA_BROWSER)) + .set(UA_CPU, detect(UA_CPU)) + .set(UA_DEVICE, detect(UA_DEVICE)) + .set(UA_ENGINE, detect(UA_ENGINE)) + .set(UA_OS, detect(UA_OS)); + } + } + return this; + }; + + this.parseUA = function () { + if (this.itemType != UA_RESULT) { + rgxMapper.call(this.data, this.ua, this.rgxMap); + } + if (this.itemType == UA_BROWSER) { + this.set(MAJOR, majorize(this.get(VERSION))); + } + return this; + }; + + this.parseCH = function () { + var uaCH = this.uaCH, + rgxMap = this.rgxMap; + + switch (this.itemType) { + case UA_BROWSER: + case UA_ENGINE: + var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName; + if (brands) { + for (var i=0; i= 13 ? '11' : '10'); + this.set(NAME, osName) + .set(VERSION, osVersion); + } + // Xbox-Specific Detection + if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') { + this.set(NAME, 'Xbox') + .set(VERSION, undefined); + } + break; + case UA_RESULT: + var data = this.data; + var parse = function (itemType) { + return data[itemType] + .getItem() + .setCH(uaCH) + .parseCH() + .get(); + }; + this.set(UA_BROWSER, parse(UA_BROWSER)) + .set(UA_CPU, parse(UA_CPU)) + .set(UA_DEVICE, parse(UA_DEVICE)) + .set(UA_ENGINE, parse(UA_ENGINE)) + .set(UA_OS, parse(UA_OS)); + } + return this; + }; + + setProps.call(this, [ + ['itemType', itemType], + ['ua', ua], + ['uaCH', uaCH], + ['rgxMap', rgxMap], + ['data', createIData(this, itemType)] + ]); + + return this; + } + + function UAParser (ua, extensions, headers) { + + if (typeof ua === OBJ_TYPE) { + if (isExtensions(ua, true)) { + if (typeof extensions === OBJ_TYPE) { + headers = extensions; // case UAParser(extensions, headers) + } + extensions = ua; // case UAParser(extensions) + } else { + headers = ua; // case UAParser(headers) + extensions = undefined; + } + ua = undefined; + } else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) { + headers = extensions; // case UAParser(ua, headers) + extensions = undefined; + } + + // Convert Headers object into a plain object + if (headers && typeof headers.append === FUNC_TYPE) { + var kv = {}; + headers.forEach(function (v, k) { kv[k] = v; }); + headers = kv; + } + + if (!(this instanceof UAParser)) { + return new UAParser(ua, extensions, headers).getResult(); + } + + var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string + (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers + ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent + EMPTY)), // empty string + + httpUACH = new UACHData(headers, true), + regexMap = extensions ? + extend(defaultRegexes, extensions) : + defaultRegexes, + + createItemFunc = function (itemType) { + if (itemType == UA_RESULT) { + return function () { + return new UAItem(itemType, userAgent, regexMap, httpUACH) + .set('ua', userAgent) + .set(UA_BROWSER, this.getBrowser()) + .set(UA_CPU, this.getCPU()) + .set(UA_DEVICE, this.getDevice()) + .set(UA_ENGINE, this.getEngine()) + .set(UA_OS, this.getOS()) + .get(); + }; + } else { + return function () { + return new UAItem(itemType, userAgent, regexMap[itemType], httpUACH) + .parseUA() + .get(); + }; + } + }; + + // public methods + setProps.call(this, [ + ['getBrowser', createItemFunc(UA_BROWSER)], + ['getCPU', createItemFunc(UA_CPU)], + ['getDevice', createItemFunc(UA_DEVICE)], + ['getEngine', createItemFunc(UA_ENGINE)], + ['getOS', createItemFunc(UA_OS)], + ['getResult', createItemFunc(UA_RESULT)], + ['getUA', function () { return userAgent; }], + ['setUA', function (ua) { + if (isString(ua)) + userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua; + return this; + }] + ]) + .setUA(userAgent); + + return this; + } + + UAParser.VERSION = LIBVERSION; + UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR, TYPE]); + UAParser.CPU = enumerize([ARCHITECTURE]); + UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]); + UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]); + + /////////// + // Export + ////////// + + // check js environment + if (typeof exports !== UNDEF_TYPE) { + // nodejs env + if (typeof module !== UNDEF_TYPE && module.exports) { + exports = module.exports = UAParser; + } + exports.UAParser = UAParser; + } else { + // requirejs env (optional) + if (typeof define === FUNC_TYPE && define.amd) { + define(function () { + return UAParser; + }); + } else if (isWindow) { + // browser env + window.UAParser = UAParser; + } + } + + // jQuery/Zepto specific (optional) + // Note: + // In AMD env the global scope should be kept clean, but jQuery is an exception. + // jQuery always exports to global scope, unless jQuery.noConflict(true) is used, + // and we should catch that. + var $ = isWindow && (window.jQuery || window.Zepto); + if ($ && !$.ua) { + var parser = new UAParser(); + $.ua = parser.getResult(); + $.ua.get = function () { + return parser.getUA(); + }; + $.ua.set = function (ua) { + parser.setUA(ua); + var result = parser.getResult(); + for (var prop in result) { + $.ua[prop] = result[prop]; + } + }; + } + +})(typeof window === 'object' ? window : this); diff --git a/src/main/ua-parser.mjs b/src/main/ua-parser.mjs new file mode 100644 index 000000000..2b45cda66 --- /dev/null +++ b/src/main/ua-parser.mjs @@ -0,0 +1,1460 @@ +// Generated ESM version of ua-parser-js +// DO NOT EDIT THIS FILE! +// Source: /src/main/ua-parser.js + +///////////////////////////////////////////////////////////////////////////////// +/* UAParser.js v2.0.4 + Copyright Š 2012-2025 Faisal Salman + AGPLv3 License *//* + Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. + Supports browser & node.js environment. + Demo : https://uaparser.dev + Source : https://github.com/faisalman/ua-parser-js */ +///////////////////////////////////////////////////////////////////////////////// + +/* jshint esversion: 6 */ +/* globals window */ + + + + ////////////// + // Constants + ///////////// + + var LIBVERSION = '2.0.4', + UA_MAX_LENGTH = 500, + USER_AGENT = 'user-agent', + EMPTY = '', + UNKNOWN = '?', + + // typeof + FUNC_TYPE = 'function', + UNDEF_TYPE = 'undefined', + OBJ_TYPE = 'object', + STR_TYPE = 'string', + + // properties + UA_BROWSER = 'browser', + UA_CPU = 'cpu', + UA_DEVICE = 'device', + UA_ENGINE = 'engine', + UA_OS = 'os', + UA_RESULT = 'result', + + NAME = 'name', + TYPE = 'type', + VENDOR = 'vendor', + VERSION = 'version', + ARCHITECTURE= 'architecture', + MAJOR = 'major', + MODEL = 'model', + + // device types + CONSOLE = 'console', + MOBILE = 'mobile', + TABLET = 'tablet', + SMARTTV = 'smarttv', + WEARABLE = 'wearable', + XR = 'xr', + EMBEDDED = 'embedded', + + // browser types + INAPP = 'inapp', + + // client hints + BRANDS = 'brands', + FORMFACTORS = 'formFactors', + FULLVERLIST = 'fullVersionList', + PLATFORM = 'platform', + PLATFORMVER = 'platformVersion', + BITNESS = 'bitness', + CH_HEADER = 'sec-ch-ua', + CH_HEADER_FULL_VER_LIST = CH_HEADER + '-full-version-list', + CH_HEADER_ARCH = CH_HEADER + '-arch', + CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS, + CH_HEADER_FORM_FACTORS = CH_HEADER + '-form-factors', + CH_HEADER_MOBILE = CH_HEADER + '-' + MOBILE, + CH_HEADER_MODEL = CH_HEADER + '-' + MODEL, + CH_HEADER_PLATFORM = CH_HEADER + '-' + PLATFORM, + CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version', + CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS], + + // device vendors + AMAZON = 'Amazon', + APPLE = 'Apple', + ASUS = 'ASUS', + BLACKBERRY = 'BlackBerry', + GOOGLE = 'Google', + HUAWEI = 'Huawei', + LENOVO = 'Lenovo', + HONOR = 'Honor', + LG = 'LG', + MICROSOFT = 'Microsoft', + MOTOROLA = 'Motorola', + NVIDIA = 'Nvidia', + ONEPLUS = 'OnePlus', + OPPO = 'OPPO', + SAMSUNG = 'Samsung', + SHARP = 'Sharp', + SONY = 'Sony', + XIAOMI = 'Xiaomi', + ZEBRA = 'Zebra', + + // browsers + CHROME = 'Chrome', + CHROMIUM = 'Chromium', + CHROMECAST = 'Chromecast', + EDGE = 'Edge', + FIREFOX = 'Firefox', + OPERA = 'Opera', + FACEBOOK = 'Facebook', + SOGOU = 'Sogou', + + PREFIX_MOBILE = 'Mobile ', + SUFFIX_BROWSER = ' Browser', + + // os + WINDOWS = 'Windows'; + + var isWindow = typeof window !== UNDEF_TYPE, + NAVIGATOR = (isWindow && window.navigator) ? + window.navigator : + undefined, + NAVIGATOR_UADATA = (NAVIGATOR && NAVIGATOR.userAgentData) ? + NAVIGATOR.userAgentData : + undefined; + + /////////// + // Helper + ////////// + + var extend = function (defaultRgx, extensions) { + var mergedRgx = {}; + var extraRgx = extensions; + 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] : []); + } + } + } + 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) { + var enums = {}; + for (var i=0; i 0) { + for (var i in str1) { + if (lowerize(str2) == lowerize(str1[i])) return true; + } + return false; + } + return isString(str1) ? lowerize(str2) == lowerize(str1) : false; + }, + isExtensions = function (obj, deep) { + for (var prop in obj) { + return /^(browser|cpu|device|engine|os)$/.test(prop) || (deep ? isExtensions(obj[prop]) : false); + } + }, + isString = function (val) { + return typeof val === STR_TYPE; + }, + itemListToArray = function (header) { + if (!header) return undefined; + var arr = []; + var tokens = strip(/\\?\"/g, header).split(','); + for (var i = 0; i < tokens.length; i++) { + if (tokens[i].indexOf(';') > -1) { + var token = trim(tokens[i]).split(';v='); + arr[i] = { brand : token[0], version : token[1] }; + } else { + arr[i] = trim(tokens[i]); + } + } + return arr; + }, + lowerize = function (str) { + return isString(str) ? str.toLowerCase() : str; + }, + majorize = function (version) { + return isString(version) ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; + }, + setProps = function (arr) { + for (var i in arr) { + if (!arr.hasOwnProperty(i)) continue; + + var propName = arr[i]; + if (typeof propName == OBJ_TYPE && propName.length == 2) { + this[propName[0]] = propName[1]; + } else { + this[propName] = undefined; + } + } + return this; + }, + strip = function (pattern, str) { + return isString(str) ? str.replace(pattern, EMPTY) : str; + }, + stripQuotes = function (str) { + return strip(/\\?\"/g, str); + }, + trim = function (str, len) { + if (isString(str)) { + str = strip(/^\s\s*/, str); + return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH); + } + }; + + /////////////// + // Map helper + ////////////// + + var rgxMapper = function (ua, arrays) { + + if(!ua || !arrays) return; + + var i = 0, j, k, p, q, matches, match; + + // loop through all regexes maps + while (i < arrays.length && !matches) { + + var regex = arrays[i], // even sequence (0,2,4,..) + props = arrays[i + 1]; // odd sequence (1,3,5,..) + j = k = 0; + + // try matching uastring with regexes + while (j < regex.length && !matches) { + + if (!regex[j]) { break; } + matches = regex[j++].exec(ua); + + if (!!matches) { + for (p = 0; p < props.length; p++) { + match = matches[++k]; + q = props[p]; + // check if given property is actually array + if (typeof q === OBJ_TYPE && q.length > 0) { + if (q.length === 2) { + if (typeof q[1] == FUNC_TYPE) { + // assign modified match + this[q[0]] = q[1].call(this, match); + } else { + // assign given value, ignore regex match + this[q[0]] = q[1]; + } + } else if (q.length >= 3) { + // Check whether q[1] FUNCTION or REGEX + if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) { + if (q.length > 3) { + this[q[0]] = match ? q[1].apply(this, q.slice(2)) : undefined; + } else { + // call function (usually string mapper) + this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined; + } + } else { + if (q.length == 3) { + // sanitize match using given regex + this[q[0]] = match ? match.replace(q[1], q[2]) : undefined; + } else if (q.length == 4) { + this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined; + } else if (q.length > 4) { + this[q[0]] = match ? q[3].apply(this, [match.replace(q[1], q[2])].concat(q.slice(4))) : undefined; + } + } + } + } else { + this[q] = match ? match : undefined; + } + } + } + } + i += 2; + } + }, + + strMapper = function (str, map) { + + for (var i in map) { + // check if current value is array + if (typeof map[i] === OBJ_TYPE && map[i].length > 0) { + for (var j = 0; j < map[i].length; j++) { + if (has(map[i][j], str)) { + return (i === UNKNOWN) ? undefined : i; + } + } + } else if (has(map[i], str)) { + return (i === UNKNOWN) ? undefined : i; + } + } + return map.hasOwnProperty('*') ? map['*'] : str; + }; + + /////////////// + // String map + ////////////// + + var windowsVersionMap = { + 'ME' : '4.90', + 'NT 3.51': '3.51', + 'NT 4.0': '4.0', + '2000' : ['5.0', '5.01'], + 'XP' : ['5.1', '5.2'], + 'Vista' : '6.0', + '7' : '6.1', + '8' : '6.2', + '8.1' : '6.3', + '10' : ['6.4', '10.0'], + 'NT' : '' + }, + + formFactorsMap = { + 'embedded' : 'Automotive', + 'mobile' : 'Mobile', + 'tablet' : ['Tablet', 'EInk'], + 'smarttv' : 'TV', + 'wearable' : 'Watch', + 'xr' : ['VR', 'XR'], + '?' : ['Desktop', 'Unknown'], + '*' : undefined + }, + + browserHintsMap = { + 'Chrome' : 'Google Chrome', + 'Edge' : 'Microsoft Edge', + 'Edge WebView2' : 'Microsoft Edge WebView2', + 'Chrome WebView': 'Android WebView', + 'Chrome Headless':'HeadlessChrome', + 'Huawei Browser': 'HuaweiBrowser', + 'MIUI Browser' : 'Miui Browser', + 'Opera Mobi' : 'OperaMobile', + 'Yandex' : 'YaBrowser' + }; + + ////////////// + // Regex map + ///////////// + + var defaultRegexes = { + + browser : [[ + + // Most common regardless engine + /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS + ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ + /webview.+edge\/([\w\.]+)/i // Microsoft Edge + ], [VERSION, [NAME, EDGE+' WebView']], [ + /edg(?:e|ios|a)?\/([\w\.]+)/i + ], [VERSION, [NAME, 'Edge']], [ + + // Presto based + /(opera mini)\/([-\w\.]+)/i, // Opera Mini + /(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i, // Opera Mobi/Tablet + /(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i // Opera + ], [NAME, VERSION], [ + /opios[\/ ]+([\w\.]+)/i // Opera mini on iphone >= 8.0 + ], [VERSION, [NAME, OPERA+' Mini']], [ + /\bop(?:rg)?x\/([\w\.]+)/i // Opera GX + ], [VERSION, [NAME, OPERA+' GX']], [ + /\bopr\/([\w\.]+)/i // Opera Webkit + ], [VERSION, [NAME, OPERA]], [ + + // Mixed + /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu + ], [VERSION, [NAME, 'Baidu']], [ + /\b(?:mxbrowser|mxios|myie2)\/?([-\w\.]*)\b/i // Maxthon + ], [VERSION, [NAME, 'Maxthon']], [ + /(kindle)\/([\w\.]+)/i, // Kindle + /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i, + // Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir + // Trident based + /(avant|iemobile|slim(?:browser|boat|jet))[\/ ]?([\d\.]*)/i, // Avant/IEMobile/SlimBrowser/SlimBoat/Slimjet + /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer + + // Blink/Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon/LG Browser/Otter/qutebrowser/Dooble + /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar|helio|(?=comodo_)?dragon|otter|dooble|(?:lg |qute)browser)\/([-\w\.]+)/i, + // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar/Helio/Dragon + /(heytap|ovi|115|surf)browser\/([\d\.]+)/i, // HeyTap/Ovi/115/Surf + /(ecosia|weibo)(?:__| \w+@)([\d\.]+)/i // Ecosia/Weibo + ], [NAME, VERSION], [ + /quark(?:pc)?\/([-\w\.]+)/i // Quark + ], [VERSION, [NAME, 'Quark']], [ + /\bddg\/([\w\.]+)/i // DuckDuckGo + ], [VERSION, [NAME, 'DuckDuckGo']], [ + /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser + ], [VERSION, [NAME, 'UCBrowser']], [ + /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser + /\bqbcore\/([\w\.]+).+microm/i, + /micromessenger\/([\w\.]+)/i // WeChat + ], [VERSION, [NAME, 'WeChat']], [ + /konqueror\/([\w\.]+)/i // Konqueror + ], [VERSION, [NAME, 'Konqueror']], [ + /trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i // IE11 + ], [VERSION, [NAME, 'IE']], [ + /ya(?:search)?browser\/([\w\.]+)/i // Yandex + ], [VERSION, [NAME, 'Yandex']], [ + /slbrowser\/([\w\.]+)/i // Smart Lenovo Browser + ], [VERSION, [NAME, 'Smart ' + LENOVO + SUFFIX_BROWSER]], [ + /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser + ], [[NAME, /(.+)/, '$1 Secure' + SUFFIX_BROWSER], VERSION], [ + /\bfocus\/([\w\.]+)/i // Firefox Focus + ], [VERSION, [NAME, FIREFOX+' Focus']], [ + /\bopt\/([\w\.]+)/i // Opera Touch + ], [VERSION, [NAME, OPERA+' Touch']], [ + /coc_coc\w+\/([\w\.]+)/i // Coc Coc Browser + ], [VERSION, [NAME, 'Coc Coc']], [ + /dolfin\/([\w\.]+)/i // Dolphin + ], [VERSION, [NAME, 'Dolphin']], [ + /coast\/([\w\.]+)/i // Opera Coast + ], [VERSION, [NAME, OPERA+' Coast']], [ + /miuibrowser\/([\w\.]+)/i // MIUI Browser + ], [VERSION, [NAME, 'MIUI' + SUFFIX_BROWSER]], [ + /fxios\/([\w\.-]+)/i // Firefox for iOS + ], [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]], [ + /\bqihoobrowser\/?([\w\.]*)/i // 360 + ], [VERSION, [NAME, '360']], [ + /\b(qq)\/([\w\.]+)/i // QQ + ], [[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 + ], [VERSION, [NAME, SAMSUNG + ' Internet']], [ + /metasr[\/ ]?([\d\.]+)/i // Sogou Explorer + ], [VERSION, [NAME, SOGOU + ' Explorer']], [ + /(sogou)mo\w+\/([\d\.]+)/i // Sogou Mobile + ], [[NAME, SOGOU + ' Mobile'], VERSION], [ + /(electron)\/([\w\.]+) safari/i, // Electron-based App + /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla + /m?(qqbrowser|2345(?=browser|chrome|explorer))\w*[\/ ]?v?([\w\.]+)/i // QQ/2345 + ], [NAME, VERSION], [ + /(lbbrowser|rekonq)/i // LieBao Browser/Rekonq + ], [NAME], [ + /ome\/([\w\.]+) \w* ?(iron) saf/i, // Iron + /ome\/([\w\.]+).+qihu (360)[es]e/i // 360 + ], [VERSION, NAME], [ + + // WebView + /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android + ], [[NAME, FACEBOOK], VERSION, [TYPE, INAPP]], [ + /(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App + /(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp + /(daum)apps[\/ ]([\w\.]+)/i, // Daum App + /safari (line)\/([\w\.]+)/i, // Line App for iOS + /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android + /(alipay)client\/([\w\.]+)/i, // Alipay + /(twitter)(?:and| f.+e\/([\w\.]+))/i, // Twitter + /(instagram|snapchat|klarna)[\/ ]([-\w\.]+)/i // Instagram/Snapchat/Klarna + ], [NAME, VERSION, [TYPE, INAPP]], [ + /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS + ], [VERSION, [NAME, 'GSA'], [TYPE, INAPP]], [ + /musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok + ], [VERSION, [NAME, 'TikTok'], [TYPE, INAPP]], [ + /\[(linkedin)app\]/i // LinkedIn App for iOS & Android + ], [NAME, [TYPE, INAPP]], [ + + /(chromium)[\/ ]([-\w\.]+)/i // Chromium + ], [NAME, VERSION], [ + + /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless + ], [VERSION, [NAME, CHROME+' Headless']], [ + + /wv\).+chrome\/([\w\.]+).+edgw\//i // Edge WebView2 + ], [VERSION, [NAME, EDGE+' WebView2']], [ + + / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView + ], [[NAME, CHROME+' WebView'], VERSION], [ + + /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser + ], [VERSION, [NAME, 'Android' + SUFFIX_BROWSER]], [ + + /chrome\/([\w\.]+) mobile/i // Chrome Mobile + ], [VERSION, [NAME, PREFIX_MOBILE + 'Chrome']], [ + + /(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia + ], [NAME, VERSION], [ + + /version\/([\w\.\,]+) .*mobile(?:\/\w+ | ?)safari/i // Safari Mobile + ], [VERSION, [NAME, PREFIX_MOBILE + 'Safari']], [ + /iphone .*mobile(?:\/\w+ | ?)safari/i + ], [[NAME, PREFIX_MOBILE + 'Safari']], [ + /version\/([\w\.\,]+) .*(safari)/i // Safari + ], [VERSION, NAME], [ + /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 + ], [NAME, [VERSION, '1']], [ + + /(webkit|khtml)\/([\w\.]+)/i + ], [NAME, VERSION], [ + + // Gecko based + /(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i // Firefox Mobile + ], [[NAME, PREFIX_MOBILE + FIREFOX], VERSION], [ + /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape + ], [[NAME, 'Netscape'], VERSION], [ + /(wolvic|librewolf)\/([\w\.]+)/i // Wolvic/LibreWolf + ], [NAME, VERSION], [ + /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality + ], [VERSION, [NAME, FIREFOX+' Reality']], [ + /ekiohf.+(flow)\/([\w\.]+)/i, // Flow + /(swiftfox)/i, // Swiftfox + /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i, + // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror + /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i, + // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix + /(firefox)\/([\w\.]+)/i, // Other Firefox-based + /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla + + // Other + /(amaya|dillo|doris|icab|ladybird|lynx|mosaic|netsurf|obigo|polaris|w3m|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, + // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser/Ladybird + /\b(links) \(([\w\.]+)/i // Links + ], [NAME, [VERSION, /_/g, '.']], [ + + /(cobalt)\/([\w\.]+)/i // Cobalt + ], [NAME, [VERSION, /[^\d\.]+./, EMPTY]] + ], + + cpu : [[ + + /\b((amd|x|x86[-_]?|wow|win)64)\b/i // AMD64 (x64) + ], [[ARCHITECTURE, 'amd64']], [ + + /(ia32(?=;))/i, // IA32 (quicktime) + /\b((i[346]|x)86)(pc)?\b/i // IA32 (x86) + ], [[ARCHITECTURE, 'ia32']], [ + + /\b(aarch64|arm(v?[89]e?l?|_?64))\b/i // ARM64 + ], [[ARCHITECTURE, 'arm64']], [ + + /\b(arm(v[67])?ht?n?[fl]p?)\b/i // ARMHF + ], [[ARCHITECTURE, 'armhf']], [ + + // PocketPC mistakenly identified as PowerPC + /( (ce|mobile); ppc;|\/[\w\.]+arm\b)/i + ], [[ARCHITECTURE, 'arm']], [ + + /((ppc|powerpc)(64)?)( mac|;|\))/i // PowerPC + ], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [ + + / sun4\w[;\)]/i // SPARC + ], [[ARCHITECTURE, 'sparc']], [ + + /\b(avr32|ia64(?=;)|68k(?=\))|\barm(?=v([1-7]|[5-7]1)l?|;|eabi)|(irix|mips|sparc)(64)?\b|pa-risc)/i + // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC + ], [[ARCHITECTURE, lowerize]] + ], + + device : [[ + + ////////////////////////// + // MOBILES & TABLETS + ///////////////////////// + + // Samsung + /\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i + ], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [ + /\b((?:s[cgp]h|gt|sm)-(?![lr])\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, + /samsung[- ]((?!sm-[lr]|browser)[-\w]+)/i, + /sec-(sgh\w+)/i + ], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [ + + // Apple + /(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone + ], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [ + /\((ipad);[-\w\),; ]+apple/i, // iPad + /applecoremedia\/[\w\.]+ \((ipad)/i, + /\b(ipad)\d\d?,\d\d?[;\]].+ios/i + ], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [ + /(macintosh);/i + ], [MODEL, [VENDOR, APPLE]], [ + + // Sharp + /\b(sh-?[altvz]?\d\d[a-ekm]?)/i + ], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [ + + // Honor + /\b((?:brt|eln|hey2?|gdi|jdn)-a?[lnw]09|(?:ag[rm]3?|jdn2|kob2)-a?[lw]0[09]hn)(?: bui|\)|;)/i + ], [MODEL, [VENDOR, HONOR], [TYPE, TABLET]], [ + /honor([-\w ]+)[;\)]/i + ], [MODEL, [VENDOR, HONOR], [TYPE, MOBILE]], [ + + // Huawei + /\b((?:ag[rs][2356]?k?|bah[234]?|bg[2o]|bt[kv]|cmr|cpn|db[ry]2?|jdn2|got|kob2?k?|mon|pce|scm|sht?|[tw]gr|vrd)-[ad]?[lw][0125][09]b?|605hw|bg2-u03|(?:gem|fdr|m2|ple|t1)-[7a]0[1-4][lu]|t1-a2[13][lw]|mediapad[\w\. ]*(?= bui|\)))\b(?!.+d\/s)/i + ], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [ + /(?:huawei)([-\w ]+)[;\)]/i, + /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i + ], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [ + + // Xiaomi + /oid[^\)]+; (2[\dbc]{4}(182|283|rp\w{2})[cgl]|m2105k81a?c)(?: bui|\))/i, + /\b((?:red)?mi[-_ ]?pad[\w- ]*)(?: bui|\))/i // Mi Pad tablets + ],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [ + + /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO + /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models + /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi + /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi + /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models + /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite|pro)?)(?: bui|\))/i, // Xiaomi Mi + / ([\w ]+) miui\/v?\d/i + ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ + + // OnePlus + /droid.+; (cph2[3-6]\d[13579]|((gm|hd)19|(ac|be|in|kb)20|(d[en]|eb|le|mt)21|ne22)[0-2]\d|p[g-k]\w[1m]10)\b/i, + /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, MOBILE]], [ + + // OPPO + /; (\w+) bui.+ oppo/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]], [ + /\b(opd2(\d{3}a?))(?: bui|\))/i + ], [MODEL, [VENDOR, strMapper, { 'OnePlus' : ['203', '304', '403', '404', '413', '415'], '*' : OPPO }], [TYPE, TABLET]], [ + + // BLU + /(vivo (5r?|6|8l?|go|one|s|x[il]?[2-4]?)[\w\+ ]*)(?: bui|\))/i // Vivo series + ], [MODEL, [VENDOR, 'BLU'], [TYPE, MOBILE]], [ + + // Vivo + /; vivo (\w+)(?: bui|\))/i, + /\b(v[12]\d{3}\w?[at])(?: bui|;)/i + ], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [ + + // Realme + /\b(rmx[1-3]\d{3})(?: bui|;|\))/i + ], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [ + + // Lenovo + /(ideatab[-\w ]+|602lv|d-42a|a101lv|a2109a|a3500-hv|s[56]000|pb-6505[my]|tb-?x?\d{3,4}(?:f[cu]|xu|[av])|yt\d?-[jx]?\d+[lfmx])( bui|;|\)|\/)/i, + /lenovo ?(b[68]0[08]0-?[hf]?|tab(?:[\w- ]+?)|tb[\w-]{6,7})( bui|;|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]], [ + /lenovo[-_ ]?([-\w ]+?)(?: bui|\)|\/)/i + ], [MODEL, [VENDOR, LENOVO], [TYPE, MOBILE]], [ + + // Motorola + /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i, + /\bmot(?:orola)?[- ]([\w\s]+)(\)| bui)/i, + /((?:moto(?! 360)[-\w\(\) ]+|xt\d{3,4}[cgkosw\+]?[-\d]*|nexus 6)(?= bui|\)))/i + ], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [ + /\b(mz60\d|xoom[2 ]{0,2}) build\//i + ], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [ + + // LG + /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i + ], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [ + /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i, + /\blg[-e;\/ ]+(?!.*(?:browser|netcast|android tv|watch|webos))(\w+)/i, + /\blg-?([\d\w]+) bui/i + ], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [ + + // Nokia + /(nokia) (t[12][01])/i + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + /(?:maemo|nokia).*(n900|lumia \d+|rm-\d+)/i, + /nokia[-_ ]?(([-\w\. ]*))/i + ], [[MODEL, /_/g, ' '], [TYPE, MOBILE], [VENDOR, 'Nokia']], [ + + // Google + /(pixel (c|tablet))\b/i // Google Pixel C/Tablet + ], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [ + // Google Pixel + /droid.+;(?: google)? (g(01[13]a|020[aem]|025[jn]|1b60|1f8f|2ybb|4s1m|576d|5nz6|8hhn|8vou|a02099|c15s|d1yq|e2ae|ec77|gh2x|kv4x|p4bc|pj41|r83y|tt9q|ur25|wvk6)|pixel[\d ]*a?( pro)?( xl)?( fold)?( \(5g\))?)( bui|\))/i + ], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [ + /(google) (pixelbook( go)?)/i + ], [VENDOR, MODEL], [ + + // Sony + /droid.+; (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-\w\w\d\d)(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i + ], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [ + /sony tablet [ps]/i, + /\b(?:sony)?sgp\w+(?: bui|\))/i + ], [[MODEL, 'Xperia Tablet'], [VENDOR, SONY], [TYPE, TABLET]], [ + + // Amazon + /(alexa)webm/i, + /(kf[a-z]{2}wi|aeo(?!bc)\w\w)( bui|\))/i, // Kindle Fire without Silk / Echo Show + /(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD + ], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [ + /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone + ], [[MODEL, /(.+)/g, 'Fire Phone $1'], [VENDOR, AMAZON], [TYPE, MOBILE]], [ + + // BlackBerry + /(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook + ], [MODEL, VENDOR, [TYPE, TABLET]], [ + /\b((?:bb[a-f]|st[hv])100-\d)/i, + /\(bb10; (\w+)/i // BlackBerry 10 + ], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [ + + // Asus + /(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i + ], [MODEL, [VENDOR, ASUS], [TYPE, TABLET]], [ + / (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i + ], [MODEL, [VENDOR, ASUS], [TYPE, MOBILE]], [ + + // HTC + /(nexus 9)/i // HTC Nexus 9 + ], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [ + /(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i, // HTC + + // ZTE + /(zte)[- ]([\w ]+?)(?: bui|\/|\))/i, + /(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony + ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [ + + // TCL + /tcl (xess p17aa)/i, + /droid [\w\.]+; ((?:8[14]9[16]|9(?:0(?:48|60|8[01])|1(?:3[27]|66)|2(?:6[69]|9[56])|466))[gqswx])(_\w(\w|\w\w))?(\)| bui)/i + ], [MODEL, [VENDOR, 'TCL'], [TYPE, TABLET]], [ + /droid [\w\.]+; (418(?:7d|8v)|5087z|5102l|61(?:02[dh]|25[adfh]|27[ai]|56[dh]|59k|65[ah])|a509dl|t(?:43(?:0w|1[adepqu])|50(?:6d|7[adju])|6(?:09dl|10k|12b|71[efho]|76[hjk])|7(?:66[ahju]|67[hw]|7[045][bh]|71[hk]|73o|76[ho]|79w|81[hks]?|82h|90[bhsy]|99b)|810[hs]))(_\w(\w|\w\w))?(\)| bui)/i + ], [MODEL, [VENDOR, 'TCL'], [TYPE, MOBILE]], [ + + // itel + /(itel) ((\w+))/i + ], [[VENDOR, lowerize], MODEL, [TYPE, strMapper, { 'tablet' : ['p10001l', 'w7001'], '*' : 'mobile' }]], [ + + // Acer + /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i + ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [ + + // Meizu + /droid.+; (m[1-5] note) bui/i, + /\bmz-([-\w]{2,})/i + ], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [ + + // Ulefone + /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i + ], [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]], [ + + // Energizer + /; (energy ?\w+)(?: bui|\))/i, + /; energizer ([\w ]+)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Energizer'], [TYPE, MOBILE]], [ + + // Cat + /; cat (b35);/i, + /; (b15q?|s22 flip|s48c|s62 pro)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Cat'], [TYPE, MOBILE]], [ + + // Smartfren + /((?:new )?andromax[\w- ]+)(?: bui|\))/i + ], [MODEL, [VENDOR, 'Smartfren'], [TYPE, MOBILE]], [ + + // Nothing + /droid.+; (a(in)?(0(15|59|6[35])|142)p?)/i + ], [MODEL, [VENDOR, 'Nothing'], [TYPE, MOBILE]], [ + + // Archos + /; (x67 5g|tikeasy \w+|ac[1789]\d\w+)( b|\))/i, + /archos ?(5|gamepad2?|([\w ]*[t1789]|hello) ?\d+[\w ]*)( b|\))/i + ], [MODEL, [VENDOR, 'Archos'], [TYPE, TABLET]], [ + /archos ([\w ]+)( b|\))/i, + /; (ac[3-6]\d\w{2,8})( b|\))/i + ], [MODEL, [VENDOR, 'Archos'], [TYPE, MOBILE]], [ + + // HMD + /; (n159v)/i + ], [MODEL, [VENDOR, 'HMD'], [TYPE, MOBILE]], [ + + // MIXED + /(imo) (tab \w+)/i, // IMO + /(infinix|tecno) (x1101b?|p904|dp(7c|8d|10a)( pro)?|p70[1-3]a?|p904|t1101)/i // Infinix XPad / Tecno + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + + /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus(?! zenw)|dell|jolla|meizu|motorola|polytron|tecno|micromax|advan)[-_ ]?([-\w]*)/i, + // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron/Tecno/Micromax/Advan + /; (blu|hmd|imo|infinix|lava|oneplus|tcl)[_ ]([\w\+ ]+?)(?: bui|\)|; r)/i, // BLU/HMD/IMO/Infinix/Lava/OnePlus/TCL + /(hp) ([\w ]+\w)/i, // HP iPAQ + /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia + /(oppo) ?([\w ]+) bui/i // OPPO + ], [VENDOR, MODEL, [TYPE, MOBILE]], [ + + /(kobo)\s(ereader|touch)/i, // Kobo + /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad + /(kindle)\/([\w\.]+)/i // Kindle + ], [VENDOR, MODEL, [TYPE, TABLET]], [ + + /(surface duo)/i // Surface Duo + ], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [ + /droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone + ], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [ + /((?:tegranote|shield t(?!.+d tv))[\w- ]*?)(?: b|\))/i // Nvidia Tablets + ], [MODEL, [VENDOR, NVIDIA], [TYPE, TABLET]], [ + /(sprint) (\w+)/i // Sprint Phones + ], [VENDOR, MODEL, [TYPE, MOBILE]], [ + /(kin\.[onetw]{3})/i // Microsoft Kin + ], [[MODEL, /\./g, ' '], [VENDOR, MICROSOFT], [TYPE, MOBILE]], [ + /droid.+; ([c6]+|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra + ], [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]], [ + /droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i + ], [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], [ + + /////////////////// + // SMARTTVS + /////////////////// + + /smart-tv.+(samsung)/i // Samsung + ], [VENDOR, [TYPE, SMARTTV]], [ + /hbbtv.+maple;(\d+)/i + ], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [ + /(vizio)(?: |.+model\/)(\w+-\w+)/i, // Vizio + /tcast.+(lg)e?. ([-\w]+)/i // LG SmartTV + ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ + /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i + ], [[VENDOR, LG], [TYPE, SMARTTV]], [ + /(apple) ?tv/i // Apple TV + ], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [ + /crkey.*devicetype\/chromecast/i // Google Chromecast Third Generation + ], [[MODEL, CHROMECAST+' Third Generation'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /crkey.*devicetype\/([^/]*)/i // Google Chromecast with specific device type + ], [[MODEL, /^/, 'Chromecast '], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /fuchsia.*crkey/i // Google Chromecast Nest Hub + ], [[MODEL, CHROMECAST+' Nest Hub'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /crkey/i // Google Chromecast, Linux-based or unknown + ], [[MODEL, CHROMECAST], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [ + /(portaltv)/i // Facebook Portal TV + ], [MODEL, [VENDOR, FACEBOOK], [TYPE, SMARTTV]], [ + /droid.+aft(\w+)( bui|\))/i // Fire TV + ], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [ + /(shield \w+ tv)/i // Nvidia Shield TV + ], [MODEL, [VENDOR, NVIDIA], [TYPE, SMARTTV]], [ + /\(dtv[\);].+(aquos)/i, + /(aquos-tv[\w ]+)\)/i // Sharp + ], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[ + /(bravia[\w ]+)( bui|\))/i // Sony + ], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [ + /(mi(tv|box)-?\w+) bui/i // Xiaomi + ], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [ + /Hbbtv.*(technisat) (.*);/i // TechniSAT + ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ + /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku + /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices + ], [[VENDOR, /.+\/(\w+)/, '$1', strMapper, {'LG':'lge'}], [MODEL, trim], [TYPE, SMARTTV]], [ + // SmartTV from Unidentified Vendors + /droid.+; ([\w- ]+) (?:android tv|smart[- ]?tv)/i + ], [MODEL, [TYPE, SMARTTV]], [ + /\b(android tv|smart[- ]?tv|opera tv|tv; rv:|large screen[\w ]+safari)\b/i + ], [[TYPE, SMARTTV]], [ + + /////////////////// + // CONSOLES + /////////////////// + + /(playstation \w+)/i // Playstation + ], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [ + /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox + ], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [ + /(ouya)/i, // Ouya + /(nintendo) (\w+)/i, // Nintendo + /(retroid) (pocket ([^\)]+))/i // Retroid Pocket + ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ + /droid.+; (shield)( bui|\))/i // Nvidia Portable + ], [MODEL, [VENDOR, NVIDIA], [TYPE, CONSOLE]], [ + + /////////////////// + // WEARABLES + /////////////////// + + /\b(sm-[lr]\d\d[0156][fnuw]?s?|gear live)\b/i // Samsung Galaxy Watch + ], [MODEL, [VENDOR, SAMSUNG], [TYPE, WEARABLE]], [ + /((pebble))app/i, // Pebble + /(asus|google|lg|oppo) ((pixel |zen)?watch[\w ]*)( bui|\))/i // Asus ZenWatch / LG Watch / Pixel Watch + ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ + /(ow(?:19|20)?we?[1-3]{1,3})/i // Oppo Watch + ], [MODEL, [VENDOR, OPPO], [TYPE, WEARABLE]], [ + /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch + ], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [ + /(opwwe\d{3})/i // OnePlus Watch + ], [MODEL, [VENDOR, ONEPLUS], [TYPE, WEARABLE]], [ + /(moto 360)/i // Motorola 360 + ], [MODEL, [VENDOR, MOTOROLA], [TYPE, WEARABLE]], [ + /(smartwatch 3)/i // Sony SmartWatch + ], [MODEL, [VENDOR, SONY], [TYPE, WEARABLE]], [ + /(g watch r)/i // LG G Watch R + ], [MODEL, [VENDOR, LG], [TYPE, WEARABLE]], [ + /droid.+; (wt63?0{2,3})\)/i + ], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [ + + /////////////////// + // XR + /////////////////// + + /droid.+; (glass) \d/i // Google Glass + ], [MODEL, [VENDOR, GOOGLE], [TYPE, XR]], [ + /(pico) (4|neo3(?: link|pro)?)/i // Pico + ], [VENDOR, MODEL, [TYPE, XR]], [ + /(quest( \d| pro)?s?).+vr/i // Meta Quest + ], [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]], [ + /mobile vr; rv.+firefox/i // Unidentifiable VR device using Firefox Reality / Wolvic + ], [[TYPE, XR]], [ + + /////////////////// + // EMBEDDED + /////////////////// + + /(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i // Tesla + ], [VENDOR, [TYPE, EMBEDDED]], [ + /(aeobc)\b/i // Echo Dot + ], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [ + /(homepod).+mac os/i // Apple HomePod + ], [MODEL, [VENDOR, APPLE], [TYPE, EMBEDDED]], [ + /windows iot/i // Unidentifiable embedded device using Windows IoT + ], [[TYPE, EMBEDDED]], [ + + //////////////////// + // MIXED (GENERIC) + /////////////////// + + /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+?(mobile|vr|\d) safari/i + ], [MODEL, [TYPE, strMapper, { 'mobile' : 'Mobile', 'xr' : 'VR', '*' : TABLET }]], [ + /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet + ], [[TYPE, TABLET]], [ + /(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i // Unidentifiable Mobile + ], [[TYPE, MOBILE]], [ + /droid .+?; ([\w\. -]+)( bui|\))/i // Generic Android Device + ], [MODEL, [VENDOR, 'Generic']] + ], + + engine : [[ + + /windows.+ edge\/([\w\.]+)/i // EdgeHTML + ], [VERSION, [NAME, EDGE+'HTML']], [ + + /(arkweb)\/([\w\.]+)/i // ArkWeb + ], [NAME, VERSION], [ + + /webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i // Blink + ], [VERSION, [NAME, 'Blink']], [ + + /(presto)\/([\w\.]+)/i, // Presto + /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna|servo)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna/Servo + /ekioh(flow)\/([\w\.]+)/i, // Flow + /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links + /(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab + + /\b(libweb)/i // LibWeb + ], [NAME, VERSION], [ + /ladybird\//i + ], [[NAME, 'LibWeb']], [ + + /rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko + ], [VERSION, NAME] + ], + + os : [[ + + // Windows + /(windows nt) (6\.[23]); arm/i // Windows RT + ], [[NAME, /N/, 'R'], [VERSION, strMapper, windowsVersionMap]], [ + /(windows (?:phone|mobile|iot))(?: os)?[\/ ]?([\d\.]*( se)?)/i, // Windows IoT/Mobile/Phone + // Windows NT/3.1/95/98/ME/2000/XP/Vista/7/8/8.1/10/11 + /(windows)[\/ ](1[01]|2000|3\.1|7|8(\.1)?|9[58]|me|server 20\d\d( r2)?|vista|xp)/i + ], [NAME, VERSION], [ + /windows nt ?([\d\.\)]*)(?!.+xbox)/i, + /\bwin(?=3| ?9|n)(?:nt| 9x )?([\d\.;]*)/i + ], [[VERSION, /(;|\))/g, '', strMapper, windowsVersionMap], [NAME, WINDOWS]], [ + /(windows ce)\/?([\d\.]*)/i // Windows CE + ], [NAME, VERSION], [ + + // iOS/macOS + /[adehimnop]{4,7}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS + /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i, + /cfnetwork\/.+darwin/i + ], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [ + /(mac os x) ?([\w\. ]*)/i, + /(macintosh|mac_powerpc\b)(?!.+(haiku|morphos))/i // Mac OS + ], [[NAME, 'macOS'], [VERSION, /_/g, '.']], [ + + // Google Chromecast + /android ([\d\.]+).*crkey/i // Google Chromecast, Android-based + ], [VERSION, [NAME, CHROMECAST + ' Android']], [ + /fuchsia.*crkey\/([\d\.]+)/i // Google Chromecast, Fuchsia-based + ], [VERSION, [NAME, CHROMECAST + ' Fuchsia']], [ + /crkey\/([\d\.]+).*devicetype\/smartspeaker/i // Google Chromecast, Linux-based Smart Speaker + ], [VERSION, [NAME, CHROMECAST + ' SmartSpeaker']], [ + /linux.*crkey\/([\d\.]+)/i // Google Chromecast, Legacy Linux-based + ], [VERSION, [NAME, CHROMECAST + ' Linux']], [ + /crkey\/([\d\.]+)/i // Google Chromecast, unknown + ], [VERSION, [NAME, CHROMECAST]], [ + + // Mobile OSes + /droid ([\w\.]+)\b.+(android[- ]x86)/i // Android-x86 + ], [VERSION, NAME], [ + /(ubuntu) ([\w\.]+) like android/i // Ubuntu Touch + ], [[NAME, /(.+)/, '$1 Touch'], VERSION], [ + /(harmonyos)[\/ ]?([\d\.]*)/i, // HarmonyOS + // Android/Blackberry/WebOS/QNX/Bada/RIM/KaiOS/Maemo/MeeGo/S40/Sailfish OS/OpenHarmony/Tizen + /(android|bada|blackberry|kaios|maemo|meego|openharmony|qnx|rim tablet os|sailfish|series40|symbian|tizen)\w*[-\/\.; ]?([\d\.]*)/i + ], [NAME, VERSION], [ + /\(bb(10);/i // BlackBerry 10 + ], [VERSION, [NAME, BLACKBERRY]], [ + /(?:symbian ?os|symbos|s60(?=;)|series ?60)[-\/ ]?([\w\.]*)/i // Symbian + ], [VERSION, [NAME, 'Symbian']], [ + /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS + ], [VERSION, [NAME, FIREFOX+' OS']], [ + /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS + /webos(?:[ \/]?|\.tv-20(?=2[2-9]))(\d[\d\.]*)/i + ], [VERSION, [NAME, 'webOS']], [ + /web0s;.+?(?:chr[o0]me|safari)\/(\d+)/i + // https://webostv.developer.lge.com/develop/specifications/web-api-and-web-engine + ], [[VERSION, strMapper, {'25':'120','24':'108','23':'94','22':'87','6':'79','5':'68','4':'53','3':'38','2':'538','1':'537','*':'TV'}], [NAME, 'webOS']], [ + /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS + ], [VERSION, [NAME, 'watchOS']], [ + + // Google ChromeOS + /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS + ], [[NAME, "Chrome OS"], VERSION],[ + + // Smart TVs + /panasonic;(viera)/i, // Panasonic Viera + /(netrange)mmh/i, // Netrange + /(nettv)\/(\d+\.[\w\.]+)/i, // NetTV + + // Console + /(nintendo|playstation) (\w+)/i, // Nintendo/Playstation + /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S) + /(pico) .+os([\w\.]+)/i, // Pico + + // Other + /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm + /linux.+(mint)[\/\(\) ]?([\w\.]*)/i, // Mint + /(mageia|vectorlinux|fuchsia|arcaos|arch(?= ?linux))[;l ]([\d\.]*)/i, // Mageia/VectorLinux/Fuchsia/ArcaOS/Arch + /([kxln]?ubuntu|debian|suse|opensuse|gentoo|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire|knoppix)(?: gnu[\/ ]linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, + // Ubuntu/Debian/SUSE/Gentoo/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire/Knoppix + /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris + /\b(aix)[; ]([1-9\.]{0,4})/i, // AIX + /(hurd|linux|morphos)(?: (?:arm|x86|ppc)\w*| ?)([\w\.]*)/i, // Hurd/Linux/MorphOS + /(gnu) ?([\w\.]*)/i, // GNU + /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly + /(haiku) ?(r\d)?/i // Haiku + ], [NAME, VERSION], [ + /(sunos) ?([\d\.]*)/i // Solaris + ], [[NAME, 'Solaris'], VERSION], [ + /\b(beos|os\/2|amigaos|openvms|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/OpenVMS/HP-UX/SerenityOS + /(unix) ?([\w\.]*)/i // UNIX + ], [NAME, VERSION] + ] + }; + + ///////////////// + // Factories + //////////////// + + var defaultProps = (function () { + var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}}; + setProps.call(props.init, [ + [UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]], + [UA_CPU, [ARCHITECTURE]], + [UA_DEVICE, [TYPE, MODEL, VENDOR]], + [UA_ENGINE, [NAME, VERSION]], + [UA_OS, [NAME, VERSION]] + ]); + setProps.call(props.isIgnore, [ + [UA_BROWSER, [VERSION, MAJOR]], + [UA_ENGINE, [VERSION]], + [UA_OS, [VERSION]] + ]); + setProps.call(props.isIgnoreRgx, [ + [UA_BROWSER, / ?browser$/i], + [UA_OS, / ?os$/i] + ]); + setProps.call(props.toString, [ + [UA_BROWSER, [NAME, VERSION]], + [UA_CPU, [ARCHITECTURE]], + [UA_DEVICE, [VENDOR, MODEL]], + [UA_ENGINE, [NAME, VERSION]], + [UA_OS, [NAME, VERSION]] + ]); + return props; + })(); + + var createIData = function (item, itemType) { + + var init_props = defaultProps.init[itemType], + is_ignoreProps = defaultProps.isIgnore[itemType] || 0, + is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0, + toString_props = defaultProps.toString[itemType] || 0; + + function IData () { + setProps.call(this, init_props); + } + + IData.prototype.getItem = function () { + return item; + }; + + IData.prototype.withClientHints = function () { + + // nodejs / non-client-hints browsers + if (!NAVIGATOR_UADATA) { + return item + .parseCH() + .get(); + } + + // browsers based on chromium 85+ + return NAVIGATOR_UADATA + .getHighEntropyValues(CH_ALL_VALUES) + .then(function (res) { + return item + .setCH(new UACHData(res, false)) + .parseCH() + .get(); + }); + }; + + IData.prototype.withFeatureCheck = function () { + return item.detectFeature().get(); + }; + + if (itemType != UA_RESULT) { + IData.prototype.is = function (strToCheck) { + var is = false; + for (var i in this) { + if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) { + is = true; + if (strToCheck != UNDEF_TYPE) break; + } else if (strToCheck == UNDEF_TYPE && is) { + is = !is; + break; + } + } + return is; + }; + IData.prototype.toString = function () { + var str = EMPTY; + for (var i in toString_props) { + if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) { + str += (str ? ' ' : EMPTY) + this[toString_props[i]]; + } + } + return str || UNDEF_TYPE; + }; + } + + if (!NAVIGATOR_UADATA) { + IData.prototype.then = function (cb) { + var that = this; + var IDataResolve = function () { + for (var prop in that) { + if (that.hasOwnProperty(prop)) { + this[prop] = that[prop]; + } + } + }; + IDataResolve.prototype = { + is : IData.prototype.is, + toString : IData.prototype.toString + }; + var resolveData = new IDataResolve(); + cb(resolveData); + return resolveData; + }; + } + + return new IData(); + }; + + ///////////////// + // Constructor + //////////////// + + function UACHData (uach, isHttpUACH) { + uach = uach || {}; + setProps.call(this, CH_ALL_VALUES); + if (isHttpUACH) { + setProps.call(this, [ + [BRANDS, itemListToArray(uach[CH_HEADER])], + [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])], + [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])], + [MODEL, stripQuotes(uach[CH_HEADER_MODEL])], + [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])], + [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])], + [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])], + [FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])], + [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])] + ]); + } else { + for (var prop in uach) { + if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop]; + } + } + } + + function UAItem (itemType, ua, rgxMap, uaCH) { + + this.get = function (prop) { + if (!prop) return this.data; + return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; + }; + + this.set = function (prop, val) { + this.data[prop] = val; + return this; + }; + + this.setCH = function (ch) { + this.uaCH = ch; + return this; + }; + + this.detectFeature = function () { + if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) { + switch (this.itemType) { + case UA_BROWSER: + // Brave-specific detection + if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) { + this.set(NAME, 'Brave'); + } + break; + case UA_DEVICE: + // Chrome-specific detection: check for 'mobile' value of navigator.userAgentData + if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) { + this.set(TYPE, MOBILE); + } + // iPadOS-specific detection: identified as Mac, but has some iOS-only properties + if (this.get(MODEL) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) { + this.set(MODEL, 'iPad') + .set(TYPE, TABLET); + } + break; + case UA_OS: + // Chrome-specific detection: check for 'platform' value of navigator.userAgentData + if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) { + this.set(NAME, NAVIGATOR_UADATA[PLATFORM]); + } + break; + case UA_RESULT: + var data = this.data; + var detect = function (itemType) { + return data[itemType] + .getItem() + .detectFeature() + .get(); + }; + this.set(UA_BROWSER, detect(UA_BROWSER)) + .set(UA_CPU, detect(UA_CPU)) + .set(UA_DEVICE, detect(UA_DEVICE)) + .set(UA_ENGINE, detect(UA_ENGINE)) + .set(UA_OS, detect(UA_OS)); + } + } + return this; + }; + + this.parseUA = function () { + if (this.itemType != UA_RESULT) { + rgxMapper.call(this.data, this.ua, this.rgxMap); + } + if (this.itemType == UA_BROWSER) { + this.set(MAJOR, majorize(this.get(VERSION))); + } + return this; + }; + + this.parseCH = function () { + var uaCH = this.uaCH, + rgxMap = this.rgxMap; + + switch (this.itemType) { + case UA_BROWSER: + case UA_ENGINE: + var brands = uaCH[FULLVERLIST] || uaCH[BRANDS], prevName; + if (brands) { + for (var i=0; i= 13 ? '11' : '10'); + this.set(NAME, osName) + .set(VERSION, osVersion); + } + // Xbox-Specific Detection + if (this.get(NAME) == WINDOWS && uaCH[MODEL] == 'Xbox') { + this.set(NAME, 'Xbox') + .set(VERSION, undefined); + } + break; + case UA_RESULT: + var data = this.data; + var parse = function (itemType) { + return data[itemType] + .getItem() + .setCH(uaCH) + .parseCH() + .get(); + }; + this.set(UA_BROWSER, parse(UA_BROWSER)) + .set(UA_CPU, parse(UA_CPU)) + .set(UA_DEVICE, parse(UA_DEVICE)) + .set(UA_ENGINE, parse(UA_ENGINE)) + .set(UA_OS, parse(UA_OS)); + } + return this; + }; + + setProps.call(this, [ + ['itemType', itemType], + ['ua', ua], + ['uaCH', uaCH], + ['rgxMap', rgxMap], + ['data', createIData(this, itemType)] + ]); + + return this; + } + + function UAParser (ua, extensions, headers) { + + if (typeof ua === OBJ_TYPE) { + if (isExtensions(ua, true)) { + if (typeof extensions === OBJ_TYPE) { + headers = extensions; // case UAParser(extensions, headers) + } + extensions = ua; // case UAParser(extensions) + } else { + headers = ua; // case UAParser(headers) + extensions = undefined; + } + ua = undefined; + } else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) { + headers = extensions; // case UAParser(ua, headers) + extensions = undefined; + } + + // Convert Headers object into a plain object + if (headers && typeof headers.append === FUNC_TYPE) { + var kv = {}; + headers.forEach(function (v, k) { kv[k] = v; }); + headers = kv; + } + + if (!(this instanceof UAParser)) { + return new UAParser(ua, extensions, headers).getResult(); + } + + var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string + (headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers + ((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent + EMPTY)), // empty string + + httpUACH = new UACHData(headers, true), + regexMap = extensions ? + extend(defaultRegexes, extensions) : + defaultRegexes, + + createItemFunc = function (itemType) { + if (itemType == UA_RESULT) { + return function () { + return new UAItem(itemType, userAgent, regexMap, httpUACH) + .set('ua', userAgent) + .set(UA_BROWSER, this.getBrowser()) + .set(UA_CPU, this.getCPU()) + .set(UA_DEVICE, this.getDevice()) + .set(UA_ENGINE, this.getEngine()) + .set(UA_OS, this.getOS()) + .get(); + }; + } else { + return function () { + return new UAItem(itemType, userAgent, regexMap[itemType], httpUACH) + .parseUA() + .get(); + }; + } + }; + + // public methods + setProps.call(this, [ + ['getBrowser', createItemFunc(UA_BROWSER)], + ['getCPU', createItemFunc(UA_CPU)], + ['getDevice', createItemFunc(UA_DEVICE)], + ['getEngine', createItemFunc(UA_ENGINE)], + ['getOS', createItemFunc(UA_OS)], + ['getResult', createItemFunc(UA_RESULT)], + ['getUA', function () { return userAgent; }], + ['setUA', function (ua) { + if (isString(ua)) + userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua; + return this; + }] + ]) + .setUA(userAgent); + + return this; + } + + UAParser.VERSION = LIBVERSION; + UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR, TYPE]); + UAParser.CPU = enumerize([ARCHITECTURE]); + UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]); + UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]); + + export {UAParser}; \ No newline at end of file diff --git a/src/ua-parser.js b/src/ua-parser.js deleted file mode 100755 index 691884903..000000000 --- a/src/ua-parser.js +++ /dev/null @@ -1,1190 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////// -/* UAParser.js v0.7.33 - Copyright Š 2012-2021 Faisal Salman - MIT License */ /* - Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. - Supports browser & node.js environment. - Demo : https://faisalman.github.io/ua-parser-js - Source : https://github.com/faisalman/ua-parser-js */ -///////////////////////////////////////////////////////////////////////////////// - -(function (window, undefined) { - "use strict"; - - ////////////// - // Constants - ///////////// - - var LIBVERSION = "0.7.33", - EMPTY = "", - UNKNOWN = "?", - FUNC_TYPE = "function", - UNDEF_TYPE = "undefined", - OBJ_TYPE = "object", - STR_TYPE = "string", - MAJOR = "major", - MODEL = "model", - NAME = "name", - TYPE = "type", - VENDOR = "vendor", - VERSION = "version", - ARCHITECTURE = "architecture", - CONSOLE = "console", - MOBILE = "mobile", - TABLET = "tablet", - SMARTTV = "smarttv", - WEARABLE = "wearable", - EMBEDDED = "embedded", - UA_MAX_LENGTH = 350; - - var AMAZON = "Amazon", - APPLE = "Apple", - ASUS = "ASUS", - BLACKBERRY = "BlackBerry", - BROWSER = "Browser", - CHROME = "Chrome", - EDGE = "Edge", - FIREFOX = "Firefox", - GOOGLE = "Google", - HUAWEI = "Huawei", - LG = "LG", - MICROSOFT = "Microsoft", - MOTOROLA = "Motorola", - OPERA = "Opera", - SAMSUNG = "Samsung", - SHARP = "Sharp", - SONY = "Sony", - XIAOMI = "Xiaomi", - ZEBRA = "Zebra", - FACEBOOK = "Facebook"; - - /////////// - // Helper - ////////// - - var extend = function (regexes, extensions) { - var mergedRegexes = {}; - for (var i in regexes) { - if (extensions[i] && extensions[i].length % 2 === 0) { - mergedRegexes[i] = extensions[i].concat(regexes[i]); - } else { - mergedRegexes[i] = regexes[i]; - } - } - return mergedRegexes; - }, - enumerize = function (arr) { - var enums = {}; - for (var i = 0; i < arr.length; i++) { - enums[arr[i].toUpperCase()] = arr[i]; - } - return enums; - }, - has = function (str1, str2) { - return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false; - }, - lowerize = function (str) { - return str.toLowerCase(); - }, - majorize = function (version) { - return typeof version === STR_TYPE ? version.replace(/[^\d\.]/g, EMPTY).split(".")[0] : undefined; - }, - trim = function (str, len) { - if (typeof str === STR_TYPE) { - str = str.replace(/^\s\s*/, EMPTY); - return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH); - } - }; - - /////////////// - // Map helper - ////////////// - - var rgxMapper = function (ua, arrays) { - var i = 0, - j, - k, - p, - q, - matches, - match; - - // loop through all regexes maps - while (i < arrays.length && !matches) { - var regex = arrays[i], // even sequence (0,2,4,..) - props = arrays[i + 1]; // odd sequence (1,3,5,..) - j = k = 0; - - // try matching uastring with regexes - while (j < regex.length && !matches) { - matches = regex[j++].exec(ua); - - if (!!matches) { - for (p = 0; p < props.length; p++) { - match = matches[++k]; - q = props[p]; - // check if given property is actually array - if (typeof q === OBJ_TYPE && q.length > 0) { - if (q.length === 2) { - if (typeof q[1] == FUNC_TYPE) { - // assign modified match - this[q[0]] = q[1].call(this, match); - } else { - // assign given value, ignore regex match - this[q[0]] = q[1]; - } - } else if (q.length === 3) { - // check whether function or regex - if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) { - // call function (usually string mapper) - this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined; - } else { - // sanitize match using given regex - this[q[0]] = match ? match.replace(q[1], q[2]) : undefined; - } - } else if (q.length === 4) { - this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined; - } - } else { - this[q] = match ? match : undefined; - } - } - } - } - i += 2; - } - }, - strMapper = function (str, map) { - for (var i in map) { - // check if current value is array - if (typeof map[i] === OBJ_TYPE && map[i].length > 0) { - for (var j = 0; j < map[i].length; j++) { - if (has(map[i][j], str)) { - return i === UNKNOWN ? undefined : i; - } - } - } else if (has(map[i], str)) { - return i === UNKNOWN ? undefined : i; - } - } - return str; - }; - - /////////////// - // String map - ////////////// - - // Safari < 3.0 - var oldSafariMap = { - "1.0": "/8", - 1.2: "/1", - 1.3: "/3", - "2.0": "/412", - "2.0.2": "/416", - "2.0.3": "/417", - "2.0.4": "/419", - "?": "/" - }, - windowsVersionMap = { - ME: "4.90", - "NT 3.11": "NT3.51", - "NT 4.0": "NT4.0", - 2000: "NT 5.0", - XP: ["NT 5.1", "NT 5.2"], - Vista: "NT 6.0", - 7: "NT 6.1", - 8: "NT 6.2", - 8.1: "NT 6.3", - 10: ["NT 6.4", "NT 10.0"], - RT: "ARM" - }; - - ////////////// - // Regex map - ///////////// - - var regexes = { - browser: [ - [ - /\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS - ], - [VERSION, [NAME, "Chrome"]], - [ - /edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge - ], - [VERSION, [NAME, "Edge"]], - [ - // Presto based - /(opera mini)\/([-\w\.]+)/i, // Opera Mini - /(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i, // Opera Mobi/Tablet - /(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i // Opera - ], - [NAME, VERSION], - [ - /opios[\/ ]+([\w\.]+)/i // Opera mini on iphone >= 8.0 - ], - [VERSION, [NAME, OPERA + " Mini"]], - [ - /\bopr\/([\w\.]+)/i // Opera Webkit - ], - [VERSION, [NAME, OPERA]], - [ - // Mixed - /(kindle)\/([\w\.]+)/i, // Kindle - /(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer - // Trident based - /(avant |iemobile|slim)(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser - /(ba?idubrowser)[\/ ]?([\w\.]+)/i, // Baidu Browser - /(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer - - // 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|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, - // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ, aka ShouQ - /(weibo)__([\d\.]+)/i // Weibo - ], - [NAME, VERSION], - [ - /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser - ], - [VERSION, [NAME, "UC" + BROWSER]], - [ - /microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser - /\bqbcore\/([\w\.]+).+microm/i - ], - [VERSION, [NAME, "WeChat(Win) Desktop"]], - [ - /micromessenger\/([\w\.]+)/i // WeChat - ], - [VERSION, [NAME, "WeChat"]], - [ - /konqueror\/([\w\.]+)/i // Konqueror - ], - [VERSION, [NAME, "Konqueror"]], - [ - /trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i // IE11 - ], - [VERSION, [NAME, "IE"]], - [ - /yabrowser\/([\w\.]+)/i // Yandex - ], - [VERSION, [NAME, "Yandex"]], - [ - /(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser - ], - [[NAME, /(.+)/, "$1 Secure " + BROWSER], VERSION], - [ - /\bfocus\/([\w\.]+)/i // Firefox Focus - ], - [VERSION, [NAME, FIREFOX + " Focus"]], - [ - /\bopt\/([\w\.]+)/i // Opera Touch - ], - [VERSION, [NAME, OPERA + " Touch"]], - [ - /coc_coc\w+\/([\w\.]+)/i // Coc Coc Browser - ], - [VERSION, [NAME, "Coc Coc"]], - [ - /dolfin\/([\w\.]+)/i // Dolphin - ], - [VERSION, [NAME, "Dolphin"]], - [ - /coast\/([\w\.]+)/i // Opera Coast - ], - [VERSION, [NAME, OPERA + " Coast"]], - [ - /miuibrowser\/([\w\.]+)/i // MIUI Browser - ], - [VERSION, [NAME, "MIUI " + BROWSER]], - [ - /fxios\/([-\w\.]+)/i // Firefox for iOS - ], - [VERSION, [NAME, FIREFOX]], - [ - /\bqihu|(qi?ho?o?|360)browser/i // 360 - ], - [[NAME, "360 " + BROWSER]], - [/(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i], - [[NAME, /(.+)/, "$1 " + BROWSER], VERSION], - [ - // Oculus/Samsung/Sailfish/Huawei Browser - /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon - ], - [[NAME, /_/g, " "], VERSION], - [ - /(electron)\/([\w\.]+) safari/i, // Electron-based App - /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla - /m?(qqbrowser|baiduboxapp|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/Baidu App/2345 Browser - ], - [NAME, VERSION], - [ - /(metasr)[\/ ]?([\w\.]+)/i, // SouGouBrowser - /(lbbrowser)/i, // LieBao Browser - /\[(linkedin)app\]/i // LinkedIn App for iOS & Android - ], - [NAME], - [ - // WebView - /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android - ], - [[NAME, FACEBOOK], VERSION], - [ - /safari (line)\/([\w\.]+)/i, // Line App for iOS - /\b(line)\/([\w\.]+)\/iab/i, // Line App for Android - /(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram - ], - [NAME, VERSION], - [ - /\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS - ], - [VERSION, [NAME, "GSA"]], - [ - /headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless - ], - [VERSION, [NAME, CHROME + " Headless"]], - [ - / wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView - ], - [[NAME, CHROME + " WebView"], VERSION], - [ - /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser - ], - [VERSION, [NAME, "Android " + BROWSER]], - [ - /(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia - ], - [NAME, VERSION], - [ - /version\/([\w\.\,]+) .*mobile\/\w+ (safari)/i // Mobile Safari - ], - [VERSION, [NAME, "Mobile Safari"]], - [ - /version\/([\w(\.|\,)]+) .*(mobile ?safari|safari)/i // Safari & Safari Mobile - ], - [VERSION, NAME], - [ - /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 - ], - [NAME, [VERSION, strMapper, oldSafariMap]], - [/(webkit|khtml)\/([\w\.]+)/i], - [NAME, VERSION], - [ - // Gecko based - /(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape - ], - [[NAME, "Netscape"], VERSION], - [ - /mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality - ], - [VERSION, [NAME, FIREFOX + " Reality"]], - [ - /ekiohf.+(flow)\/([\w\.]+)/i, // Flow - /(swiftfox)/i, // Swiftfox - /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i, - // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar - /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i, - // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix - /(firefox)\/([\w\.]+)/i, // Other Firefox-based - /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla - - // 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/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser - /(links) \(([\w\.]+)/i // Links - ], - [NAME, VERSION], - [ - /(cobalt)\/([\w\.]+)/i // Cobalt - ], - [NAME, [VERSION, /master.|lts./, ""]] - ], - - cpu: [ - [ - /(?:(amd|x(?:(?:86|64)[-_])?|wow|win)64)[;\)]/i // AMD64 (x64) - ], - [[ARCHITECTURE, "amd64"]], - [ - /(ia32(?=;))/i // IA32 (quicktime) - ], - [[ARCHITECTURE, lowerize]], - [ - /((?:i[346]|x)86)[;\)]/i // IA32 (x86) - ], - [[ARCHITECTURE, "ia32"]], - [ - /\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64 - ], - [[ARCHITECTURE, "arm64"]], - [ - /\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF - ], - [[ARCHITECTURE, "armhf"]], - [ - // PocketPC mistakenly identified as PowerPC - /windows (ce|mobile); ppc;/i - ], - [[ARCHITECTURE, "arm"]], - [ - /((?:ppc|powerpc)(?:64)?)(?: mac|;|\))/i // PowerPC - ], - [[ARCHITECTURE, /ower/, EMPTY, lowerize]], - [ - /(sun4\w)[;\)]/i // SPARC - ], - [[ARCHITECTURE, "sparc"]], - [ - /((?:avr32|ia64(?=;))|68k(?=\))|\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i - // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC - ], - [[ARCHITECTURE, lowerize]] - ], - - device: [ - [ - ////////////////////////// - // MOBILES & TABLETS - // Ordered by popularity - ///////////////////////// - - // Samsung - /\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i - ], - [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], - [ - /\b((?:s[cgp]h|gt|sm)-\w+|galaxy nexus)/i, - /samsung[- ]([-\w]+)/i, - /sec-(sgh\w+)/i - ], - [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], - [ - // Apple - /((ipod|iphone)\d+,\d+)/i // iPod/iPhone model - ], - [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], - [ - /(ipad\d+,\d+)/i // iPad model - ], - [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], - [ - /\((ip(?:hone|od)[\w ]*);/i // iPod/iPhone - ], - [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], - [ - /\((ipad);[-\w\),; ]+apple/i, // iPad - /applecoremedia\/[\w\.]+ \((ipad)/i, - /\b(ipad)\d\d?,\d\d?[;\]].+ios/i - ], - [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], - [/(macintosh);/i], - [MODEL, [VENDOR, APPLE]], - [ - // Huawei - /\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i - ], - [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], - [ - /(?:huawei|honor)([-\w ]+)[;\)]/i, - /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i - ], - [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], - [ - // Xiaomi - /\b(poco[\w ]+)(?: bui|\))/i, // Xiaomi POCO - /\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models - /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi - /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi - /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi - ], - [ - [MODEL, /_/g, " "], - [VENDOR, XIAOMI], - [TYPE, MOBILE] - ], - [ - /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets - ], - [ - [MODEL, /_/g, " "], - [VENDOR, XIAOMI], - [TYPE, TABLET] - ], - [ - // OPPO - /; (\w+) bui.+ oppo/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]], - [ - // Vivo - /vivo (\w+)(?: bui|\))/i, - /\b(v[12]\d{3}\w?[at])(?: bui|;)/i - ], - [MODEL, [VENDOR, "Vivo"], [TYPE, MOBILE]], - [ - // Realme - /\b(rmx[12]\d{3})(?: bui|;|\))/i - ], - [MODEL, [VENDOR, "Realme"], [TYPE, MOBILE]], - [ - // Motorola - /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i, - /\bmot(?:orola)?[- ](\w*)/i, - /((?:moto[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i - ], - [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], - [/\b(mz60\d|xoom[2 ]{0,2}) build\//i], - [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], - [ - // LG - /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i - ], - [MODEL, [VENDOR, LG], [TYPE, TABLET]], - [ - /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i, - /\blg[-e;\/ ]+((?!browser|netcast|android tv)\w+)/i, - /\blg-?([\d\w]+) bui/i - ], - [MODEL, [VENDOR, LG], [TYPE, MOBILE]], - [ - // Lenovo - /(ideatab[-\w ]+)/i, - /lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i - ], - [MODEL, [VENDOR, "Lenovo"], [TYPE, TABLET]], - [ - // Nokia - /(?:maemo|nokia).*(n900|lumia \d+)/i, - /nokia[-_ ]?([-\w\.]*)/i - ], - [ - [MODEL, /_/g, " "], - [VENDOR, "Nokia"], - [TYPE, MOBILE] - ], - [ - // Google - /(pixel c)\b/i // Google Pixel C - ], - [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], - [ - /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel - ], - [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], - [ - // Sony - /droid.+ (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i - ], - [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], - [/sony tablet [ps]/i, /\b(?:sony)?sgp\w+(?: bui|\))/i], - [ - [MODEL, "Xperia Tablet"], - [VENDOR, SONY], - [TYPE, TABLET] - ], - [ - // OnePlus - / (kb2005|in20[12]5|be20[12][59])\b/i, - /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i - ], - [MODEL, [VENDOR, "OnePlus"], [TYPE, MOBILE]], - [ - // Amazon - /(alexa)webm/i, - /(kf[a-z]{2}wi)( bui|\))/i, // Kindle Fire without Silk - /(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD - ], - [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], - [ - /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone - ], - [ - [MODEL, /(.+)/g, "Fire Phone $1"], - [VENDOR, AMAZON], - [TYPE, MOBILE] - ], - [ - // BlackBerry - /(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook - ], - [MODEL, VENDOR, [TYPE, TABLET]], - [ - /\b((?:bb[a-f]|st[hv])100-\d)/i, - /\(bb10; (\w+)/i // BlackBerry 10 - ], - [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], - [ - // Asus - /(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i - ], - [MODEL, [VENDOR, ASUS], [TYPE, TABLET]], - [/ (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i], - [MODEL, [VENDOR, ASUS], [TYPE, MOBILE]], - [ - // HTC - /(nexus 9)/i // HTC Nexus 9 - ], - [MODEL, [VENDOR, "HTC"], [TYPE, TABLET]], - [ - /(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i, // HTC - - // ZTE - /(zte)[- ]([\w ]+?)(?: bui|\/|\))/i, - /(alcatel|geeksphone|nexian|panasonic|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony - ], - [VENDOR, [MODEL, /_/g, " "], [TYPE, MOBILE]], - [ - // Acer - /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i - ], - [MODEL, [VENDOR, "Acer"], [TYPE, TABLET]], - [ - // Meizu - /droid.+; (m[1-5] note) bui/i, - /\bmz-([-\w]{2,})/i - ], - [MODEL, [VENDOR, "Meizu"], [TYPE, MOBILE]], - [ - // Sharp - /\b(sh-?[altvz]?\d\d[a-ekm]?)/i - ], - [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], - [ - // MIXED - /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i, - // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron - /(hp) ([\w ]+\w)/i, // HP iPAQ - /(asus)-?(\w+)/i, // Asus - /(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia - /(lenovo)[-_ ]?([-\w]+)/i, // Lenovo - /(jolla)/i, // Jolla - /(oppo) ?([\w ]+) bui/i // OPPO - ], - [VENDOR, MODEL, [TYPE, MOBILE]], - [ - /(archos) (gamepad2?)/i, // Archos - /(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad - /(kindle)\/([\w\.]+)/i, // Kindle - /(nook)[\w ]+build\/(\w+)/i, // Nook - /(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak - /(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets - /(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets - /(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets - /(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone - ], - [VENDOR, MODEL, [TYPE, TABLET]], - [ - /(surface duo)/i // Surface Duo - ], - [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], - [ - /droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone - ], - [MODEL, [VENDOR, "Fairphone"], [TYPE, MOBILE]], - [ - /(u304aa)/i // AT&T - ], - [MODEL, [VENDOR, "AT&T"], [TYPE, MOBILE]], - [ - /\bsie-(\w*)/i // Siemens - ], - [MODEL, [VENDOR, "Siemens"], [TYPE, MOBILE]], - [ - /\b(rct\w+) b/i // RCA Tablets - ], - [MODEL, [VENDOR, "RCA"], [TYPE, TABLET]], - [ - /\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets - ], - [MODEL, [VENDOR, "Dell"], [TYPE, TABLET]], - [ - /\b(q(?:mv|ta)\w+) b/i // Verizon Tablet - ], - [MODEL, [VENDOR, "Verizon"], [TYPE, TABLET]], - [ - /\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet - ], - [MODEL, [VENDOR, "Barnes & Noble"], [TYPE, TABLET]], - [/\b(tm\d{3}\w+) b/i], - [MODEL, [VENDOR, "NuVision"], [TYPE, TABLET]], - [ - /\b(k88) b/i // ZTE K Series Tablet - ], - [MODEL, [VENDOR, "ZTE"], [TYPE, TABLET]], - [ - /\b(nx\d{3}j) b/i // ZTE Nubia - ], - [MODEL, [VENDOR, "ZTE"], [TYPE, MOBILE]], - [ - /\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile - ], - [MODEL, [VENDOR, "Swiss"], [TYPE, MOBILE]], - [ - /\b(zur\d{3}) b/i // Swiss ZUR Tablet - ], - [MODEL, [VENDOR, "Swiss"], [TYPE, TABLET]], - [ - /\b((zeki)?tb.*\b) b/i // Zeki Tablets - ], - [MODEL, [VENDOR, "Zeki"], [TYPE, TABLET]], - [ - /\b([yr]\d{2}) b/i, - /\b(dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet - ], - [[VENDOR, "Dragon Touch"], MODEL, [TYPE, TABLET]], - [ - /\b(ns-?\w{0,9}) b/i // Insignia Tablets - ], - [MODEL, [VENDOR, "Insignia"], [TYPE, TABLET]], - [ - /\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets - ], - [MODEL, [VENDOR, "NextBook"], [TYPE, TABLET]], - [ - /\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones - ], - [[VENDOR, "Voice"], MODEL, [TYPE, MOBILE]], - [ - /\b(lvtel\-)?(v1[12]) b/i // LvTel Phones - ], - [[VENDOR, "LvTel"], MODEL, [TYPE, MOBILE]], - [ - /\b(ph-1) /i // Essential PH-1 - ], - [MODEL, [VENDOR, "Essential"], [TYPE, MOBILE]], - [ - /\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets - ], - [MODEL, [VENDOR, "Envizen"], [TYPE, TABLET]], - [ - /\b(trio[-\w\. ]+) b/i // MachSpeed Tablets - ], - [MODEL, [VENDOR, "MachSpeed"], [TYPE, TABLET]], - [ - /\btu_(1491) b/i // Rotor Tablets - ], - [MODEL, [VENDOR, "Rotor"], [TYPE, TABLET]], - [ - /(shield[\w ]+) b/i // Nvidia Shield Tablets - ], - [MODEL, [VENDOR, "Nvidia"], [TYPE, TABLET]], - [ - /(sprint) (\w+)/i // Sprint Phones - ], - [VENDOR, MODEL, [TYPE, MOBILE]], - [ - /(kin\.[onetw]{3})/i // Microsoft Kin - ], - [ - [MODEL, /\./g, " "], - [VENDOR, MICROSOFT], - [TYPE, MOBILE] - ], - [ - /droid.+; (cc6666?|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra - ], - [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]], - [/droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i], - [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], - [ - /////////////////// - // CONSOLES - /////////////////// - - /(ouya)/i, // Ouya - /(nintendo) ([wids3utch]+)/i // Nintendo - ], - [VENDOR, MODEL, [TYPE, CONSOLE]], - [ - /droid.+; (shield) bui/i // Nvidia - ], - [MODEL, [VENDOR, "Nvidia"], [TYPE, CONSOLE]], - [ - /(playstation [345portablevi]+)/i // Playstation - ], - [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], - [ - /\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox - ], - [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], - [ - /////////////////// - // SMARTTVS - /////////////////// - - /smart-tv.+(samsung)/i // Samsung - ], - [VENDOR, [TYPE, SMARTTV]], - [/hbbtv.+maple;(\d+)/i], - [ - [MODEL, /^/, "SmartTV"], - [VENDOR, SAMSUNG], - [TYPE, SMARTTV] - ], - [ - /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV - ], - [ - [VENDOR, LG], - [TYPE, SMARTTV] - ], - [ - /(apple) ?tv/i // Apple TV - ], - [VENDOR, [MODEL, APPLE + " TV"], [TYPE, SMARTTV]], - [ - /crkey/i // Google Chromecast - ], - [ - [MODEL, CHROME + "cast"], - [VENDOR, GOOGLE], - [TYPE, SMARTTV] - ], - [ - /droid.+aft(\w)( bui|\))/i // Fire TV - ], - [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], - [ - /\(dtv[\);].+(aquos)/i, - /(aquos-tv[\w ]+)\)/i // Sharp - ], - [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]], - [ - /(bravia[\w ]+)( bui|\))/i // Sony - ], - [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], - [ - /(mitv-\w{5}) bui/i // Xiaomi - ], - [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], - [ - /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku - /hbbtv\/\d+\.\d+\.\d+ +\([\w ]*; *(\w[^;]*);([^;]*)/i // HbbTV devices - ], - [ - [VENDOR, trim], - [MODEL, trim], - [TYPE, SMARTTV] - ], - [ - /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors - ], - [[TYPE, SMARTTV]], - [ - /////////////////// - // WEARABLES - /////////////////// - - /((pebble))app/i // Pebble - ], - [VENDOR, MODEL, [TYPE, WEARABLE]], - [ - /droid.+; (glass) \d/i // Google Glass - ], - [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], - [/droid.+; (wt63?0{2,3})\)/i], - [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], - [ - /(quest( 2)?)/i // Oculus Quest - ], - [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], - [ - /////////////////// - // EMBEDDED - /////////////////// - - /(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i // Tesla - ], - [VENDOR, [TYPE, EMBEDDED]], - [ - //////////////////// - // MIXED (GENERIC) - /////////////////// - - /droid .+?; ([^;]+?)(?: bui|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors - ], - [MODEL, [TYPE, MOBILE]], - [ - /droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors - ], - [MODEL, [TYPE, TABLET]], - [ - /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet - ], - [[TYPE, TABLET]], - [ - /(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i // Unidentifiable Mobile - ], - [[TYPE, MOBILE]], - [ - /(android[-\w\. ]{0,9});.+buil/i // Generic Android Device - ], - [MODEL, [VENDOR, "Generic"]] - ], - - engine: [ - [ - /windows.+ edge\/([\w\.]+)/i // EdgeHTML - ], - [VERSION, [NAME, EDGE + "HTML"]], - [ - /webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i // Blink - ], - [VERSION, [NAME, "Blink"]], - [ - /(presto)\/([\w\.]+)/i, // Presto - /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna - /ekioh(flow)\/([\w\.]+)/i, // Flow - /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links - /(icab)[\/ ]([23]\.[\d\.]+)/i // iCab - ], - [NAME, VERSION], - [ - /rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko - ], - [VERSION, NAME] - ], - - os: [ - [ - // Windows - /microsoft (windows) (vista|xp)/i // Windows (iTunes) - ], - [NAME, VERSION], - [ - /(windows) nt 6\.2; (arm)/i, // Windows RT - /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i, // Windows Phone - /(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i - ], - [NAME, [VERSION, strMapper, windowsVersionMap]], - [/(win(?=3|9|n)|win 9x )([nt\d\.]+)/i], - [ - [NAME, "Windows"], - [VERSION, strMapper, windowsVersionMap] - ], - [ - // iOS/macOS - /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS - /cfnetwork\/.+darwin/i - ], - [ - [VERSION, /_/g, "."], - [NAME, "iOS"] - ], - [ - /(mac os x) ?([\w\. ]*)/i, - /(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS - ], - [ - [NAME, "Mac OS"], - [VERSION, /_/g, "."] - ], - [ - // Mobile OSes - /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS - ], - [VERSION, NAME], - [ - // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS - /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-\/ ]?([\w\.]*)/i, - /(blackberry)\w*\/([\w\.]*)/i, // Blackberry - /(tizen|kaios)[\/ ]([\w\.]+)/i, // Tizen/KaiOS - /\((series40);/i // Series 40 - ], - [NAME, VERSION], - [ - /\(bb(10);/i // BlackBerry 10 - ], - [VERSION, [NAME, BLACKBERRY]], - [ - /(?:symbian ?os|symbos|s60(?=;)|series60)[-\/ ]?([\w\.]*)/i // Symbian - ], - [VERSION, [NAME, "Symbian"]], - [ - /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS - ], - [VERSION, [NAME, FIREFOX + " OS"]], - [ - /web0s;.+rt(tv)/i, - /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i // WebOS - ], - [VERSION, [NAME, "webOS"]], - [ - // Google Chromecast - /crkey\/([\d\.]+)/i // Google Chromecast - ], - [VERSION, [NAME, CHROME + "cast"]], - [ - /(cros) [\w]+ ([\w\.]+\w)/i // Chromium OS - ], - [[NAME, "Chromium OS"], VERSION], - [ - // Console - /(nintendo|playstation) ([wids345portablevuch]+)/i, // Nintendo/Playstation - /(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S) - - // Other - /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm - /(mint)[\/\(\) ]?(\w*)/i, // Mint - /(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux - /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, - // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire - /(hurd|linux) ?([\w\.]*)/i, // Hurd/Linux - /(gnu) ?([\w\.]*)/i, // GNU - /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly - /(haiku) (\w+)/i // Haiku - ], - [NAME, VERSION], - [ - /(sunos) ?([\w\.\d]*)/i // Solaris - ], - [[NAME, "Solaris"], VERSION], - [ - /((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris - /(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX - /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX - /(unix) ?([\w\.]*)/i // UNIX - ], - [NAME, VERSION] - ] - }; - - ///////////////// - // Constructor - //////////////// - - var UAParser = function (ua, extensions) { - if (typeof ua === OBJ_TYPE) { - extensions = ua; - ua = undefined; - } - - if (!(this instanceof UAParser)) { - return new UAParser(ua, extensions).getResult(); - } - - var _ua = - ua || - (typeof window !== UNDEF_TYPE && - window.navigator && - window.navigator.userAgent ? window.navigator.userAgent : EMPTY); - var _rgxmap = extensions ? extend(regexes, extensions) : regexes; - - this.getBrowser = function () { - var _browser = {}; - _browser[NAME] = undefined; - _browser[VERSION] = undefined; - rgxMapper.call(_browser, _ua, _rgxmap.browser); - _browser.major = majorize(_browser.version); - return _browser; - }; - this.getCPU = function () { - var _cpu = {}; - _cpu[ARCHITECTURE] = undefined; - rgxMapper.call(_cpu, _ua, _rgxmap.cpu); - return _cpu; - }; - this.getDevice = function () { - var _device = {}; - _device[VENDOR] = undefined; - _device[MODEL] = undefined; - _device[TYPE] = undefined; - rgxMapper.call(_device, _ua, _rgxmap.device); - return _device; - }; - this.getEngine = function () { - var _engine = {}; - _engine[NAME] = undefined; - _engine[VERSION] = undefined; - rgxMapper.call(_engine, _ua, _rgxmap.engine); - return _engine; - }; - this.getOS = function () { - var _os = {}; - _os[NAME] = undefined; - _os[VERSION] = undefined; - rgxMapper.call(_os, _ua, _rgxmap.os); - return _os; - }; - this.getResult = function () { - return { - ua: this.getUA(), - browser: this.getBrowser(), - engine: this.getEngine(), - os: this.getOS(), - device: this.getDevice(), - cpu: this.getCPU() - }; - }; - this.getUA = function () { - return _ua; - }; - this.setUA = function (ua) { - _ua = - typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua; - return this; - }; - this.setUA(_ua); - return this; - }; - - UAParser.VERSION = LIBVERSION; - UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]); - UAParser.CPU = enumerize([ARCHITECTURE]); - UAParser.DEVICE = enumerize([ - MODEL, - VENDOR, - TYPE, - CONSOLE, - MOBILE, - SMARTTV, - TABLET, - WEARABLE, - EMBEDDED - ]); - UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]); - - /////////// - // Export - ////////// - - // check js environment - if (typeof exports !== UNDEF_TYPE) { - // nodejs env - if (typeof module !== UNDEF_TYPE && module.exports) { - exports = module.exports = UAParser; - } - exports.UAParser = UAParser; - } else { - // requirejs env (optional) - if (typeof define === FUNC_TYPE && define.amd) { - define(function () { - return UAParser; - }); - } else if (typeof window !== UNDEF_TYPE) { - // browser env - window.UAParser = UAParser; - } - } - - // jQuery/Zepto specific (optional) - // Note: - // In AMD env the global scope should be kept clean, but jQuery is an exception. - // jQuery always exports to global scope, unless jQuery.noConflict(true) is used, - // and we should catch that. - var $ = typeof window !== UNDEF_TYPE && (window.jQuery || window.Zepto); - if ($ && !$.ua) { - var parser = new UAParser(); - $.ua = parser.getResult(); - $.ua.get = function () { - return parser.getUA(); - }; - $.ua.set = function (ua) { - parser.setUA(ua); - var result = parser.getResult(); - for (var prop in result) { - $.ua[prop] = result[prop]; - } - }; - } -})(typeof window === "object" ? window : this); diff --git a/test/browser-test.json b/test/data/ua/browser/browser-all.json similarity index 57% rename from test/browser-test.json rename to test/data/ua/browser/browser-all.json index 2be58c21b..a2a637903 100644 --- a/test/browser-test.json +++ b/test/data/ua/browser/browser-all.json @@ -1,12 +1,94 @@ [ + { + "desc" : "115 Browser", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36 115Browser/24.3.0.3", + "expect" : + { + "name" : "115", + "version" : "24.3.0.3", + "major" : "24" + } + }, + { + "desc" : "2345 Browser", + "ua" : "Mozilla/5.0 (Linux; Android 7.0; MI NOTE Pro Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36 Mb2345Browser/15.6.2", + "expect" : + { + "name" : "2345", + "version" : "15.6.2", + "major" : "15" + } + }, + { + "desc" : "2345 Chrome", + "ua" : "Mozilla/5.0 (Windows NT 6.3) AppleWebKit/537.36 (KHTML like Gecko) Chrome/39.0.2171.99 Safari/537.36 2345chrome v3.0.0.9739", + "expect" : + { + "name" : "2345", + "version" : "3.0.0.9739", + "major" : "3" + } + }, + { + "desc" : "2345 Explorer", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.90 Safari/537.36 2345Explorer/9.2.1.17116", + "expect" : + { + "name" : "2345", + "version" : "9.2.1.17116", + "major" : "9" + } + }, { "desc" : "360 Browser on iOS", "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 12_4_1 like Mac OS X) AppleWebKit/607.3.9 (KHTML, like Gecko) Mobile/16G102 QHBrowser/317 QihooBrowser/4.0.10", "expect" : { - "name" : "360 Browser", - "version" : "undefined", - "major" : "undefined" + "name" : "360", + "version" : "4.0.10", + "major" : "4" + } + }, + { + "desc" : "360 Secure Browser on Windows 10", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 QIHU 360SE", + "expect" : + { + "name" : "360", + "version" : "86.0.4240.198", + "major" : "86" + } + }, + { + "desc" : "360 Speed Browser on Windows 10", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 QIHU 360EE", + "expect" : + { + "name" : "360", + "version" : "86.0.4240.198", + "major" : "86" + } + }, + { + "desc" : "Alipay", + "ua" : "Mozilla/5.0 (Linux; U; Android 10; zh-CN; V2034A Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 UWS/3.22.2.33 Mobile Safari/537.36 UCBS/3.22.2.33_211025173018 NebulaSDK/1.8.100112 Nebula AlipayDefined(nt:WIFI,ws:360|0|2.0) AliApp(AP/10.2.51.7100) AlipayClient/10.2.51.7100 Language/zh-Hans useStatusBar/true isConcaveScreen/true Region/CNAriver/1.0.0", + "expect" : + { + "name" : "Alipay", + "version" : "10.2.51.7100", + "major" : "10", + "type" : "inapp" + } + }, + { + "desc" : "Alipay", + "ua" : "Mozilla/5.0 (Linux; Android 10; VOG-AL00 Build/HUAWEIVOG-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/105.0.5195.148 MYWeb/0.2.103.0_20230131112530 UWS/3.22.2.9999 UCBS/3.22.2.9999_220000000000 Mobile Safari/537.36 NebulaSDK/1.8.100112 Nebula AlipayDefined(nt:WIFI,ws:360|0|3.0) AliApp(AP/10.3.50.9999) AlipayClient/10.3.50.9999 Language/en isConcaveScreen/true Region/CN ProductType/devAriver/1.0.0", + "expect" : + { + "name" : "Alipay", + "version" : "10.3.50.9999", + "major" : "10", + "type" : "inapp" } }, { @@ -64,7 +146,7 @@ "ua" : "Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925F Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/3.0 Chrome/38.0.2125.102 Mobile Safari/537.36", "expect" : { - "name" : "Samsung Browser", + "name" : "Samsung Internet", "version" : "3.0", "major" : "3" } @@ -94,7 +176,7 @@ "ua" : "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; GTB5; Avant Browser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)", "expect" : { - "name" : "Avant ", + "name" : "Avant", "version" : "undefined", "major" : "undefined" } @@ -124,11 +206,81 @@ "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; baidubrowser 1.x)", "expect" : { - "name" : "baidubrowser", + "name" : "Baidu", "version" : "1.x", "major" : "1" } }, + { + "desc" : "Baidu", + "ua" : "Mozilla/5.0 (Linux; Android 9; Redmi Note 5 Build/PKQ1.180904.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/110.0.5481.153 Mobile Safari/537.36 bdbrowser/6.4.0.4", + "expect" : + { + "name" : "Baidu", + "version" : "6.4.0.4", + "major" : "6" + } + }, + { + "desc" : "Baidu", + "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.4.9999.1900 Safari/537.31 BDSpark/26.4", + "expect" : + { + "name" : "Baidu", + "version" : "26.4", + "major" : "26" + } + }, + { + "desc" : "Baidu", + "ua" : "Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) BaiduHD/5.4.0.0 Mobile/10A406 Safari/8536.25", + "expect" : + { + "name" : "Baidu", + "version" : "5.4.0.0", + "major" : "5" + } + }, + { + "desc" : "BaiDu Browser", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 BIDUBrowser/8.7 Safari/537.36", + "expect" : + { + "name" : "Baidu", + "version" : "8.7", + "major" : "8" + } + }, + { + "desc" : "baidu app on iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16C101 main%2F1.0 baiduboxapp/11.12.0.18 (Baidu; P2 12.1.2)", + "expect" : + { + "name" : "Baidu", + "version" : "11.12.0.18", + "major" : "11" + } + }, + { + "desc" : "baidu app on Android", + "ua" : "Mozilla/5.0 (Linux; Android 8.1.0; BKK-AL10 Build/HONORBKK-AL10; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.83 Mobile Safari/537.36 T7/11.11 baiduboxapp/11.11.0.0 (Baidu; P1 8.1.0)", + "expect" : + { + "name" : "Baidu", + "version" : "11.11.0.0", + "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", "ua" : "Mozilla/5.0 (X11; 78; CentOS; US-en) AppleWebKit/527+ (KHTML, like Gecko) Bolt/0.862 Version/3.0 Safari/523.15", @@ -199,6 +351,46 @@ "major" : "100" } }, + { + "desc" : "Chrome 112.0 on Win10", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Chrome", + "version" : "112.0.0.0", + "major" : "112" + } + }, + { + "desc" : "Chrome 112.0 on macOS", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Chrome", + "version" : "112.0.0.0", + "major" : "112" + } + }, + { + "desc" : "Chrome 111.0 on Linux", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Chrome", + "version" : "111.0.0.0", + "major" : "111" + } + }, + { + "desc" : "Chrome 111.0 on ChromeOS", + "ua" : "Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Chrome", + "version" : "111.0.0.0", + "major" : "111" + } + }, { "desc" : "Chrome Headless", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome Safari/537.36", @@ -234,7 +426,7 @@ "ua" : "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3", "expect" : { - "name" : "Chrome", + "name" : "Mobile Chrome", "version" : "19.0.1084.60", "major" : "19" } @@ -254,7 +446,7 @@ "ua" : "Mozilla/5.0 (Linux; U; Android-4.0.3; en-us; Galaxy Nexus Build/IML74K) AppleWebKit/535.7 (KHTML, like Gecko) CrMo/16.0.912.75 Mobile Safari/535.7", "expect" : { - "name" : "Chrome", + "name" : "Mobile Chrome", "version" : "16.0.912.75", "major" : "16" } @@ -269,6 +461,36 @@ "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" : "Dragon", + "version" : "16.1.1.0", + "major" : "16" + } + }, + { + "desc" : "Comodo Dragon", + "ua" : "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Dragon/98.0.4758.102 Chrome/98.0.4758.102 Safari/537.36", + "expect" : + { + "name" : "Dragon", + "version" : "98.0.4758.102", + "major" : "98" + } + }, + { + "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", "ua" : "Dillo/2.2", @@ -289,6 +511,16 @@ "major" : "2" } }, + { + "desc" : "Dooble", + "ua" : "Mozilla/5.0 (X11; Haiku BePC) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.15.17 Chrome/87.0.4280.144 Safari/537.36 Dooble/2023.12.25 Dooble/2023.12.25", + "expect" : + { + "name" : "Dooble", + "version" : "2023.12.25", + "major" : "2023" + } + }, { "desc" : "Doris", "ua" : "Doris/1.15 [en] (Symbian)", @@ -299,6 +531,16 @@ "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", "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", @@ -309,6 +551,26 @@ "major" : "5" } }, + { + "desc" : "Ecosia on Android", + "ua" : "Mozilla/5.0 (Linux; Android 10; SM-G975U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36 (Ecosia android@85.0.4183.127)", + "expect" : + { + "name" : "Ecosia", + "version" : "85.0.4183.127", + "major" : "85" + } + }, + { + "desc" : "Ecosia on iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Mobile/14G60 (Ecosia ios@3.0.1.533)", + "expect" : + { + "name" : "Ecosia", + "version" : "3.0.1.533", + "major" : "3" + } + }, { "desc" : "Epiphany", "ua" : "Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7) Gecko/20040628 Epiphany/1.2.6", @@ -329,6 +591,16 @@ "major" : "5" } }, + { + "desc" : "Go Browser", + "ua" : "NokiaE66/GoBrowser/2.0.297", + "expect" : + { + "name" : "GoBrowser", + "version" : "2.0.297", + "major" : "2" + } + }, { "desc" : "Waterfox", "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:55.0) Gecko/20100101 Firefox/55.2.2 Waterfox/55.2.2", @@ -366,7 +638,8 @@ { "name" : "Facebook", "version" : "35.0.0.48.273", - "major" : "35" + "major" : "35", + "type" : "inapp" } }, { @@ -376,7 +649,8 @@ { "name" : "Facebook", "version" : "91.0.0.41.73", - "major" : "91" + "major" : "91", + "type" : "inapp" } }, { @@ -386,7 +660,30 @@ { "name" : "Facebook", "version" : "undefined", - "major" : "undefined" + "major" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Klarna in-App Browser for iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Klarna/23.36.223", + "expect" : + { + "name" : "Klarna", + "version" : "23.36.223", + "major" : "23", + "type" : "inapp" + } + }, + { + "desc" : "Klarna in-App Browser for Android", + "ua" : "Mozilla/5.0 (Linux; Android 12; moto g(60)s Build/S3RLS32.114-25-13; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 Klarna/23.36.215", + "expect" : + { + "name" : "Klarna", + "version" : "23.36.215", + "major" : "23", + "type" : "inapp" } }, { @@ -396,7 +693,8 @@ { "name" : "Instagram", "version" : "142.0.0.22.109", - "major" : "142" + "major" : "142", + "type" : "inapp" } }, { @@ -508,6 +806,26 @@ "major" : "1" } }, + { + "desc" : "Helio", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36 Helio/0.98.20", + "expect" : + { + "name" : "Helio", + "version" : "0.98.20", + "major" : "0" + } + }, + { + "desc" : "HeyTap", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.61 Safari/537.36 HeyTapBrowser/40.8.10.1", + "expect" : + { + "name" : "HeyTap", + "version" : "40.8.10.1", + "major" : "40" + } + }, { "desc" : "HuaweiBrowser", "ua" : "Mozilla/5.0 (Linux; Android 6.0.1; LYA-AL00īŧ›HMSCore/4.0.0 GMS/10.4 ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.64 HuaweiBrowser/10.0.3.102 Mobile Safari/537.36", @@ -558,6 +876,16 @@ "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", "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092921 IceCat/3.0.3-g1", @@ -618,6 +946,46 @@ "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" : "Iron", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Iron Safari/537.36", + "expect" : + { + "name" : "Iron", + "version" : "129.0.0.0", + "major" : "129" + } + }, + { + "desc" : "Iron", + "ua" : "Mozilla/5.0 (Linux; Android 11; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Iron Safari/537.36", + "expect" : + { + "name" : "Iron", + "version" : "113.0.0.0", + "major" : "113" + } + }, + { + "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", "ua" : "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031016 K-Meleon/0.8.2", @@ -638,6 +1006,16 @@ "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", "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux; X11; x86_64) KHTML/3.5.6 (like Gecko) (Kubuntu)", @@ -659,41 +1037,153 @@ } }, { - "desc" : "LINE on Android", - "ua" : "Mozilla/5.0 (Linux; Android 5.0; ASUS_Z00AD Build/LRX21V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/51.0.2704.81 Mobile Safari/537.36 Line/6.5.1/IAB", + "desc" : "Otter", + "ua" : "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/602.1 (KHTML, like Gecko) Otter/1.0.81", "expect" : { - "name" : "Line", - "version" : "6.5.1", - "major" : "6" + "name" : "Otter", + "version" : "1.0.81", + "major" : "1" } }, { - "desc" : "LINE on iOS", - "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 11_2_6 like Mac OS X) AppleWebKit/604.5.6 (KHTML, like Gecko) Mobile/15D100 Safari Line/8.4.1", + "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" : "Line", - "version" : "8.4.1", - "major" : "8" + "name" : "Pico Browser", + "version" : "3.3.22", + "major" : "3" } }, { - "desc" : "Lunascape", - "ua" : "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.2) Gecko/20090804 Firefox/3.5.2 Lunascape/5.1.4.5", + "desc" : "PicoBrowser", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.8.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.38 Chrome/105.0.5195.68 VR Safari/537.36", "expect" : { - "name" : "Lunascape", - "version" : "5.1.4.5", - "major" : "5" + "name" : "Pico Browser", + "version" : "3.3.38", + "major" : "3" } }, { - "desc" : "Lynx", - "ua" : "Lynx/2.8.5dev.16 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/0.9.6b", + "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" : "Lynx", + "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", + "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", + "expect" : + { + "name" : "Smart Lenovo Browser", + "version" : "8.0.0.10171", + "major" : "8" + } + }, + { + "desc" : "Smart Lenovo Browser", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 SLBrowser/9.0.0.9011 SLBChan/10", + "expect" : + { + "name" : "Smart Lenovo Browser", + "version" : "9.0.0.9011", + "major" : "9" + } + }, + { + "desc" : "Surf Browser", + "ua" : "Mozilla/5.0 (Android 6.0; HUAWEI ALE-L21) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36 SurfBrowser/3.0", + "expect" : + { + "name" : "Surf", + "version" : "3.0", + "major" : "3" + } + }, + { + "desc" : "LG Browser", + "ua" : "Mozilla/5.0 (Unknown; Linux armv7l) AppleWebKit/537.1+ (KHTML, like Gecko) Safari/537.1+ LG Browser/6.00.00(+mouse+3D+SCREEN+TUNER; LGE; 47LA621V-ZD; 04.28.17; 0x00000001;); LG NetCast.TV-2013 /04.28.17 (LG, 47LA621V-ZD, wired)", + "expect" : + { + "name" : "LG Browser", + "version" : "6.00.00", + "major" : "6" + } + }, + { + "desc" : "Ladybird", + "ua" : "Mozilla/5.0 (Linux; x86_64) Ladybird/1.0", + "expect" : + { + "name" : "Ladybird", + "version" : "1.0", + "major" : "1" + } + }, + { + "desc" : "LibreWolf", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:97.0) Gecko/20100101 Firefox/97.0 LibreWolf/97.0.1", + "expect" : + { + "name" : "LibreWolf", + "version" : "97.0.1", + "major" : "97" + } + }, + { + "desc" : "LINE on Android", + "ua" : "Mozilla/5.0 (Linux; Android 5.0; ASUS_Z00AD Build/LRX21V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/51.0.2704.81 Mobile Safari/537.36 Line/6.5.1/IAB", + "expect" : + { + "name" : "Line", + "version" : "6.5.1", + "major" : "6", + "type" : "inapp" + } + }, + { + "desc" : "LINE on iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 11_2_6 like Mac OS X) AppleWebKit/604.5.6 (KHTML, like Gecko) Mobile/15D100 Safari Line/8.4.1", + "expect" : + { + "name" : "Line", + "version" : "8.4.1", + "major" : "8", + "type" : "inapp" + } + }, + { + "desc" : "Lunascape", + "ua" : "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.2) Gecko/20090804 Firefox/3.5.2 Lunascape/5.1.4.5", + "expect" : + { + "name" : "Lunascape", + "version" : "5.1.4.5", + "major" : "5" + } + }, + { + "desc" : "Lynx", + "ua" : "Lynx/2.8.5dev.16 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/0.9.6b", + "expect" : + { + "name" : "Lynx", "version" : "2.8.5dev.16", "major" : "2" } @@ -709,7 +1199,57 @@ } }, { - "desc" : "Maxthon", + "desc" : "Maxthon on Android", + "ua" : "Mozilla/5.0 (Linux; Android 5.1.1; KFAUWI Build/LVY48F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.127 Safari/537.36 MxBrowser/4.3.5.2000", + "expect" : + { + "name" : "Maxthon", + "version" : "4.3.5.2000", + "major" : "4" + } + }, + { + "desc" : "Maxthon on iOS", + "ua" : "Mozilla/5.0 (iPad; CPU OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/10.0 Mobile/15E148 Safari/602.1 MXiOS/5.4.5.2", + "expect" : + { + "name" : "Maxthon", + "version" : "5.4.5.2", + "major" : "5" + } + }, + { + "desc" : "Maxthon on Linux", + "ua" : "Mozilla/5.0 (X11; Linux i686; Ubuntu 14.04.3 LTS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.0 Maxthon/1.0.5.3 Safari/537.36", + "expect" : + { + "name" : "Maxthon", + "version" : "1.0.5.3", + "major" : "1" + } + }, + { + "desc" : "Maxthon on macOS", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6 Maxthon/5.1.102", + "expect" : + { + "name" : "Maxthon", + "version" : "5.1.102", + "major" : "5" + } + }, + { + "desc" : "Maxthon on Windows Server 2003", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; MyIE2; .NET CLR 1.1.4322)", + "expect" : + { + "name" : "Maxthon", + "version" : "undefined", + "major" : "undefined" + } + }, + { + "desc" : "Maxthon on Windows XP", "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; Maxthon; .NET CLR 1.1.4322)", "expect" : { @@ -718,6 +1258,16 @@ "major" : "undefined" } }, + { + "desc" : "Maxthon on Windows 10", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36 Maxthon/5.2.7.2000", + "expect" : + { + "name" : "Maxthon", + "version" : "5.2.7.2000", + "major" : "5" + } + }, { "desc" : "Midori", "ua" : "Midori/0.2.2 (X11; Linux i686; U; en-us) WebKit/531.2+", @@ -758,6 +1308,16 @@ "major" : "4" } }, + { + "desc" : "Mobile Safari", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Safari/604.1", + "expect" : + { + "name" : "Mobile Safari", + "version" : "undefined", + "major" : "undefined" + } + }, { "desc" : "Mosaic", "ua" : "NCSA_Mosaic/2.6 (X11; SunOS 4.1.3 sun4m)", @@ -828,6 +1388,26 @@ "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", "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", @@ -958,6 +1538,26 @@ "major" : "12" } }, + { + "desc" : "Opera GX on Android", + "ua" : "Mozilla/5.0 (Linux; Android 10; Redmi Note 8 Pro Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.168 Mobile Safari/537.36 OPX/2", + "expect" : + { + "name" : "Opera GX", + "version" : "2", + "major" : "2" + } + }, + { + "desc" : "Opera GX on Windows", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36 OPR/60.0.3255.50747 OPRGX/60.0.3255.50747", + "expect" : + { + "name" : "Opera GX", + "version" : "60.0.3255.50747", + "major" : "60" + } + }, { "desc" : "Opera Tablet", "ua" : "Opera/9.80 (Windows NT 6.1; Opera Tablet/15165; U; en) Presto/2.8.149 Version/11.1", @@ -988,6 +1588,16 @@ "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", "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.2 Safari/534.34", @@ -1019,7 +1629,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", "expect" : { @@ -1028,6 +1638,36 @@ "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" : "Quark", + "ua" : "mozilla/5.0 (windows nt 10.0; win64; x64) applewebkit/537.36 (khtml, like gecko) chrome/112.0.0.0 safari/537.36 quarkpc/1.5.5.75", + "expect" : + { + "name" : "Quark", + "version" : "1.5.5.75", + "major" : "1" + } + }, { "desc" : "QupZilla", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) QupZilla/1.8.9 Safari/538.1", @@ -1038,6 +1678,26 @@ "major" : "1" } }, + { + "desc" : "qutebrowser", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) qutebrowser/2.4.0 QtWebEngine/5.15.6 Chrome/95.0.4628.2 Safari/537.36", + "expect" : + { + "name" : "qutebrowser", + "version" : "2.4.0", + "major" : "2" + } + }, + { + "desc" : "Rekonq 2", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.21 (KHTML, like Gecko) rekonq/2.2.1 Safari/537.21", + "expect" : + { + "name" : "rekonq", + "version" : "2.2.1", + "major" : "2" + } + }, { "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", @@ -1064,20 +1724,60 @@ "expect" : { "name" : "Safari", - "version" : "2.0.4", - "major" : "2" + "version" : "1", + "major" : "1" } }, { - "desc" : "Samsung Browser", + "desc" : "Samsung Internet for Android", "ua" : "Mozilla/5.0 (Linux; Android 6.0.1; SAMSUNG-SM-G925A Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36", "expect" : { - "name" : "Samsung Browser", + "name" : "Samsung Internet", + "version" : "4.0", + "major" : "4" + } + }, + { + "desc" : "Samsung Internet for Tizen Mobile", + "ua" : "Mozilla/5.0 (Linux; Tizen 2.3; SAMSUNG SM-Z130H) AppleWebKit/537.3 (KHTML, like Gecko) SamsungBrowser/1.0 Mobile Safari/537.3", + "expect" : + { + "name" : "Samsung Internet", + "version" : "1.0", + "major" : "1" + } + }, + { + "desc" : "Samsung Internet for Smart-TV", + "ua" : "Mozilla/5.0 (SMART-TV; Linux; Tizen 2.3) AppleWebkit/538.1 (KHTML, like Gecko) SamsungBrowser/1.0 TV Safari/538.1", + "expect" : + { + "name" : "Samsung Internet", + "version" : "1.0", + "major" : "1" + } + }, + { + "desc" : "Samsung Internet for Gear VR", + "ua" : "Mozilla/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925K Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile VR Safari/537.36", + "expect" : + { + "name" : "Samsung Internet", "version" : "4.0", "major" : "4" } }, + { + "desc" : "Samsung Internet in Redmi 8A", + "ua" : "Mozilla/5.0 (Linux; Android 10; Redmi 8A) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect" : + { + "name" : "Samsung Internet", + "version" : "23.0", + "major" : "23" + } + }, { "desc" : "SeaMonkey", "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1b4pre) Gecko/20090405 SeaMonkey/2.0b1pre", @@ -1117,17 +1817,68 @@ "version" : "2.0", "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" : "SlimBoat", + "ua" : "Mozilla/5.0 (Windows NT 5.2) AppleWebKit/534.34 (KHTML, like Gecko) SlimBoat/1.1.23 Chrome/11.0.696.7 Version/5.1 Safari/534.34", + "expect" : + { + "name" : "SlimBoat", + "version" : "1.1.23", + "major" : "1" + } }, { "desc" : "SlimBrowser", "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SlimBrowser)", "expect" : { - "name" : "Slim", + "name" : "SlimBrowser", "version" : "undefined", "major" : "undefined" } }, + { + "desc" : "Slimjet", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36 Slimjet/20.0.2.0", + "expect" : + { + "name" : "Slimjet", + "version" : "20.0.2.0", + "major" : "20" + } + }, { "desc" : "Swiftfox", "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko/20061024 Firefox/2.0 (Swiftfox)", @@ -1229,7 +1980,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", "expect" : { @@ -1278,6 +2029,46 @@ "major" : "1" } }, + { + "desc" : "Vivaldi on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.88 Safari/537.36 Vivaldi/2.4.1488.36", + "expect" : + { + "name" : "Vivaldi", + "version" : "2.4.1488.36", + "major" : "2" + } + }, + { + "desc" : "Vivo Browser", + "ua" : "Mozilla/5.0 (Linux; Android 13; 23049RAD8C; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36 VivoBrowser/16.7.1.1", + "expect" : + { + "name" : "Vivo Browser", + "version" : "16.7.1.1", + "major" : "16" + } + }, + { + "desc" : "w3m", + "ua" : "w3m/0.5.1", + "expect" : + { + "name" : "w3m", + "version" : "0.5.1", + "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" + } + }, { "desc" : "Yandex", "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/536.5 (KHTML, like Gecko) YaBrowser/1.0.1084.5402 Chrome/19.0.1084.5402 Safari/536.5", @@ -1288,6 +2079,66 @@ "major" : "1" } }, + { + "desc" : "Yandex", + "ua" : "Mozilla/5.0 (Linux; arm_64; Android 11; M2101K7AG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.125 YaApp_Android/22.70 YaSearchBrowser/22.70 BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect" : + { + "name" : "Yandex", + "version" : "22.70", + "major" : "22" + } + }, + { + "desc" : "Yandex", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 YaBrowser/23.3.0.2246 Yowser/2.5 Safari/537.36", + "expect" : + { + "name" : "Yandex", + "version" : "23.3.0.2246", + "major" : "23" + } + }, + { + "desc" : "Yandex on Android", + "ua" : "Mozilla/5.0 (Linux; arm_64; Android 13; SM-G965F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.76 YaBrowser/21.3.4.59 Mobile Safari/537.36", + "expect" : + { + "name" : "Yandex", + "version" : "21.3.4.59", + "major" : "21" + } + }, + { + "desc" : "Yandex on iPhone", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 YaBrowser/23.3.3.330 Mobile/15E148 Safari/604.1", + "expect" : + { + "name" : "Yandex", + "version" : "23.3.3.330", + "major" : "23" + } + }, + { + "desc" : "Yandex on iPad", + "ua" : "Mozilla/5.0 (iPad; CPU OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 YaBrowser/23.3.3.330 Mobile/15E148 Safari/605.1", + "expect" : + { + "name" : "Yandex", + "version" : "23.3.3.330", + "major" : "23" + } + }, + { + "desc" : "Yandex on iPod", + "ua" : "Mozilla/5.0 (iPod touch; CPU iPhone 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 YaBrowser/23.3.3.330 Mobile/15E148 Safari/605.1", + "expect" : + { + "name" : "Yandex", + "version" : "23.3.3.330", + "major" : "23" + } + }, { "desc" : "Puffin", "ua" : "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo P2a42 Build/MMB29M; en-us) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Mobile Safari/537.36 Puffin/6.0.8.15804AP", @@ -1298,6 +2149,16 @@ "major" : "6" } }, + { + "desc" : "Puffin", + "ua" : "Mozilla/5.0 (Linux; Android 7.1.1; ZTE BLADE A0620 Build/NMF26F; ru-ru) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36 Puffin/9.2.0.50586AP", + "expect" : + { + "name" : "Puffin", + "version" : "9.2.0.50586AP", + "major" : "9" + } + }, { "desc" : "Microsoft Edge 0.1", "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0", @@ -1368,6 +2229,26 @@ "major" : "74" } }, + { + "desc" : "Microsoft Edge WebView", + "ua" : "Mozilla/5.0 (Windows IoT 10.0; Android 6.0.1; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Mobile Safari/537.36 Edge/18.17763", + "expect" : + { + "name" : "Edge WebView", + "version" : "18.17763", + "major" : "18" + } + }, + { + "desc" : "Microsoft Edge WebView2", + "ua" : "Mozilla/5.0 (Linux; Android 11; SM-G991B Build/RP1A.200720.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.120 Mobile Safari/537.36 EdgW/1.0", + "expect" : + { + "name" : "Edge WebView2", + "version" : "91.0.4472.120", + "major" : "91" + } + }, { "desc" : "Iridium", "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Iridium/43.8 Safari/537.36 Chrome/43.0.2357.132", @@ -1383,17 +2264,27 @@ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) FxiOS/1.1 Mobile/13B143 Safari/601.1.46", "expect" : { - "name" : "Firefox", + "name" : "Mobile Firefox", "version" : "1.1", "major" : "1" } }, + { + "desc" : "Firefox on iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/112.0 Mobile/15E148 Safari/605.1.15", + "expect" : + { + "name" : "Mobile Firefox", + "version" : "112.0", + "major" : "112" + } + }, { "desc" : "Firefox iOS using iPad", "ua" : "Mozilla/5.0 (iPad; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4", "expect" : { - "name" : "Firefox", + "name" : "Mobile Firefox", "version" : "1.0", "major" : "1" } @@ -1403,7 +2294,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", "expect" : { - "name" : "QQ", + "name" : "QQBrowser", "version" : "6.5.3.410", "major" : "6" } @@ -1413,37 +2304,17 @@ "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" : { - "name" : "QQ", + "name" : "QQBrowser", "version" : "6.5.8.2910", "major" : "6" } }, - { - "desc" : "baidu app on iOS", - "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16C101 main%2F1.0 baiduboxapp/11.12.0.18 (Baidu; P2 12.1.2)", - "expect" : - { - "name" : "baiduboxapp", - "version" : "11.12.0.18", - "major" : "11" - } - }, - { - "desc" : "baidu app on Android", - "ua" : "Mozilla/5.0 (Linux; Android 8.1.0; BKK-AL10 Build/HONORBKK-AL10; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.83 Mobile Safari/537.36 T7/11.11 baiduboxapp/11.11.0.0 (Baidu; P1 8.1.0)", - "expect" : - { - "name" : "baiduboxapp", - "version" : "11.11.0.0", - "major" : "11" - } - }, { "desc" : "WeChat Desktop for Windows Built-in Browser", "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 MicroMessenger/6.5.2.501 NetType/WIFI WindowsWechat QBCore/3.43.901.400 QQBrowser/9.0.2524.400", "expect" : { - "name" : "WeChat(Win) Desktop", + "name" : "WeChat", "version" : "3.43.901.400", "major" : "3" } @@ -1453,7 +2324,7 @@ "ua" : "mozilla/5.0 (windows nt 10.0; wow64) applewebkit/537.36 (khtml, like gecko) chrome/53.0.2785.116 safari/537.36 qbcore/4.0.1301.400 qqbrowser/9.0.2524.400 mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) chrome/81.0.4044.138 safari/537.36 nettype/wifi micromessenger/7.0.20.1781(0x6700143b) windowswechat", "expect" : { - "name" : "WeChat(Win) Desktop", + "name" : "WeChat", "version" : "4.0.1301.400", "major" : "4" } @@ -1475,7 +2346,8 @@ { "name" : "GSA", "version" : "30.1.161623614", - "major" : "30" + "major" : "30", + "type" : "inapp" } }, { @@ -1483,35 +2355,27 @@ "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0", "expect" : { - "name" : "MetaSr" - } - }, - { - "desc" : "LieBao Browser", - "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.154 Safari/537.36 LBBROWSER", - "expect" : - { - "name" : "LBBROWSER" + "name" : "Sogou Explorer", + "version" : "1.0", + "major" : "1" } }, { - "desc" : "BaiDu Browser", - "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 BIDUBrowser/8.7 Safari/537.36", + "desc" : "Sogou Mobile Browser", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 SogouMSE,SogouMobileBrowser/3.7.4", "expect" : { - "name" : "BIDUBrowser", - "version" : "8.7", - "major" : "8" + "name" : "Sogou Mobile", + "version" : "3.7.4", + "major" : "3" } }, { - "desc" : "2345 Browser", - "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.90 Safari/537.36 2345Explorer/9.2.1.17116", + "desc" : "LieBao Browser", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.154 Safari/537.36 LBBROWSER", "expect" : { - "name" : "2345Explorer", - "version" : "9.2.1.17116", - "major" : "9" + "name" : "LBBROWSER" } }, { @@ -1569,7 +2433,48 @@ "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [LinkedInApp]", "expect" : { - "name" : "LinkedIn" + "name" : "LinkedIn", + "type" : "inapp" + } + }, + { + "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" } }, { @@ -1627,5 +2532,190 @@ "version": "9.0", "major" : "9" } + }, + { + "desc" : "KakaoTalk App Android", + "ua" : "Mozilla/5.0 (Linux; Android 12; SM-G988N Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.79 Mobile Safari/537.36;KAKAOTALK 2409760", + "expect" : { + "name" : "KAKAOTALK", + "version": "2409760", + "major" : "2409760", + "type" : "inapp" + } + }, + { + "desc" : "KakaoStory App Android", + "ua" : "Mozilla/5.0 (Linux; Android 12; SM-G988N Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.79 Mobile Safari/537.36 KAKAOSTORY/6.8.3_21046", + "expect" : { + "name" : "KAKAOSTORY", + "version": "6.8.3_21046", + "major" : "6", + "type" : "inapp" + } + }, + { + "desc" : "KakaoTalk App iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU; iPhone OS 15_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 BizWebView KAKAOTALK 9.7.6", + "expect" : { + "name" : "KAKAOTALK", + "version": "9.7.6", + "major" : "9", + "type" : "inapp" + } + }, + { + "desc" : "Naver App Android", + "ua" : "Mozilla/5.0 (Linux; Android 12; SM-G988N Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.232 Whale/1.0.0.0 Crosswalk/26.90.3.21 Mobile Safari/537.36 NAVER(inapp; search; 1010; 11.11.2)", + "expect" : { + "name" : "NAVER", + "version": "11.11.2", + "major" : "11", + "type" : "inapp" + } + }, + { + "desc" : "Naver App iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Mobile/15E148 Safari/605.1 NAVER(inapp; search; 720; 10.25.0; 11PRO)", + "expect" : { + "name" : "NAVER", + "version": "10.25.0", + "major" : "10", + "type" : "inapp" + } + }, + { + "desc" : "Daum App Android", + "ua" : "Mozilla/5.0 (Linux; Android 11; SM-G970N Build/RP1A.200720.012; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36 DaumApps/7.5.0 DaumDevice/mobile", + "expect" : { + "name" : "Daum", + "version": "7.5.0", + "major" : "7", + "type" : "inapp" + } + }, + { + "desc" : "Daum App iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Safari/605.1.15 Mobile/15E148 DaumApps/7.5.1 DaumDevice/mobile", + "expect" : { + "name" : "Daum", + "version": "7.5.1", + "major" : "7", + "type" : "inapp" + } + }, + { + "desc" : "TikTok", + "ua" : "Mozilla/5.0 (Linux; Android 11; 21061119AG Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.131 Mobile Safari/537.36 trill_2022109040 JsSdk/1.0 NetType/MOBILE Channel/googleplay AppName/musical_ly app_version/21.9.4 ByteLocale/ru-RU ByteFullLocale/ru-RU Region/KG BytedanceWebview/d8a21c6", + "expect" : { + "name" : "TikTok", + "version": "21.9.4", + "major" : "21", + "type" : "inapp" + } + }, + { + "desc" : "TikTok", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 14_8 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 musical_ly_21.1.0 JsSdk/2.0 NetType/4G Channel/App Store ByteLocale/ru Region/RU ByteFullLocale/ru-RU isDarkMode/1 WKWebView/1 BytedanceWebview/d8a21c6", + "expect" : { + "name" : "TikTok", + "version": "21.1.0", + "major" : "21", + "type" : "inapp" + } + }, + { + "desc" : "TikTok", + "ua" : "Mozilla/5.0 (Linux; Android 10; STK-LX1 Build/HONORSTK-LX1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/110.0.5481.153 Mobile Safari/537.36 musical_ly_2022803040 JsSdk/1.0 NetType/WIFI Channel/huaweiadsglobal_int AppName/musical_ly app_version/28.3.4 ByteLocale/en ByteFullLocale/en Region/IQ Spark/1.2.7-alpha.8 AppVersion/28.3.4 PIA/1.5.11 BytedanceWebview/d8a21c6", + "expect" : { + "name" : "TikTok", + "version": "28.3.4", + "major" : "28", + "type" : "inapp" + } + }, + { + "desc" : "Chrome Mobile", + "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36", + "expect" : + { + "name" : "Mobile Chrome", + "version" : "58.0.3029.83", + "major" : "58" + } + }, + { + "desc" : "Firefox Mobile", + "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5X Build/N2G47W) AppleWebKit/537.36 (KHTML, like Gecko) FxiOS/7.5b3349 Mobile/14F89 Safari/603.2.4", + "expect" : + { + "name" : "Mobile Firefox", + "version" : "7.5b3349", + "major" : "7" + } + }, + { + "desc" : "Firefox Mobile", + "ua" : "Mozilla/5.0 (Android 5.0; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0", + "expect" : + { + "name" : "Mobile Firefox", + "version" : "41.0", + "major" : "41" + } + }, + { + "desc" : "Snapchat", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Snapchat/12.33.0.36 (like Safari/8614.1.25.0.31, panda)", + "expect" : + { + "name" : "Snapchat", + "version" : "12.33.0.36", + "major" : "12", + "type" : "inapp" + } + }, + { + "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", + "type" : "inapp" + } + }, + { + "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", + "type" : "inapp" + } + }, + { + "desc" : "Zalo on iOS", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Zalo/20.05.01 Mobile/15E148", + "expect" : + { + "name" : "Zalo", + "version" : "20.05.01", + "major" : "20", + "type" : "inapp" + } + }, + { + "desc" : "Zalo on Android", + "ua" : "Mozilla/5.0 (Linux; Android 10; Vsmart Live Build/QKQ1.190918.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Mobile Safari/537.36 Zalo/20.04.02.r1", + "expect" : + { + "name" : "Zalo", + "version" : "20.04.02.r1", + "major" : "20", + "type" : "inapp" + } } -] +] \ No newline at end of file diff --git a/test/cpu-test.json b/test/data/ua/cpu/cpu-all.json similarity index 66% rename from test/cpu-test.json rename to test/data/ua/cpu/cpu-all.json index cf314f9c8..e9b749e7d 100644 --- a/test/cpu-test.json +++ b/test/data/ua/cpu/cpu-all.json @@ -7,6 +7,14 @@ "architecture" : "ia32" } }, + { + "desc" : "i686", + "ua" : "Mozilla/5.0 (X11; U; CrOS i686 9.10.0; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.253.0 Safari/532.5", + "expect" : + { + "architecture" : "ia32" + } + }, { "desc" : "i386", "ua" : "Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.7) Gecko/20040628 Epiphany/1.2.6", @@ -23,6 +31,46 @@ "architecture" : "amd64" } }, + { + "desc" : "Vivaldi on Windows", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18", + "expect" : + { + "architecture" : "amd64" + } + }, + { + "desc" : "Vivaldi on Windows", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18", + "expect" : + { + "architecture" : "amd64" + } + }, + { + "desc" : "Vivaldi on Linux", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18", + "expect" : + { + "architecture" : "amd64" + } + }, + { + "desc" : "Vivaldi on Linux", + "ua" : "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Vivaldi/6.0.2979.18", + "expect" : + { + "architecture" : "ia32" + } + }, + { + "desc": "Xiaomi POCO M2 Pro", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; POCO M2 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 YaBrowser/22.11.7.42.00 SA/3 Mobile Safari/537.36", + "expect" : + { + "architecture" : "arm64" + } + }, { "desc" : "win64", "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Win64; x64; Trident/6.0; .NET4.0E; .NET4.0C)", @@ -39,6 +87,22 @@ "architecture" : "amd64" } }, + { + "desc" : "Alpha", + "ua" : "Mozilla/3.01 (WinNT; I) [AXP]", + "expect" : + { + "architecture" : "alpha" + } + }, + { + "desc" : "Alpha", + "ua" : "Mozilla/5.0 (X11; OpenBSD alpha; rv:78.0) Gecko/20100101 Firefox/78.0", + "expect" : + { + "architecture" : "alpha" + } + }, { "desc" : "ARM", "ua" : "Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537", @@ -127,6 +191,22 @@ "architecture" : "arm64" } }, + { + "desc" : "Google Search App", + "ua" : "Mozilla/5.0 (Linux; Android 9; JAT-LX1 Build/HONORJAT-LX1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.96 Mobile Safari/537.36 GoogleApp/11.11.10.21.arm", + "expect" : + { + "architecture" : "arm" + } + }, + { + "desc" : "Google Search App", + "ua" : "Mozilla/5.0 (Linux; Android 6.0; M5s Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.147 Mobile Safari/537.36 GSA/12.40.17.23.arm64", + "expect" : + { + "architecture" : "arm64" + } + }, { "desc" : "Pocket PC", "ua" : "Opera/9.7 (Windows Mobile; PPC; Opera Mobi/35166; U; en) Presto/2.2.1", @@ -206,5 +286,21 @@ { "architecture" : "irix64" } + }, + { + "desc" : "68k", + "ua" : "'Mozilla/1.1 (Macintosh; U; 68K)'", + "expect" : + { + "architecture" : "68k" + } + }, + { + "desc" : "x86", + "ua" : "Mozilla/5.0 (Photon; U; QNX x86pc; en-US; rv:1.8.1.20) Gecko/20090127 BonEcho/2.0.0.20", + "expect" : + { + "architecture" : "ia32" + } } ] diff --git a/test/data/ua/device/_others.json b/test/data/ua/device/_others.json new file mode 100644 index 000000000..06a70fef7 --- /dev/null +++ b/test/data/ua/device/_others.json @@ -0,0 +1,320 @@ +[ + { + "desc": "Generic Android Device", + "ua": "Dalvik/2.1.0 (Linux; U; Android 9; X96mini_RP Build/X96mini_RP)", + "expect": { + "vendor": "Generic", + "model": "X96mini_RP" + } + }, + { + "desc": "Generic Android Device", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; i980 Build/MRA58K)", + "expect": { + "vendor": "Generic", + "model": "i980" + } + }, + { + "desc": "K", + "ua": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "K", + "type": "mobile" + } + }, + { + "desc": "Unknown VR Device", + "ua": "Mozilla/5.0 (Linux; Android 5.0.2; Unknown Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile VR Safari/537.36", + "expect": { + "model": "Unknown", + "type": "xr" + } + }, + { + "desc": "Desktop (IE11 with Tablet string)", + "ua": "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; Tablet PC 2.0; GWX:MANAGED; rv:11.0) like Gecko", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "undefined" + } + }, + { + "desc": "Mobile (DuckDuckGo mobile browser)", + "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", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + }, + { + "desc": "ChangHong Android TV", + "ua": "Mozilla/5.0 (Linux; U; Android 5.1.1; zh-cn; ChangHong Android TV Build/LMY49J) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/10.8 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "ChangHong", + "type": "smarttv" + } + }, + { + "desc": "MStar Android TV", + "ua": "Mozilla/5.0 (Linux; Android 4.3.1; MStar Android TV Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.95 Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "MStar", + "type": "smarttv" + } + }, + { + "desc": "ONIDA Android TV", + "ua": "Mozilla/5.0 (Linux; Android 6.0; ONIDA Android TV Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/48.0.2542.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "ONIDA", + "type": "smarttv" + } + }, + { + "desc": "JVC LT-43V55LFA Smart TV", + "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 OPR/40.0.2207.0 OMI/4.9.0.237.DOM3-OPT.245 Model/Vestel-MB211 VSTVB MB200 HbbTV/1.2.1 (; JVC; MB211; 3.19.4.2; _TV_NT72563_2017 SmartTvA/3.0.0", + "expect": { + "vendor": "JVC", + "model": "MB211", + "type": "smarttv" + } + }, + { + "desc": "JVC LT-43V65LUA Smart TV", + "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36 OPR/40.0.2207.0 OMI/4.9.0.237.DOM3-OPT.245 Model/Vestel-MB130 VSTVB MB100 HbbTV/1.2.1 (; JVC; MB130; 5.7.20.0; _TV_G10_2017;) SmartTvA/3.0.0", + "expect": { + "vendor": "JVC", + "model": "MB130", + "type": "smarttv" + } + }, + { + "desc": "Loewe Smart TV", + "ua": "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH", + "expect": { + "vendor": "LOEWE", + "model": "SL410", + "type": "smarttv" + } + }, + { + "desc": "Issue #747", + "ua": "python-requests/2.25.1", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "undefined" + } + }, + { + "desc": "Issue #454", + "ua": "Mosamzilla/5.0 (Windows; U; Win98; en-US; rv:1.7.5) Gecko/20050603 Netscape/8.0.2", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "undefined" + } + }, + { + "desc": "Samsung SmartTV", + "ua": "Mozilla/5.0 (SMART-TV; X11; Linux armv7l) AppleWebkit/537.42 (KHTML, like Gecko) Safari/537.42", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Android Phone Unidentified Vendor (docomo F-04K)", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; F-04K Build/V15R060P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36", + "expect": { + "model": "F-04K", + "type": "mobile" + } + }, + { + "desc": "Android Tablet Unidentified Vendor (docomo F-02K)", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; F-02K Build/V44R059G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Safari/537.36", + "expect": { + "model": "F-02K", + "type": "tablet" + } + }, + { + "desc": "Android Tablet Unidentified Vendor (docomo d-02K)", + "ua": "Mozilla/5.0 (Linux; Android 9; d-02K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Safari/537.36", + "expect": { + "model": "d-02K", + "type": "tablet" + } + }, + { + "desc": "Android TV", + "ua": "Mozilla/5.0 (Linux; Android 10; 2020/2021 UHD Android TV Build/QTG3.201102.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) version/4.0 Chrome/83.0.4103.101 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Unknown Mobile using Firefox", + "ua": "Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + }, + { + "desc": "Unknown Tablet using Firefox", + "ua": "Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "tablet" + } + }, + { + "desc": "Unknown Mobile using Focus for Android", + "ua": "Mozilla/5.0 (Linux; Android 7.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Focus/1.0 Chrome/59.0.3029.83 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + }, + { + "desc": "Unknown Tablet using Focus for Android", + "ua": "Mozilla/5.0 (Linux; Android 7.0) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Focus/1.0 Chrome/59.0.3029.83 Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "tablet" + } + }, + { + "desc": "Unknown Device using Focus for Android with GeckoView", + "ua": "Mozilla/5.0 (Android 7.0; Mobile; rv:62.0) Gecko/62.0 Firefox/62.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + }, + { + "desc": "Unknown Mobile using Firefox OS", + "ua": "Mozilla/5.0 (Mobile; rv:26.0) Gecko/26.0 Firefox/26.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + }, + { + "desc": "Unknown Tablet using Firefox OS", + "ua": "Mozilla/5.0 (Tablet; rv:26.0) Gecko/26.0 Firefox/26.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "tablet" + } + }, + { + "desc": "Unknown TV using Firefox OS", + "ua": "Mozilla/5.0 (TV; rv:44.0) Gecko/44.0 Firefox/44.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Unknown TV", + "ua": "Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.127 Large Screen Safari/533.4 GoogleTV/ 162671", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Unknown TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.41 (KHTML, like Gecko) Large Screen WebAppManager Safari/537.41", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Unknown VR Device using Firefox Reality", + "ua": "Mozilla/5.0 (Android 10; Mobile VR; rv:123.0) Gecko/123.0 Firefox/123.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "xr" + } + }, + { + "desc": "Unknown VR Device using Wolvic", + "ua": "Mozilla/5.0 (Android 14; Mobile VR; rv:128.0) Gecko/128.0 Firefox/128.0 Wolvic/1.8", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "xr" + } + }, + { + "desc": "Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 HbbTV/1.6.1 ( DRM; LGE/ATMACA/GRAETZ; GR32S1470; WEBOS22 04.41.53; W22_K8AP; DTV_C22L;) LaTivu_1.0.1_2022", + "expect": { + "vendor": "GRAETZ", + "model": "GR32S1470", + "type": "smarttv" + } + }, + { + "desc": "Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 HbbTV/1.5.1 (+DRM; LGE/DUALSHINE/SKYTECH; ST-5090; WEBOS5.0 04.50.63; W50_K6LP; DTV_C20P;)", + "expect": { + "vendor": "SKYTECH", + "model": "ST-5090", + "type": "smarttv" + } + }, + { + "desc": "Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 HbbTV/1.6.1 (+DRM; LGE/SILICONPLAYER/Hyundai; 50HYN3205; WEBOS22 04.42.26; W22_K8LP; DTV_C22P;)", + "expect": { + "vendor": "Hyundai", + "model": "50HYN3205", + "type": "smarttv" + } + }, + { + "desc": "PDA with Windows CE", + "ua": "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "mobile" + } + }, + { + "desc" : "Windows IoT", + "ua" : "Mozilla/5.0 (Windows IoT 10.0; Android 6.0.1; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Mobile Safari/537.36 Edge/18.17763", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "embedded" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/acer.json b/test/data/ua/device/acer.json new file mode 100644 index 000000000..7eff9d80e --- /dev/null +++ b/test/data/ua/device/acer.json @@ -0,0 +1,11 @@ +[ + { + "desc": "Acer Iconia A1-810", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; A1-810 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Safari/537.36", + "expect": { + "vendor": "Acer", + "model": "A1-810", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/advan.json b/test/data/ua/device/advan.json new file mode 100644 index 000000000..16a233106 --- /dev/null +++ b/test/data/ua/device/advan.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Advan M4", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0; ADVAN M4 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Mobile Safari/537.36 OPR/28.0.2254.119214", + "expect": { + "vendor": "ADVAN", + "model": "M4", + "type": "mobile" + } + }, + { + "desc": "Advan S40", + "ua": "Mozilla/5.0 (Linux; Android 7.0; ADVAN S40 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Mobile Safari/537.36 EdgA/79.0.309.58", + "expect": { + "vendor": "ADVAN", + "model": "S40", + "type": "mobile" + } + }, + { + "desc": "Advan Sketsa 2", + "ua": "Mozilla/5.0 (Linux; Android 11; ADVAN 1011) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Safari/537.36", + "expect": { + "vendor": "ADVAN", + "model": "1011", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/alcatel.json b/test/data/ua/device/alcatel.json new file mode 100644 index 000000000..3cd613ee9 --- /dev/null +++ b/test/data/ua/device/alcatel.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Alcatel 4056W", + "ua": "Mozilla/5.0 (Mobile; ALCATEL 4056W; rv:84.0) Gecko/84.0 Firefox/84.0 KAIOS/3.0", + "expect": { + "vendor": "ALCATEL", + "model": "4056W", + "type": "mobile" + } + }, + { + "desc": "Alcatel A564C", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; ALCATEL A564C Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36", + "expect": { + "vendor": "ALCATEL", + "model": "A564C", + "type": "mobile" + } + }, + { + "desc": "Alcatel Go Flip", + "ua": "Mozilla/5.0 (Mobile; ALCATEL4044T; rv:37.0) Gecko/37.0 Firefox/37.0 KaiOS/1.0", + "expect": { + "vendor": "ALCATEL", + "model": "4044T", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/amazon.json b/test/data/ua/device/amazon.json new file mode 100644 index 000000000..6e60318e8 --- /dev/null +++ b/test/data/ua/device/amazon.json @@ -0,0 +1,137 @@ +[ + { + "desc": "Kindle Fire HD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; KFTT Build/IML74K) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.4 Mobile Safari/535.19 Silk-Accelerated=true", + "expect": { + "vendor": "Amazon", + "model": "KFTT", + "type": "tablet" + } + }, + { + "desc": "Kindle Fire HD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; KFTT) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.4 Mobile Safari/535.19 Silk-Accelerated=true", + "expect": { + "vendor": "Amazon", + "model": "KFTT", + "type": "tablet" + } + }, + { + "desc": "Echo Show 5", + "ua": "Mozilla/5.0 (Linux; Android 5.1; AEORK Build/LVY48F; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEORK", + "type": "tablet" + } + }, + { + "desc": "Echo Show 8", + "ua": "Mozilla/5.0 (Linux; Android 7.1; AEOCH) AppleWebKit/537.36 (KHTML, like Gecko) Silk/77.2.21 like Chrome/77.0.3865.92 Mobile Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEOCH", + "type": "tablet" + } + }, + { + "desc": "Echo Show 8", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; AEOCW) AppleWebKit/537.36 (KHTML, like Gecko) Silk/106.3.3 like Chrome/106.0.5249.170 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEOCW", + "type": "tablet" + } + }, + { + "desc": "Echo Show 15", + "ua": "Mozilla/5.0 (Linux; Android 9; AEOHY) AppleWebKit/537.36 (KHTML, like Gecko) Silk/112.6.3 like Chrome/112.0.5615.213 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "AEOHY", + "type": "tablet" + } + }, + { + "desc": "Echo Dot", + "ua": "Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEOBC Build/LVY48F)", + "expect": { + "vendor": "Amazon", + "model": "AEOBC", + "type": "embedded" + } + }, + { + "desc": "Amazon Kindle Fire Tablet", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.3; en-us; KFSAWI Build/KTU84M) AppleWebKit/537.36 (KHTML, like Gecko) Silk/3.66 like Chrome/39.0.2171.93 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KFSAWI", + "type": "tablet" + } + }, + { + "desc": "Amazon Kindle Fire Tablet", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.3; en-us; KFSAWI) AppleWebKit/537.36 (KHTML, like Gecko) Silk/3.66 like Chrome/39.0.2171.93 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KFSAWI", + "type": "tablet" + } + }, + { + "desc": "Amazon Kindle Fire Tablet", + "ua": "Mozilla/5.0 (Linux; Android 9; KFMAWI Build/PS7312; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KFMAWI", + "type": "tablet" + } + }, + { + "desc": "Amazon Fire TV", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; AFTB Build/JDQ39) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.173 Mobile Safari/537.22", + "expect": { + "vendor": "Amazon", + "model": "B", + "type": "smarttv" + } + }, + { + "desc": "Amazon Fire TV", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; AFTT) AppleWebKit/537.36 (KHTML, like Gecko) Silk/86.3.20 like Chrome/86.0.4240.198 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "T", + "type": "smarttv" + } + }, + { + "desc": "Amazon Fire TV", + "ua": "Mozilla/5.0 (Linux; Android 9; AFTKA Build/PS7633.3445N; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.160 Mobile Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KA", + "type": "smarttv" + } + }, + { + "desc": "Amazon Fire 7", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; KFAUWI) AppleWebKit/537.36 (KHTML, like Gecko) Silk/80.5.3 like Chrome/80.0.3987.162 Safari/537.36", + "expect": { + "vendor": "Amazon", + "model": "KFAUWI", + "type": "tablet" + } + }, + { + "desc": "Amazon Alexa Echo Show", + "ua": "AlexaWebMediaPlayer/1.0.200641.0 (Linux;Android 5.1.1)", + "expect": { + "vendor": "Amazon", + "model": "Alexa", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/apple.json b/test/data/ua/device/apple.json new file mode 100644 index 000000000..ea68e8acc --- /dev/null +++ b/test/data/ua/device/apple.json @@ -0,0 +1,137 @@ +[ + { + "desc": "Apple Desktop", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15", + "expect": { + "vendor": "Apple", + "model": "Macintosh", + "type": "undefined" + } + }, + { + "desc": "Apple Watch", + "ua": "atc/1.0 watchOS/7.3.3 model/Watch4,2 hwp/t8006 build/18S830 (6; dt:191)", + "expect": { + "vendor": "Apple", + "model": "watch", + "type": "wearable" + } + }, + { + "desc": "iPad using UCBrowser", + "ua": "Mozilla/5.0 (iPad; U; CPU OS 11_2 like Mac OS X; zh-CN; iPad5,3) AppleWebKit/534.46 (KHTML, like Gecko) UCBrowser/3.0.1.776 U3/ Mobile/10A403 Safari/7543.48.3", + "expect": { + "vendor": "Apple", + "model": "iPad", + "type": "tablet" + } + }, + { + "desc": "iPad Air", + "ua": "Mozilla/5.0 (iPad; CPU OS 12_4_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPad4,1;FBMD/iPad;FBSN/iOS;FBSV/12.4.5;FBSS/2;FBID/tablet;FBLC/en_US;FBOP/5;FBCR/]", + "expect": { + "vendor": "Apple", + "model": "iPad", + "type": "tablet" + } + }, + { + "desc": "iPad using Facebook Browser", + "ua": "Mozilla/5.0 (iPad; CPU OS 14_4_2 like Mac OS X) WebKit/8610 (KHTML, like Gecko) Mobile/18D70 [FBAN/FBIOS;FBDV/iPad7,11;FBMD/iPad;FBSN/iOS;FBSV/14.4.2;FBSS/2;FBID/tablet;FBLC/en_US;FBOP/5]", + "expect": { + "vendor": "Apple", + "model": "iPad", + "type": "tablet" + } + }, + { + "desc": "iPod", + "ua": "Mozilla/5.0 (iPod touch; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53", + "expect": { + "vendor": "Apple", + "model": "iPod touch", + "type": "mobile" + } + }, + { + "desc": "iPhone", + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + }, + { + "desc": "iPhone SE", + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone8,4;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + }, + { + "desc": "iPhone SE using Facebook App", + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone8,4;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + }, + { + "desc": "iPhone 11 Pro Max", + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone12,5;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/3;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + }, + { + "desc": "iPhone XS", + "ua": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBDV/iPhone11,2;FBMD/iPhone;FBSN/iOS;FBSV/13.3.1;FBSS/3;FBID/phone;FBLC/en_US;FBOP/5;FBCR/]", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + }, + { + "desc": "iPod touch", + "ua": "Mozilla/5.0 (iPod touch; CPU iPhone OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A501 Safari/9537.53", + "expect": { + "vendor": "Apple", + "model": "iPod touch", + "type": "mobile" + } + }, + { + "desc": "FaceBook Mobile App", + "ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]", + "expect": { + "vendor": "Apple", + "model": "iPhone12,1", + "type": "mobile" + } + }, + { + "desc": "Apple HomePod", + "ua": "AppleCoreMedia/1.0.0.15D61 (HomePod; U; CPU OS 11_2_5 like Mac OS X; en_us)", + "expect": { + "vendor": "Apple", + "model": "HomePod", + "type": "embedded" + } + }, + { + "desc": "Issue #519", + "ua": "ios/iPhone/14.2/SOME_CUSTOM_APP_VERSION", + "expect": { + "vendor": "Apple", + "model": "iPhone", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/archos.json b/test/data/ua/device/archos.json new file mode 100644 index 000000000..53628c535 --- /dev/null +++ b/test/data/ua/device/archos.json @@ -0,0 +1,227 @@ +[ + { + "desc": "Archos 5", + "ua": "Mozilla/5.0 (Linux; U; Android 1.6; fr-fr; Archos5 Build/Donut) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1", + "expect": { + "vendor": "Archos", + "model": "5", + "type": "tablet" + } + }, + { + "desc": "Archos 40b Titanium Surround", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Archos 40b Titanium Surround Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "40b Titanium Surround", + "type": "mobile" + } + }, + { + "desc": "Archos 40c Titanium v2", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; ARCHOS 40C TIv2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "40C TIv2", + "type": "mobile" + } + }, + { + "desc": "Archos 45 Neon", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Archos 45 Neon Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "45 Neon", + "type": "mobile" + } + }, + { + "desc": "Archos 45 Neon", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; AC45NE Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 YaBrowser/15.6.2311.6088.00 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "AC45NE", + "type": "mobile" + } + }, + { + "desc": "Archos 45B Helium", + "ua": "Mozilla/5.0 (Linux; Android 7.0; AC45BHE Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.109 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "AC45BHE", + "type": "mobile" + } + }, + { + "desc": "Archos 45B Titanium", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Archos 45B Titanium) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "45B Titanium", + "type": "mobile" + } + }, + { + "desc": "Archos 50 Cesium", + "ua": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; ARCHOS; AC50CE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", + "expect": { + "vendor": "Archos", + "model": "AC50CE", + "type": "mobile" + } + }, + { + "desc": "Archos 50B Helium 4G", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; AC50BHE Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "AC50BHE", + "type": "mobile" + } + }, + { + "desc": "Archos 55 diamond Selfie", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Archos 55 diamond Selfie Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.91 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "55 diamond Selfie", + "type": "mobile" + } + }, + { + "desc": "Archos 80 G9", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; zh-tw; ARCHOS 80G9 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "Archos", + "model": "80G9", + "type": "tablet" + } + }, + { + "desc": "Archos 80 Xenon", + "ua": "Mozilla/5.0 (Linux; Android 4.1.2; Archos 80 Xenon Build/JZO54K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.99 Safari/537.36 OPR/50.6.2426.201126", + "expect": { + "vendor": "Archos", + "model": "80 Xenon", + "type": "tablet" + } + }, + { + "desc": "Archos 97c Platinum", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Archos 97c Platinum Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "97c Platinum", + "type": "tablet" + } + }, + { + "desc": "Archos 101 Access 3G V2", + "ua": "Mozilla/5.0 (Linux; Android 7.0; Archos Access 101 3G V2 Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.130 Safari/537.36[FBAN/EMA;FBLC/pt_PT;FBAV/360.0.0.7.53;]", + "expect": { + "vendor": "Archos", + "model": "Access 101 3G V2", + "type": "tablet" + } + }, + { + "desc": "Archos 101 Oxygen 4G", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Archos 101 Oxygen 4G Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/103.0.5060.71 Safari/537.36 [FB_IAB/FB4A;FBAV/374.0.0.20.109;]", + "expect": { + "vendor": "Archos", + "model": "101 Oxygen 4G", + "type": "tablet" + } + }, + { + "desc": "Archos 101 Platinum 3G V2", + "ua": "Mozilla/5.0 (Linux; Android 7.0; AC101PL3GV2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "AC101PL3GV2", + "type": "tablet" + } + }, + { + "desc": "Archos 101B Helium 4G", + "ua": "Mozilla/5.0 (Linux; Android 6.0; AC101BHE Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "AC101BHE", + "type": "tablet" + } + }, + { + "desc": "Archos 101s Oxygen Ardoiz", + "ua": "Mozilla/5.0 (Linux; Android 9; Archos Oxygen 101S ARDOIZ Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.102 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "Oxygen 101S ARDOIZ", + "type": "tablet" + } + }, + { + "desc": "Archos GAMEPAD2", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; ARCHOS GAMEPAD2 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.94 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "GAMEPAD2", + "type": "tablet" + } + }, + { + "desc": "Archos Hello 7", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Archos Hello 7 Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/103.0.5060.53 Safari/537.36 GoogleApp/13.24.9.26.arm64", + "expect": { + "vendor": "Archos", + "model": "Hello 7", + "type": "tablet" + } + }, + { + "desc": "Archos Sense 101 X", + "ua": "Mozilla/5.0 (Linux; arm; Android 7.0; Archos Sense 101 X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 YaBrowser/20.2.0.215.01 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "Sense 101 X", + "type": "tablet" + } + }, + { + "desc": "Archos T101 FHD WiFi", + "ua": "Mozilla/5.0 (Linux; Android 13; ARCHOS T101 FHD WiFi Build/T00624; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/124.0.6367.159 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "T101 FHD WiFi", + "type": "tablet" + } + }, + { + "desc": "Archos Tikeasy 10d", + "ua": "Mozilla/5.0 (Linux; Android 13; Tikeasy 10d Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.260 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "Tikeasy 10d", + "type": "tablet" + } + }, + { + "desc": "Archos T96 WIFI", + "ua": "Mozilla/5.0 (Linux; Android 11; ARCHOS T96 WIFI_EEA Build/RP1A.201005.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/102.0.5005.78 Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "T96 WIFI_EEA", + "type": "tablet" + } + }, + { + "desc": "Archos X67 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; X67 5G Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.58 Mobile Safari/537.36", + "expect": { + "vendor": "Archos", + "model": "X67 5G", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/asus.json b/test/data/ua/device/asus.json new file mode 100644 index 000000000..e5fee47bd --- /dev/null +++ b/test/data/ua/device/asus.json @@ -0,0 +1,164 @@ +[ + { + "desc": "ASUS Nexus 7", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 7 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "Nexus 7", + "type": "tablet" + } + }, + { + "desc": "ASUS Padfone", + "ua": "Mozilla/5.0 (Linux; Android 4.1.1; PadFone 2 Build/JRO03L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.117 Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "PadFone", + "type": "tablet" + } + }, + { + "desc": "ASUS ZenPad 10", + "ua": "Mozilla/5.0 (Linux; Android 6.0; P00C Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "P00C", + "type": "tablet" + } + }, + { + "desc": "ASUS ZenPad Z8s", + "ua": "Mozilla/5.0 (Linux; Android 7.0; ASUS_P00J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.111 Safari/537.36\n", + "expect": { + "vendor": "ASUS", + "model": "P00J", + "type": "tablet" + } + }, + { + "desc": "ASUS ROG", + "ua": "Mozilla/5.0 (Linux; Android 8.1; ZS600KL Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.126 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZS600KL", + "type": "mobile" + } + }, + { + "desc": "ASUS ROG II", + "ua": "Mozilla/5.0 (Linux; Android 9; ASUS_I001DA Build/PKQ1.190414.001; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "I001DA", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 2", + "ua": "Mozilla/5.0 (Linux; Android 5.0; ASUS ZenFone 2 Build/LRX22C) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/37.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZenFone 2", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 3 Deluxe", + "ua": "Mozilla/5.0 (Linux; Android 6.0; ASUS_Z016D Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.132 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "Z016D", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 5", + "ua": "Mozilla/5.0 (Linux; Android 8.0; ZE620KL Build/OPR1.170623.032) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.158 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZE620KL", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 7", + "ua": "Mozilla/5.0 (Linux; Android 10; ASUS_I002D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.81 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "I002D", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; ZS671KS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZS671KS", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone Max Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; ZB602KL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZB602KL", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone Max Pro (M1)", + "ua": "Mozilla/5.0 (Linux; Android 8.1; ASUS_X00TD Build/OPM1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "X00TD", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone Max M2", + "ua": "Mozilla/5.0 (Linux; Android 8.1; ASUS_X01AD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.99 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "X01AD", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone Max Pro M2", + "ua": "Mozilla/5.0 (Linux; Android 8.1; ASUS_X01BDA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.99 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "X01BDA", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone Go", + "ua": "Mozilla/5.0 (Linux; Android 6.0; ASUS_X009DA Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "X009DA", + "type": "mobile" + } + }, + { + "desc": "ASUS Zenfone 2 Laser", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; ASUS_Z00ED) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "Z00ED", + "type": "mobile" + } + }, + { + "desc": "ASUS ZenWatch", + "ua": "Mozilla/5.0 (Linux; Android 5.0.1; ASUS ZenWatch Build/LWX48S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/19.77.34.5 Mobile Safari/537.36", + "expect": { + "vendor": "ASUS", + "model": "ZenWatch", + "type": "wearable" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/blackberry.json b/test/data/ua/device/blackberry.json new file mode 100644 index 000000000..f809d5541 --- /dev/null +++ b/test/data/ua/device/blackberry.json @@ -0,0 +1,47 @@ +[ + { + "desc": "BlackBerry Priv", + "ua": "User-Agent: Mozilla/5.0 (Linux; Android 5.1.1; STV100-1 Build/LMY47V; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/46.0.2490.76 Mobile Safari/537.36", + "expect": { + "vendor": "BlackBerry", + "model": "STV100-1", + "type": "mobile" + } + }, + { + "desc": "BlackBerry Keyone", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; BBB100-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.111 Mobile Safari/537.36", + "expect": { + "vendor": "BlackBerry", + "model": "BBB100-1", + "type": "mobile" + } + }, + { + "desc": "BlackBerry Key2", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; BBF100-1 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.91 Mobile Safari/537.36", + "expect": { + "vendor": "BlackBerry", + "model": "BBF100-1", + "type": "mobile" + } + }, + { + "desc": "BlackBerry Key2 LE", + "ua": "User-Agent: Mozilla/5.0 (Linux; Android 8.1.0; BBE100-1 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497 Mobile Safari/537.36", + "expect": { + "vendor": "BlackBerry", + "model": "BBE100-1", + "type": "mobile" + } + }, + { + "desc": "Blackview 4900Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; BV4900Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "BV4900Pro", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/blu.json b/test/data/ua/device/blu.json new file mode 100644 index 000000000..969d08682 --- /dev/null +++ b/test/data/ua/device/blu.json @@ -0,0 +1,101 @@ +[ + { + "desc": "BLU Grand X LTE", + "ua": "Mozilla/5.0 (Linux; Android 7.0; BLU Grand X LTE Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36", + "expect": { + "vendor": "BLU", + "model": "Grand X LTE", + "type": "mobile" + } + }, + { + "desc": "BLU Neo Energy Mini", + "ua": "Mozilla/5.0 (Linux; Android 10; BLU_NEO_ENERGY_MINI Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.54 Mobile Safari/537.36", + "expect": { + "vendor": "BLU", + "model": "NEO_ENERGY_MINI", + "type": "mobile" + } + }, + { + "desc": "BLU NEO X PLUS", + "ua": "Mozilla/5.0 (Linux; Android 5.1; BLU NEO X PLUS Build/N090U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Mobile Safari/537.36", + "expect": { + "vendor": "BLU", + "model": "NEO X PLUS", + "type": "mobile" + } + }, + { + "desc": "BLU STUDIO X MINI", + "ua": "Mozilla/5.0 (Linux; Android 5.1; BLU STUDIO X MINI Build/S0150UU) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36", + "expect": { + "vendor": "BLU", + "model": "STUDIO X MINI", + "type": "mobile" + } + }, + { + "desc": "BLU Tank Mega", + "ua": "Mozilla/5.0 (Mobile; BLU_TankMega_3G; rv:48.0; CAEN) Gecko/48.0 Firefox/48.0 KAIOS/2.5.1.1", + "expect": { + "vendor": "BLU", + "model": "TankMega_3G", + "type": "mobile" + } + }, + { + "desc": "BLU TOUCHBOOK G7", + "ua": "Mozilla/5.0 (Linux; Android 5.0; BLU TOUCHBOOK G7 Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.107 Safari/537.36 OPR/29.0.1809.91837", + "expect": { + "vendor": "BLU", + "model": "TOUCHBOOK G7", + "type": "mobile" + } + }, + { + "desc": "BLU Vivo 5 Mini", + "ua": "Mozilla/5.0 (Linux; Android 7.0; Vivo 5 Mini Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "BLU", + "model": "Vivo 5 Mini", + "type": "mobile" + } + }, + { + "desc": "BLU VIVO AIR LTE", + "ua": "Mozilla/5.0 (Linux; Android 5.0.2; BLU VIVO AIR LTE Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.107 Mobile Safari/537.36 OPR/29.0.1809.91837", + "expect": { + "vendor": "BLU", + "model": "VIVO AIR LTE", + "type": "mobile" + } + }, + { + "desc": "BLU VIVO GO", + "ua": "Mozilla/5.0 (Linux; U; Android 9; VIVO GO Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/72.0.3626.121 Mobile Safari/537.36 OPR/50.0.2254.149182", + "expect": { + "vendor": "BLU", + "model": "VIVO GO", + "type": "mobile" + } + }, + { + "desc": "BLU Vivo One Plus 2019", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Vivo One Plus 2019 Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/277.0.0.41.126;]", + "expect": { + "vendor": "BLU", + "model": "Vivo One Plus 2019", + "type": "mobile" + } + }, + { + "desc": "BLU VIVO SELFIE", + "ua": "Mozilla/5.0 (Linux; U; Android 5.0; es-LA; BLU VIVO SELFIE Build/LRX21M) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 UCBrowser/11.3.5.972 U3/0.8.0 Mobile Safari/534.30", + "expect": { + "vendor": "BLU", + "model": "VIVO SELFIE", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/cat.json b/test/data/ua/device/cat.json new file mode 100644 index 000000000..7c5710d30 --- /dev/null +++ b/test/data/ua/device/cat.json @@ -0,0 +1,38 @@ +[ + { + "desc": "Cat B15Q", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; B15Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36", + "expect": { + "vendor": "Cat", + "model": "B15Q", + "type": "mobile" + } + }, + { + "desc": "Cat B35", + "ua": "Mozilla/5.0 (Mobile; CAT B35; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5.1", + "expect": { + "vendor": "Cat", + "model": "B35", + "type": "mobile" + } + }, + { + "desc": "Cat S22 Flip", + "ua": "Mozilla/5.0 (Linux; Android 11; S22 FLIP Build/RKQ1.210416.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.165 Mobile Safari/537.36", + "expect": { + "vendor": "Cat", + "model": "S22 FLIP", + "type": "mobile" + } + }, + { + "desc": "Cat S62 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; S62 Pro Build/RKQ1.210406.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/94.0.4606.85 Mobile Safari/537.36 GSA/12.34.17.23.arm64", + "expect": { + "vendor": "Cat", + "model": "S62 Pro", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/energizer.json b/test/data/ua/device/energizer.json new file mode 100644 index 000000000..ec1ec4762 --- /dev/null +++ b/test/data/ua/device/energizer.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Energizer Energy 400", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Energy400 Build/MRA58K test-keys; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.158 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/172.0.0.66.93;]", + "expect": { + "vendor": "Energizer", + "model": "Energy400", + "type": "mobile" + } + }, + { + "desc": "Energizer Energy 400S", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Energy 400S Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.85 Mobile Safari/537.36", + "expect": { + "vendor": "Energizer", + "model": "Energy 400S", + "type": "mobile" + } + }, + { + "desc": "Energizer Ultimate 65G", + "ua": "Mozilla/5.0 (Linux; Android 14; Energizer Ultimate 65G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Energizer", + "model": "Ultimate 65G", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/facebook.json b/test/data/ua/device/facebook.json new file mode 100644 index 000000000..f08f81f31 --- /dev/null +++ b/test/data/ua/device/facebook.json @@ -0,0 +1,47 @@ +[ + { + "desc": "Oculus Quest", + "ua": "Mozilla/5.0 (Linux; Android 10; Quest) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/15.0.0.0.22.280317669 SamsungBrowser/4.0 Chrome/89.0.4389.90 VR Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "Quest", + "type": "xr" + } + }, + { + "desc": "Oculus Quest 2", + "ua": "Mozilla/5.0 (Linux; Android 10; Quest 2) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/15.0.0.0.22.280317669 SamsungBrowser/4.0 Chrome/89.0.4389.90 VR Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "Quest 2", + "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" + } + }, + { + "desc": "Oculus Quest Pro", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Quest Pro) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/24.4.0.22.60.426469926 SamsungBrowser/4.0 Chrome/106.0.5249.181 VR Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "Quest Pro", + "type": "xr" + } + }, + { + "desc": "Portal TV", + "ua": "Mozilla/5.0 (Linux; Android 9; PortalTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.120 Mobile Safari/537.36", + "expect": { + "vendor": "Facebook", + "model": "PortalTV", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/fairphone.json b/test/data/ua/device/fairphone.json new file mode 100644 index 000000000..e341cf7b1 --- /dev/null +++ b/test/data/ua/device/fairphone.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Fairphone 1U", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; FP1U Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Fairphone", + "model": "FP1U", + "type": "mobile" + } + }, + { + "desc": "Fairphone 2", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; FP2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "Fairphone", + "model": "FP2", + "type": "mobile" + } + }, + { + "desc": "Fairphone 3", + "ua": "Mozilla/5.0 (Linux; Android 9; FP3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", + "expect": { + "vendor": "Fairphone", + "model": "FP3", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/google.json b/test/data/ua/device/google.json new file mode 100644 index 000000000..0897534e7 --- /dev/null +++ b/test/data/ua/device/google.json @@ -0,0 +1,614 @@ +[ + { + "desc": "Google Chromecast with Google TV", + "ua": "Mozilla/5.0 (Linux; Android 12.0; Build/STTL.240206.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.0 Safari/537.36 CrKey/1.56.500000 DeviceType/AndroidTV", + "expect": { + "vendor": "Google", + "model": "Chromecast AndroidTV", + "type": "smarttv" + } + }, + { + "desc": "Google Chromecast Mini Smart Speaker", + "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/SmartSpeaker", + "expect": { + "vendor": "Google", + "model": "Chromecast SmartSpeaker", + "type": "smarttv" + } + }, + { + "desc": "Google Chromecast Third Generation", + "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/Chromecast", + "expect": { + "vendor": "Google", + "model": "Chromecast Third Generation", + "type": "smarttv" + } + }, + { + "desc": "Google Chromecast Nest Hub", + "ua": "Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 CrKey/1.56.500000", + "expect": { + "vendor": "Google", + "model": "Chromecast Nest Hub", + "type": "smarttv" + } + }, + { + "desc": "Google Chromecast", + "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.84 Safari/537.36 CrKey/1.22.79313", + "expect": { + "vendor": "Google", + "model": "Chromecast", + "type": "smarttv" + } + }, + { + "desc": "Google Pixel C", + "ua": "Mozilla/5.0 (Linux; Android 7.0; Pixel C Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel C", + "type": "tablet" + } + }, + { + "desc": "Google Pixel C", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.64 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel C", + "type": "tablet" + } + }, + { + "desc": "Google Pixel", + "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel Build/NDE63V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 2", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Pixel 2 Build/OPM1.171019.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 2", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 2", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 2) AppleWebKit/537.36 (KHTML, like Gecko) Edg/57.0.986.6", + "expect": { + "vendor": "Google", + "model": "Pixel 2", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 2", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; G011A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 Safari/537.36 OPR/68.3.3557.64528", + "expect": { + "vendor": "Google", + "model": "G011A", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 2 XL", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Pixel 2 XL Build/OPM1.171019.013) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 2 XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 2 XL", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 2 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 2 XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PD1A.180720.030) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 3", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3", + "ua": "Mozilla/5.0 (Linux; Android 11; G013A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G013A", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3a", + "ua": "Mozilla/5.0 (Linux; Android 14; G020E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G020E", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3 XL", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 3 XL Build/PD1A.180720.030) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 3 XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3 XL", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 3 XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3a", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 3a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 3a", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 3a XL", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 3a XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 3a XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 4", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4", + "ua": "Mozilla/5.0 (Linux; Android 12; G020M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G020M", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4a) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.83 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 4a", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a", + "ua": "Mozilla/5.0 (Linux; Android 13; G025J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G025J", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a", + "ua": "Mozilla/5.0 (Linux; Android 12; GA02099) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GA02099", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a", + "ua": "Mozilla/5.0 (Linux; Android 13; G025N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G025N", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4a (5G)", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 4a (5G) Build/UP1A.231105.001.B2; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.2478.64", + "expect": { + "vendor": "Google", + "model": "Pixel 4a (5G)", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 4 XL", + "ua": "Mozilla/5.0 (Linux; Android 10; Pixel 4 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 4 XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5", + "ua": "Mozilla/5.0 (Linux; Android 13; G5NZ6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G5NZ6", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5", + "ua": "Mozilla/5.0 (Linux; Android 12; GD1YQ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GD1YQ", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5", + "ua": "Mozilla/5.0 (Linux; Android 12; GTT9Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GTT9Q", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5", + "ua": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.120 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 5", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5a 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; G4S1M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G4S1M", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 5a 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; G1F8F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G1F8F", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 6", + "ua": "Mozilla/5.0 (Linux; Android 11; Pixel 6 Build/QP1A.190711.020) AppleWebKit/545.31 (KHTML, like Gecko) Firefox/109.0.2318.118 Mobile Safari/545.22", + "expect": { + "vendor": "Google", + "model": "Pixel 6", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 6 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; Pixel 6 Pro Build/AP4A.241205.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.163 Mobile Safari/537.36 Line/15.0.0/IAB", + "expect": { + "vendor": "Google", + "model": "Pixel 6 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 6 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; G8VOU) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G8VOU", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7", + "ua": "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 7", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; Pixel 7 Pro Build/AP4A.250205.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.121 Mobile Safari/537.36 musical_ly_2023808030 BytedanceWebview/d8a21c6", + "expect": { + "vendor": "Google", + "model": "Pixel 7 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; GP4BC) AppleWebKit/537.46 (KHTML, like Gecko) Chrome/118.0.5993.80 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GP4BC", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; GE2AE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5993.48 Mobile Safari/537.46", + "expect": { + "vendor": "Google", + "model": "GE2AE", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 7a", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 7a Build/AP2A.240905.003; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.2478.64", + "expect": { + "vendor": "Google", + "model": "Pixel 7a", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 8 Build/UPB2.230407.014) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.2.7822.95 Mobile Safari/537.36 Vivaldi/5.2.9076.131", + "expect": { + "vendor": "Google", + "model": "Pixel 8", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 8 Pro Build/UPB1.230309.017; Rooted) AppleWebKit/537.36 (KHTML, like Gecko) Puffin/10.9.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 8 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 14; Google Pixel 8a) Chrome/121.0.6167 Mobile", + "expect": { + "vendor": "Google", + "model": "Pixel 8a", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 15; GKV4X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GKV4X", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 15; G8HHN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G8HHN", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 8a", + "ua": "Mozilla/5.0 (Linux; Android 15; G576D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G576D", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; U; Android 14; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.119 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 9", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; G2YBB) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G2YBB", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; GUR25) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GUR25", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; GWVK6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GWVK6", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9", + "ua": "Mozilla/5.0 (Linux; Android 15; G1B60) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "G1B60", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro Build/AD1A.240530.047; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.170 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/506.1.0.74.27;IABMV/1;]", + "expect": { + "vendor": "Google", + "model": "Pixel 9 Pro", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; GR83Y) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GR83Y", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; GEC77) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GEC77", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro Fold", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel 9 Pro Fold) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel 9 Pro Fold", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro Fold", + "ua": "Mozilla/5.0 (Linux; Android 15; GC15S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GC15S", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro Fold", + "ua": "Mozilla/5.0 (Linux; Android 15; GGH2X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "GGH2X", + "type": "mobile" + } + }, + { + "desc": "Google Pixel 9 Pro XL", + "ua": "Mozilla/5.0 (Linux; U; Android 15; Pixel 9 Pro XL Build/AP4A.250105.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.89 Mobile Safari/537.36 OPR/88.0.2254.75874", + "expect": { + "vendor": "Google", + "model": "Pixel 9 Pro XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel Fold", + "ua": "Mozilla/5.0 (Linux; Android 15; Pixel Fold Build/AP4A.250105.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.97 Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.40.36;IABMV/1;]", + "expect": { + "vendor": "Google", + "model": "Pixel Fold", + "type": "mobile" + } + }, + { + "desc": "Google Pixel Tablet", + "ua": "Mozilla/5.0 (Linux; Android 14; Pixel Tablet Build/AP2A.240905.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel Tablet", + "type": "tablet" + } + }, + { + "desc": "Google Pixel Watch", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch Build/TWD4.231005.002)", + "expect": { + "vendor": "Google", + "model": "Pixel Watch", + "type": "wearable" + } + }, + { + "desc": "Google Pixel Watch 2", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Google Pixel Watch 2 Build/TWD9.240605.001.A1)", + "expect": { + "vendor": "Google", + "model": "Pixel Watch 2", + "type": "wearable" + } + }, + { + "desc": "Google Pixel XL", + "ua": "Mozilla/5.0 (Linux; Android 7.1; Pixel XL Build/NDE63X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixel XL", + "ua": "Mozilla/5.0 (Linux; Android 9; Pixel XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixel XL", + "type": "mobile" + } + }, + { + "desc": "Google Pixelbook", + "ua": "Mozilla/5.0 (Linux; Android 9; Google Pixelbook) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36", + "expect": { + "vendor": "Google", + "model": "Pixelbook", + "type": "undefined" + } + }, + { + "desc": "Google Pixelbook Go", + "ua": "Mozilla/5.0 (Linux; Android 9; Google Pixelbook Go) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 OPR/62.3.3146.57763", + "expect": { + "vendor": "Google", + "model": "Pixelbook Go", + "type": "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/hmd.json b/test/data/ua/device/hmd.json new file mode 100644 index 000000000..037148c41 --- /dev/null +++ b/test/data/ua/device/hmd.json @@ -0,0 +1,65 @@ +[ + { + "desc": "HMD Barbie Phone", + "ua": "Mozilla/5.0 (Mobile; HMD Barbie Phone; rv:84.0) Gecko/84.0 Firefox/84.0 KAIOS/3.1", + "expect": { + "vendor": "HMD", + "model": "Barbie Phone", + "type": "mobile" + } + }, + { + "desc": "HMD Fusion", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Fusion) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Fusion", + "type": "mobile" + } + }, + { + "desc": "HMD Pulse", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Pulse", + "type": "mobile" + } + }, + { + "desc": "HMD Pulse Plus", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse Plus) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Pulse Plus", + "type": "mobile" + } + }, + { + "desc": "HMD Pulse Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; HMD Pulse Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Pulse Pro", + "type": "mobile" + } + }, + { + "desc": "HMD Skyline", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 14; HMD Skyline) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.1804 YaApp_Android/24.120.1 YaSearchBrowser/24.120.1 BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "HMD", + "model": "Skyline", + "type": "mobile" + } + }, + { + "desc": "HMD Vibe", + "ua": "Mozilla/5.0 (Linux; Android 14; N159V Build/UKQ1.231025.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/135.0.7049.111 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.47.36;]", + "expect": { + "vendor": "HMD", + "model": "N159V", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/honor.json b/test/data/ua/device/honor.json new file mode 100644 index 000000000..321e51b1f --- /dev/null +++ b/test/data/ua/device/honor.json @@ -0,0 +1,164 @@ +[ + { + "desc": "Honor MagicPad 13 WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; GDI-W09 Build/HONORGDI-W09) 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": "Honor", + "model": "GDI-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 2", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; en-nz; JDN-W09 Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko)Version/4.0 Chrome/37.0.0.0 MQQBrowser/6.0 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "JDN-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 2", + "ua": "Mozilla/5.0 (Linux; U; Android 9; zh-Hans-CN; JDN2-W09HN Build/HUAWEIJDN2-W09HN) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Quark/4.6.6.164 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "JDN2-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 7 10.1", + "ua": "Mozilla/5.0 (Linux; Android 12; AGM3-AL09HN Build/HONORAGM3-AL09HN; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.46 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/490.0.0.63.82;IABMV/1;]", + "expect": { + "vendor": "Honor", + "model": "AGM3-AL09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 8 12.0", + "ua": "Mozilla/5.0 (Linux; Android 12; HEY-W09 Build/HONORHEY-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "HEY-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 9 12.1", + "ua": "Mozilla/5.0 (Linux; Android 13; HEY2-N09 Build/HONORHEY2-N09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 [FB_IAB/FB4A;FBAV/465.0.0.63.83;]", + "expect": { + "vendor": "Honor", + "model": "HEY2-N09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad 9 12.1 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 14; HEY2-W09 Build/HONORHEY2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.102 Safari/537.36 [FB_IAB/FB4A;FBAV/489.0.0.66.81;IABMV/1;]", + "expect": { + "vendor": "Honor", + "model": "HEY2-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad V7 Pro 11", + "ua": "Mozilla/5.0 (Linux; Android 12; BRT-AN09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 EdgA/109.0.1518.53", + "expect": { + "vendor": "Honor", + "model": "BRT-AN09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad V7 Pro 11 WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; BRT-W09 Build/HONORBRT-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.5.0.336 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "BRT-W09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X6", + "ua": "Mozilla/5.0 (Linux; Android 10; AGR-W09HN Build/HUAWEIAGR-W09HN; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.9 SP-engine/2.28.0 baiduboxapp/12.9.0.11 (Baidu; P1 10) NABar/1.0", + "expect": { + "vendor": "Honor", + "model": "AGR-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X7 8 LTE", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-AL00HN; HMSCore 6.0.0.306) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.93 HuaweiBrowser/11.1.3.300 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "KOB2-AL00HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X7 8 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-W09HN; HMSCore 6.1.0.314) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 HuaweiBrowser/12.0.0.301 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "KOB2-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X8 Lite", + "ua": "Mozilla/5.0 (Linux; Android 12; AGM-W09HN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "AGM-W09HN", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X9 11.5 LTE", + "ua": "Mozilla/5.0 (Linux; Android 13; ELN-L09 Build/HONORELN-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Mobile Safari/537.36[FBAN/EMA;FBLC/zh_CN;FBAV/432.0.0.9.110;FBCX/modulariab;]", + "expect": { + "vendor": "Honor", + "model": "ELN-L09", + "type": "tablet" + } + }, + { + "desc": "Honor Pad X9 11.5 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 13; ELN-W09 Build/HONORELN-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "ELN-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei Honor 6A", + "ua": "Mozilla/5.0 (Linux; Android 7.0; DLI-L22 Build/HONORDLI-L22; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/252.0.0.22.355;]", + "expect": { + "vendor": "Honor", + "model": "DLI-L22", + "type": "mobile" + } + }, + { + "desc": "Huawei Honor 7", + "ua": "Mozilla/5.0 (Linux; Android 6.0; PLK-L01 Build/HONORPLK-L01; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "PLK-L01", + "type": "mobile" + } + }, + { + "desc": "Huawei 10 Lite", + "ua": "Mozilla/5.0 (Linux; Android 9; HRY-LX1 Build/HONORHRY-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.91 Mobile Safari/537.36", + "expect": { + "vendor": "Honor", + "model": "HRY-LX1", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/htc.json b/test/data/ua/device/htc.json new file mode 100644 index 000000000..51ff339ba --- /dev/null +++ b/test/data/ua/device/htc.json @@ -0,0 +1,29 @@ +[ + { + "desc": "HTC Desire 820", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; HTC Desire 820 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36", + "expect": { + "vendor": "HTC", + "model": "Desire 820", + "type": "mobile" + } + }, + { + "desc": "HTC Evo Shift 4G", + "ua": "Mozilla/5.0 (Linux; U; Android 2.3.4; en-us; Sprint APA7373KT Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0", + "expect": { + "vendor": "Sprint", + "model": "APA7373KT", + "type": "mobile" + } + }, + { + "desc": "HTC Nexus 9", + "ua": "Mozilla/5.0 (Linux; Android 5.0; Nexus 9 Build/LRX21R) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Mobile Crosswalk/7.36.154.13 Safari/537.36", + "expect": { + "vendor": "HTC", + "model": "Nexus 9", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/huawei.json b/test/data/ua/device/huawei.json new file mode 100644 index 000000000..670879e66 --- /dev/null +++ b/test/data/ua/device/huawei.json @@ -0,0 +1,947 @@ +[ + { + "desc": "Huawei Honor", + "ua": "Mozilla/5.0 (Linux; U; Android 2.3; xx-xx; U8860 Build/HuaweiU8860) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", + "expect": { + "vendor": "Huawei", + "model": "U8860", + "type": "mobile" + } + }, + { + "desc": "Huawei Honor 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L41) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-L41", + "type": "mobile" + } + }, + { + "desc": "Huawei Honor 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-AL10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-AL10", + "type": "mobile" + } + }, + { + "desc": "Huawei Nexus 6P", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 6P Build/MTC19V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.81 Mobile Safari/537", + "expect": { + "vendor": "Huawei", + "model": "Nexus 6P", + "type": "mobile" + } + }, + { + "desc": "Huawei P10", + "ua": "Mozilla/5.0 (Linux; Android 7.0; VTR-L09 Build/HUAWEIVTR-L09; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "VTR-L09", + "type": "mobile" + } + }, + { + "desc": "Huawei Y3II", + "ua": "Mozilla/5.0 (Linux; U; Android 5.1; xx-xx; HUAWEI LUA-L03 Build/HUAWEILUA-L03) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LUA-L03", + "type": "mobile" + } + }, + { + "desc": "HUAWEI MediaPad C5 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; MON-AL19B Build/HUAWEIMON-AL19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.83 Mobile Safari/537.36 T7/11.7 baiduboxapp/11.7.0.10 (Baidu; P1 7.0)", + "expect": { + "vendor": "Huawei", + "model": "MON-AL19B", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M2 10.1", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; HUAWEI M2-A01L Build/HUAWEIM2-A01L; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/95.0.4638.74 Safari/537.36[FBAN/EMA;FBLC/fr_FR;FBAV/421.0.0.14.100;]", + "expect": { + "vendor": "Huawei", + "model": "M2-A01L", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0; en-US; BTV-DL09 Build/HUAWEIBEETHOVEN-DL09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/40.0.2214.89 UCBrowser/11.5.0.1015 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BTV-DL09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; HUAWEI BTV-W09 Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.96 Mobile Safari/537.36 AlohaBrowser/3.1.1", + "expect": { + "vendor": "Huawei", + "model": "BTV-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3 Lite", + "ua": "Mozilla/5.0 (Linux; Android 7.0; CPN-L09 Build/HUAWEICPN-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36[FBAN/EMA;FBLC/ru_RU;FBAV/233.0.0.12.118;]", + "expect": { + "vendor": "Huawei", + "model": "CPN-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3 Lite", + "ua": "Mozilla/5.0 (Linux; Android 7.0; CPN-W09 Build/HUAWEICPN-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/103.0.5060.71 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/374.0.0.20.109;]", + "expect": { + "vendor": "Huawei", + "model": "CPN-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M3 Lite 10", + "ua": "Mozilla/5.0 (Linux; Android 7.0; BAH-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 10.8", + "ua": "Mozilla/5.0 (Linux; Android 9; CMR-W09 Build/HUAWEICMR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.102 Safari/537.36 Line/14.18.1/IAB", + "expect": { + "vendor": "Huawei", + "model": "CMR-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 Lite", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; BAH2-W19 Build/HUAWEIBAH2-W19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH2-W19", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 Lite", + "ua": "Mozilla/5.0 (Linux; Android 9; JDN2-W09 Build/HUAWEIJDN2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.210 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/318.0.0.39.154;]", + "expect": { + "vendor": "Huawei", + "model": "JDN2-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 Lite", + "ua": "Mozilla/5.0 (Linux; Android 9; JDN2-AL50 Build/HUAWEIJDN2-AL50; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.13.0 SP-engine/2.29.0 matrixstyle/0 lite baiduboxapp/5.8.0.10 (Baidu; P1 9) NABar/1.", + "expect": { + "vendor": "Huawei", + "model": "JDN2-AL50", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5 8.4", + "ua": "Mozilla/5.0 (Linux; Android 9; SHT-W09 Build/HUAWEISHT-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.87 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "SHT-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M5", + "ua": "Mozilla/5.0 (Linux; Android 9; SHT-AL09 Build/HUAWEISHT-AL09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "SHT-AL09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M6 10.8", + "ua": "Mozilla/5.0 (Linux; Android 14; SCM-W09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6612.143 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "SCM-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad M6 8.4", + "ua": "Mozilla/5.0 (Linux; Android 9; VRD-W09; HMSCore 6.14.0.321; GMSCore 22.26.15) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "VRD-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T5", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; AGS2-L09 Build/HUAWEIAGS2-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/84.0.4147.125 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS2-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10", + "ua": "Mozilla/5.0 (Linux; U; Android 10; en-US; AGR-L09 Build/HUAWEIAGR-L09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.3.8.1305 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGR-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10", + "ua": "Mozilla/5.0 (Linux; Android 10; AGR-W09 Build/HUAWEIAGR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGR-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10s", + "ua": "Mozilla/5.0 (Linux; Android 10; AGS3-W09 Build/HUAWEIAGS3-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS3-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T 8.0", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-L09 Build/HUAWEIKOB2-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/396.0.0.21.104;]", + "expect": { + "vendor": "Huawei", + "model": "KOB2-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T 8.0", + "ua": "Mozilla/5.0 (Linux; Android 10; KOB2-W09 Build/HUAWEIKOB2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 Mobile Safari/537.36 HuaweiBrowser/15.0.4.312 HMSCore/6.14.0.301", + "expect": { + "vendor": "Huawei", + "model": "KOB2-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 10", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; T1-A21w Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Safari/537.36 SputnikBrowser/1.2.8.161", + "expect": { + "vendor": "Huawei", + "model": "T1-A21w", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 10", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; T1-A23L Build/HuaweiMediaPad; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.121 Mobile Safari/537.36 BingWeb/6.9.10", + "expect": { + "vendor": "Huawei", + "model": "T1-A23L", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 10", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; T1-A21L Build/HuaweiMediaPad) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "T1-A21L", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 7", + "ua": "Mozilla/5.0 (Linux; 4.4.2; T1-701u) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "T1-701u", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T1 8", + "ua": "Mozilla/5.0 (Linux; U; Android 9.0; MediaPad T1 8.0 Build/HuaweiMediaPad) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30 OPR/28.0.2254.119224", + "expect": { + "vendor": "Huawei", + "model": "MediaPad T1 8.0", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10 9.7", + "ua": "Mozilla/5.0 (Linux; U; Android 10; en-US; AGRK-L09 Build/HUAWEIAGRK-L09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.6.0.1315 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGRK-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10 9.7 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; AGRK-W09; HMSCore 6.14.0.321) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGRK-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10s 10.1 LTE", + "ua": "Mozilla/5.0 (Linux; Android 10; AGS3K-L09 Build/HUAWEIAGS3K-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36 [FB_IAB/FB4A;FBAV/362.0.0.27.109;]", + "expect": { + "vendor": "Huawei", + "model": "AGS3K-L09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T10s 10.1 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; AGS3K-W09; HMSCore 6.14.0.321) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS3K-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T2 10.0 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; 605HW Build/HuaweiMediaPad; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/102.0.5005.78 Safari/537.36 [FB_IAB/FB4A;FBAV/436.0.0.35.101;]", + "expect": { + "vendor": "Huawei", + "model": "605HW", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T2 7.0 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0; BGO-DL09 Build/HuaweiBAGGIO; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/407.0.0.30.97;]", + "expect": { + "vendor": "Huawei", + "model": "BGO-DL09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T3 10", + "ua": "Mozilla/5.0 (Linux; Android 7.0; AGS-W09 Build/HUAWEIAGS-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.90 Safari/537.36 GSA/10.83.10.21.arm64", + "expect": { + "vendor": "Huawei", + "model": "AGS-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T3 7", + "ua": "Mozilla/5.0 (Linux; Android 7.0; BG2-U03 Build/HUAWEIBG2-U03; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/84.0.4147.111 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BG2-U03", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T3 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; KOB-W09 Build/HUAWEIKOB-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Safari/537.36 [FB_IAB/Orca-Android;FBAV/354.0.0.10.113;]", + "expect": { + "vendor": "Huawei", + "model": "KOB-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad T5 10", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; AGS2-W09 Build/HUAWEIAGS2-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", + "expect": { + "vendor": "Huawei", + "model": "AGS2-W09", + "type": "tablet" + } + }, + { + "desc": "HUAWEI MediaPad X2", + "ua": "Mozilla/5.0 (Linux; Android 8.0; GEM-703L Build/HUAWEIGEM-703L; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/043906 Mobile Safari/537.36 MicroMessenger/6.6.3.1260(0x26060339) NetType/WIFI Language/zh_", + "expect": { + "vendor": "Huawei", + "model": "GEM-703L", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH3-W09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH3-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH3-L09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH3-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; BAH3-W59 Build/HUAWEIBAH3-W59; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36HiSearch/22.0.6.315", + "expect": { + "vendor": "Huawei", + "model": "BAH3-W59", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 (2022)", + "ua": "Mozilla/5.0 (Linux; Android 10; BAH4-L09 Build/HUAWEIBAH4-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH4-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 (2022) WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; BAH4-W09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BAH4-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 SE", + "ua": "Mozilla/5.0 (Linux; Android 12; AGS5-L09 Build/HUAWEIAGS5-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS5-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 10.4 SE WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; AGS5-W09 Build/HUAWEIAGS5-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.88 Safari/537.36 [FB_IAB/FB4A;FBAV/480.0.0.54.88;]", + "expect": { + "vendor": "Huawei", + "model": "AGS5-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11 (2023) WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; DBR-W10 Build/HUAWEIDBR-W10) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/6.9.6.501 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "DBR-W10", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11 WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-cn; DBY-W09 Build/HUAWEIDBY-W09) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.86 MQQBrowser/14.6 Mobile Safari/537.36 COVC/046801", + "expect": { + "vendor": "Huawei", + "model": "DBY-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 Air WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 12; zh-Hans-CN; DBY2-W00 Build/HUAWEIDBY2-W00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 Quark/7.3.8.663 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "DBY2-W00", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 LTE", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; BTK-AL09; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BTK-AL09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 S WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; TGR-W09; HMSCore 6.14.0.322; GMSCore 0.3.3.1.240913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 HuaweiBrowser/14.0.2.317 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "TGR-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad 11.5 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; BTK-W09; HMSCore 6.14.0.322; GMSCore 214816056) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BTK-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad C5 8", + "ua": "Mozilla/5.0 (Linux; Android 7.0; MON-W19 Build/HUAWEIMON-W19; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36 [Pinterest/Android]", + "expect": { + "vendor": "Huawei", + "model": "MON-W19", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 11", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 12; GOT-AL09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 YaBrowser/23.5.5.60.01 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "GOT-AL09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 11 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 12; GOT-W09 Build/HUAWEIGOT-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Safari/537.36 T7/13.19 BDOS/1.0 (HarmonyOS 3.0.0) SP-engine/2.57.0 baiduboxapp/13.19.0.12 (Baidu; P1 12) NABar/1.0", + "expect": { + "vendor": "Huawei", + "model": "GOT-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 12.6 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; WGR-W09 Build/HUAWEIWGR-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/92.0.4515.105 Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/412.0.0.8.106;]", + "expect": { + "vendor": "Huawei", + "model": "WGR-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad SE 11 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; HarmonyOS; AGS6-W09; HMSCore 6.12.2.309) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.93 HuaweiBrowser/11.1.5.315 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGS6-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad Pro 13.2", + "ua": "Mozilla/5.0 (Linux; Android 12; HarmonyOS; PCE-W29; HMSCore 6.14.0.322) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 HuaweiBrowser/15.0.4.312 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "PCE-W29", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad T 10", + "ua": "Mozilla/5.0 (Linux; Android 10; AGR-L09; HMSCore 5.0.4.301) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 HuaweiBrowser/11.0.3.304 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AGR-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad T10s", + "ua": "Mozilla/5.0 (Linux; U; Android 10; zh-cn; AGS3-AL00 Build/HUAWEIAGS3-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/77.0.3865.120 MQQBrowser/11.4 Mobile Safari/537.36 COVC/045530", + "expect": { + "vendor": "Huawei", + "model": "AGS3-AL00", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad T10s WiFi", + "ua": "Mozilla/5.0 (Linux; U; Android 10; AGS3-W09 Build/HUAWEIAGS3-W09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/88.0.4324.93 Safari/537.36 OPR/60.0.2254.59405", + "expect": { + "vendor": "Huawei", + "model": "AGS3-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei MatePad T8 8 LTE", + "ua": "Mozilla/5.0 (Linux; U; Android 10; KOB2K-L09 Build/HUAWEIKOB2K-L09; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Safari/537.36 OPR/83.0.2254.73002", + "expect": { + "vendor": "Huawei", + "model": "KOB2K-L09", + "type": "tablet" + } + }, + { + "desc": "Huawei M3", + "ua": "Mozilla/5.0 (Linux; Android 7.0; BTV-W09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BTV-W09", + "type": "tablet" + } + }, + { + "desc": "Huawei Mate 10 Pro", + "ua": "Mozilla/5.0 (Linux; Android 8.0; BLA-L29 Build/HUAWEIBLA-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3236.6 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BLA-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate X", + "ua": "Mozilla/5.0 (Linux; Android 9; TAH-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.111 Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "TAH-AN00", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate X2", + "ua": "Mozilla/5.0 (Linux; Android 10; TET-AN00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "TET-AN00", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 X", + "ua": "Mozilla/5.0 (Linux; Android 9; EVR-L29 Build/HUAWEIEVR-L29; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "EVR-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L09) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-L09", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-AL00", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-AL10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-AL10", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L0C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-L0C", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; LYA-TL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LYA-TL00", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 50 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; DCO-LX9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "DCO-LX9", + "type": "mobile" + } + }, + { + "desc": "Huawei P20 Lite", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "ANE-LX1", + "type": "mobile" + } + }, + { + "desc": "Huawei P20", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; EML-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "EML-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei P20 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; CLT-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "CLT-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei P30", + "ua": "Mozilla/5.0 (Linux; Android 9; ELE-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "ELE-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei P30 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; VOG-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "VOG-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei P40", + "ua": "Mozilla/5.0 (Linux; Android 10; ANA-AN00 Build/HUAWEIANA-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/11.26 SP-engine/2.22.0 baiduboxapp/11.26.0.10 (Baidu; P1 10) NABar/1.0", + "expect": { + "vendor": "Huawei", + "model": "ANA-AN00", + "type": "mobile" + } + }, + { + "desc": "Huawei P40 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; ELS-AN00 Build/HUAWEIELS-AN00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Mobile Safari/537.36 mailapp/6.0.0", + "expect": { + "vendor": "Huawei", + "model": "ELS-AN00", + "type": "mobile" + } + }, + { + "desc": "Huawei 30 Pro+", + "ua": "Mozilla/5.0 (Linux; Android 10; EBG-AN10 Build/HUAWEIEBG-AN10) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.86 Mobile Safari/537.36 EdgA/42.0.0.2741", + "expect": { + "vendor": "Huawei", + "model": "EBG-AN10", + "type": "mobile" + } + }, + { + "desc": "Huawei 30S", + "ua": "Mozilla/5.0 (Linux; Android 10; CDY-AN90 Build/HUAWEICDY-AN90; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Mobile Safari/537.36 mailapp/5.8.0", + "expect": { + "vendor": "Huawei", + "model": "CDY-AN90", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L21) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-L21", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L61) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-L61", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L71) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-L71", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YAL-L61D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YAL-L61D", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L61A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YALE-L61A", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L61D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YALE-L61D", + "type": "mobile" + } + }, + { + "desc": "Huawei Nova 5T", + "ua": "Mozilla/5.0 (Linux; Android 10; YALE-L71A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "YALE-L71A", + "type": "mobile" + } + }, + { + "desc": "Huawei Enjoy10e", + "ua": "Dalvik/2.1.0 (Linux; U; Android 10; MED-AL00 Build/HUAWEIMED-AL00)", + "expect": { + "vendor": "Huawei", + "model": "MED-AL00", + "type": "mobile" + } + }, + { + "desc": "Huawei Y7 2018", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; LDN-L01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.62 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "LDN-L01", + "type": "mobile" + } + }, + { + "desc": "Huawei Honor 8X", + "ua": "Mozilla/5.0 (Linux; Android 9; JSN-L21) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "JSN-L21", + "type": "mobile" + } + }, + { + "desc": "Huawei Y6 2019", + "ua": "Mozilla/5.0 (Linux; Android 9; MRD-LX1N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "MRD-LX1N", + "type": "mobile" + } + }, + { + "desc": "Huawei Y9 2019", + "ua": "Mozilla/5.0 (Linux; Android 9; JKM-LX2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "JKM-LX2", + "type": "mobile" + } + }, + { + "desc": "Huawei Y5", + "ua": "Mozilla/5.0 (Linux; Android 9; AMN-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "AMN-LX3", + "type": "mobile" + } + }, + { + "desc": "Huawei Y7p", + "ua": "Mozilla/5.0 (Linux; Android 9; ART-L29) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "ART-L29", + "type": "mobile" + } + }, + { + "desc": "Huawei Mate 20 Lite", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; SNE-LX1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.116 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "SNE-LX1", + "type": "mobile" + } + }, + { + "desc": "Huawei P10 Lite", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; WAS-LX1A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.90 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "WAS-LX1A", + "type": "mobile" + } + }, + { + "desc": "Huawei Y5 Lite 2018", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; DRA-LX5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "DRA-LX5", + "type": "mobile" + } + }, + { + "desc": "Huawei Honor 8C", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; BKK-LX2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "Huawei", + "model": "BKK-LX2", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/imo.json b/test/data/ua/device/imo.json new file mode 100644 index 000000000..e794a801b --- /dev/null +++ b/test/data/ua/device/imo.json @@ -0,0 +1,38 @@ +[ + { + "desc": "IMO FEEL A2", + "ua": "Mozilla/5.0 (Linux; Android 5.1; IMO FEEL A2 Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/49.0.2623.105 Mobile Safari/537.36", + "expect": { + "vendor": "IMO", + "model": "FEEL A2", + "type": "mobile" + } + }, + { + "desc": "IMO Q2", + "ua": "Mozilla/5.0 (Linux; Android 5.1; IMO Q2 Build/LMY47D; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/65.0.3325.109 Mobile Safari/537.36 GSA/7.22.24.21.arm", + "expect": { + "vendor": "IMO", + "model": "Q2", + "type": "mobile" + } + }, + { + "desc": "IMO S2", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; IMO S2 Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.162 Mobile Safari/537.36", + "expect": { + "vendor": "IMO", + "model": "S2", + "type": "mobile" + } + }, + { + "desc": "IMO Tab X9", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.3; id-id; IMO TAB X9 Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "IMO", + "model": "TAB X9", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/infinix.json b/test/data/ua/device/infinix.json new file mode 100644 index 000000000..3474003b9 --- /dev/null +++ b/test/data/ua/device/infinix.json @@ -0,0 +1,83 @@ +[ + { + "desc": "Infinix Hot 4", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; en-us; Infinix HOT 4 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36 PHX/17.9", + "expect": { + "vendor": "Infinix", + "model": "HOT 4", + "type": "mobile" + } + }, + { + "desc": "Infinix Hot 4 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Infinix_X556_LTE Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/390.0.0.27.105;]", + "expect": { + "vendor": "Infinix", + "model": "X556_LTE", + "type": "mobile" + } + }, + { + "desc": "Infinix Hot 7 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; Infinix X625C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X625C", + "type": "mobile" + } + }, + { + "desc": "Infinix Hot 10T", + "ua": "Mozilla/5.0 (Linux; Android 11; Infinix X689C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X689C", + "type": "mobile" + } + }, + { + "desc": "Infinix Hot 11s", + "ua": "Mozilla/5.0 (Linux; Android 11; Infinix X6812 Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X6812", + "type": "mobile" + } + }, + { + "desc": "Infinix Note 3", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Infinix_X601_LTE Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/81.0.4044.111 Mobile Safari/537.36 GSA/11.5.9.21.arm64", + "expect": { + "vendor": "Infinix", + "model": "X601_LTE", + "type": "mobile" + } + }, + { + "desc": "Infinix Smart 5", + "ua": "Mozilla/5.0 (Linux; Android 10; Infinix X657C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X657C", + "type": "mobile" + } + }, + { + "desc": "Infinix XPad", + "ua": "Mozilla/5.0 (Linux; Android 14; Infinix X1101B Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.99 Safari/537.36 [FB_IAB/FB4A;FBAV/489.0.0.66.81;IABMV/1;]", + "expect": { + "vendor": "Infinix", + "model": "X1101B", + "type": "tablet" + } + }, + { + "desc": "Infinix Zero 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; Infinix X6815B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Infinix", + "model": "X6815B", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/itel.json b/test/data/ua/device/itel.json new file mode 100644 index 000000000..109e6e0b5 --- /dev/null +++ b/test/data/ua/device/itel.json @@ -0,0 +1,56 @@ +[ + { + "desc": "itel A25", + "ua": "Mozilla/5.0 (Linux; Android 9; itel L5002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.130 Mobile Safari/537.36 OPR/63.3.3216.58675", + "expect": { + "vendor": "itel", + "model": "L5002", + "type": "mobile" + } + }, + { + "desc": "itel A50", + "ua": "Mozilla/5.0 (Linux; U; Android 14; itel A667L Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.103 Mobile Safari/537.36 OPR/83.1.2254.73239", + "expect": { + "vendor": "itel", + "model": "A667L", + "type": "mobile" + } + }, + { + "desc": "itel KidPad 1", + "ua": "Mozilla/5.0 (Linux; Android 10; Itel W7001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.101 Mobile Safari/537.36", + "expect": { + "vendor": "itel", + "model": "W7001", + "type": "tablet" + } + }, + { + "desc": "itel Pad One", + "ua": "Mozilla/5.0 (Linux; Android 12; itel P10001L Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.172 Safari/537.36", + "expect": { + "vendor": "itel", + "model": "P10001L", + "type": "tablet" + } + }, + { + "desc": "itel RS4", + "ua": "Mozilla/5.0 (Linux; Android 13; itel S666LN Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.165 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/468.1.0.56.78;]", + "expect": { + "vendor": "itel", + "model": "S666LN", + "type": "mobile" + } + }, + { + "desc": "itel Vision 2S", + "ua": "Mozilla/5.0 (Linux; Android 11; itel P651L Build/RP1A.201005.001) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.76 Mobile Safari/537.36", + "expect": { + "vendor": "itel", + "model": "P651L", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/jolla.json b/test/data/ua/device/jolla.json new file mode 100644 index 000000000..622fea01f --- /dev/null +++ b/test/data/ua/device/jolla.json @@ -0,0 +1,11 @@ +[ + { + "desc": "Jolla", + "ua": "Mozilla/5.0 (Maemo; Linux; U; Jolla; Sailfish; Mobile; rv:31.0) Gecko/31.0 Firefox/31.0 SailfishBrowser/1.0", + "expect": { + "vendor": "Jolla", + "model": "undefined", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/kobo.json b/test/data/ua/device/kobo.json new file mode 100644 index 000000000..6648a9a59 --- /dev/null +++ b/test/data/ua/device/kobo.json @@ -0,0 +1,20 @@ +[ + { + "desc": "Kobo eReader", + "ua": "Mozilla/5.0 (Unknown; Linux) AppleWebKit/538.1 (KHTML, like Gecko) Kobo eReader Safari/538.1", + "expect": { + "vendor": "Kobo", + "model": "eReader", + "type": "tablet" + } + }, + { + "desc": "Kobo Touch", + "ua": "Mozilla/5.0 (Linux; U; Android 2.0; en-us;) AppleWebKit/538.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/538.1 (Kobo Touch 0377/4.20.14622)", + "expect": { + "vendor": "Kobo", + "model": "Touch", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/lava.json b/test/data/ua/device/lava.json new file mode 100644 index 000000000..5279f2cf5 --- /dev/null +++ b/test/data/ua/device/lava.json @@ -0,0 +1,110 @@ +[ + { + "desc": "Lava Agni 2 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; LAVA LXX504 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.46 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/499.0.0.31.60;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LXX504", + "type": "mobile" + } + }, + { + "desc": "Lava Agni 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; LAVA LXX501 Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/122.0.6261.119 Mobile Safari/537.36[FBAN/EMA;FBLC/hi_IN;FBAV/396.0.0.9.115;]", + "expect": { + "vendor": "LAVA", + "model": "LXX501", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze", + "ua": "Mozilla/5.0 (Linux; U; Android 12; en-US; LAVA Blaze Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.4.0.1306 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "Blaze", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze 2", + "ua": "Mozilla/5.0 (Linux; Android 13; LAVA LZX409 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.97 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.47.36;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LZX409", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; LAVA LXX503) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LXX503", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze Curve", + "ua": "Mozilla/5.0 (Linux; Android 14; LAVA LXX505 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/505.0.0.57.45;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LXX505", + "type": "mobile" + } + }, + { + "desc": "Lava Blaze Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; LAVA LZX404 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.50 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/503.0.0.69.76;IABMV/1;]", + "expect": { + "vendor": "LAVA", + "model": "LZX404", + "type": "mobile" + } + }, + { + "desc": "Lava Iris 46", + "ua": "Mozilla/5.0 (Linux; Android 9; LAVA LH9950) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/21.0 Chrome/110.0.5481.154 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LH9950", + "type": "mobile" + } + }, + { + "desc": "Lava Iris 54", + "ua": "Mozilla/5.0 (Linux; U; Android 9; LAVA LH9931 Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36 OPR/39.1.2254.136708", + "expect": { + "vendor": "LAVA", + "model": "LH9931", + "type": "mobile" + } + }, + { + "desc": "Lava Storm 5G", + "ua": "Mozilla/5.0 (Linux; Android 13; LAVA LXX508 Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.100 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LXX508", + "type": "mobile" + } + }, + { + "desc": "Lava V7 Prime", + "ua": "Mozilla/5.0 (Linux; Android 9; LAVA LE9940) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Mobile Safari/537.36", + "expect": { + "vendor": "LAVA", + "model": "LE9940", + "type": "mobile" + } + }, + { + "desc": "Lava V7s Prime", + "ua": "Mozilla/5.0 (Linux; U; Android 9; LAVA LE9940_W Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36 OPR/43.2.2254.140294", + "expect": { + "vendor": "LAVA", + "model": "LE9940_W", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/lenovo.json b/test/data/ua/device/lenovo.json new file mode 100644 index 000000000..526a3b50c --- /dev/null +++ b/test/data/ua/device/lenovo.json @@ -0,0 +1,1100 @@ +[ + { + "desc": "Lenovo A7", + "ua": "Mozilla/5.0 (Linux; U; Android 9; en-US; Lenovo L19111 Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/13.2.8.1301 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L19111", + "type": "mobile" + } + }, + { + "desc": "Lenovo A8", + "ua": "Mozilla/5.0 (Linux; Android 10; Lenovo L10041) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.73 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L10041", + "type": "mobile" + } + }, + { + "desc": "Lenovo dtab Compact 42A", + "ua": "Mozilla/5.0 (Linux; Android 12; d-42A Build/SKQ1.220201.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/102.0.5005.125 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "d-42A", + "type": "tablet" + } + }, + { + "desc": "Lenovo IdeaTab A7-50", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Lenovo A3500-HV Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "A3500-HV", + "type": "tablet" + } + }, + { + "desc": "Lenovo IdeaTab A2109A", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; ru-ru; A2109A Build/JDQ39; CyanogenMod-10.1) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Lenovo", + "model": "A2109A", + "type": "tablet" + } + }, + { + "desc": "Lenovo IdeaTab S6000", + "ua": "Mozilla/5.0 (Linux; Android 6.0; S6000 Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "S6000", + "type": "tablet" + } + }, + { + "desc": "Lenovo IdeaTab S6000", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; IdeaTab S6000-H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 YaBrowser/18.11.1.1011.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "IdeaTab S6000-H", + "type": "tablet" + } + }, + { + "desc": "Lenovo K5 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 9;zh-cn; Lenovo L38041 Build/PKQ1.190127.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.117 MobileLenovoBrowser/9.1.3 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L38041", + "type": "mobile" + } + }, + { + "desc": "Lenovo K9", + "ua": "Mozilla/5.0 (Linux; U; Android 8.1.0; en-US; Lenovo L38043 Build/O11019) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/40.0.2214.89 UCBrowser/11.4.8.1012 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L38043", + "type": "mobile" + } + }, + { + "desc": "Lenovo K10 Plus", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo L39051) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.66 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L39051", + "type": "mobile" + } + }, + { + "desc": "Lenovo K12", + "ua": "Mozilla/5.0 (Linux; Android 10; Lenovo XT2081-4 Build/QCZ30.30-Q3-45-17; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.128 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/409.0.0.27.106;]", + "expect": { + "vendor": "Lenovo", + "model": "XT2081-4", + "type": "mobile" + } + }, + { + "desc": "Lenovo K12", + "ua": "Mozilla/5.0 (Linux; U; Android 10; Lenovo K12 Build/QOGS30.569-83-18; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.130 Mobile Safari/537.36 OPR/69.0.2254.66073", + "expect": { + "vendor": "Lenovo", + "model": "K12", + "type": "mobile" + } + }, + { + "desc": "Lenovo Legion 2 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; Lenovo L70081 Build/RKQ1.201112.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.58 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L70081", + "type": "mobile" + } + }, + { + "desc": "Lenovo Legion Y90", + "ua": "Mozilla/5.0 (Linux; U; Android 12;en-us; Lenovo L71061/SKQ1.211113.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.132 MobileLenovoBrowser/8.6.0 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L71061", + "type": "mobile" + } + }, + { + "desc": "Lenovo Legion Y700", + "ua": "Mozilla/5.0 (Linux; U; Android 13;zh-cn; Lenovo TB-9707F Build/TKQ1.221013.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.117 MobileLenovoBrowser/2.1.7 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-9707F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Legion Y700", + "ua": "Mozilla/5.0 (Linux; Android 12; TB320FC) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB320FC", + "type": "tablet" + } + }, + { + "desc": "Lenovo Moto Tab", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; TB-X704A Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X704A", + "type": "tablet" + } + }, + { + "desc": "Lenovo Phone", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Lenovo PB2-650M Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.105 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/311.0.0.44.117;]", + "expect": { + "vendor": "Lenovo", + "model": "PB2-650M", + "type": "mobile" + } + }, + { + "desc": "Lenovo S5 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo L58041) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L58041", + "type": "mobile" + } + }, + { + "desc": "Lenovo Smart Tab M8", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 10; Lenovo TB-8505XS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 YaBrowser/21.11.7.71.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8505XS", + "type": "tablet" + } + }, + { + "desc": "Lenovo Smart Tab M8", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 10; Lenovo TB-8505FS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.166 YaBrowser/21.8.4.111.00 (beta) SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8505FS", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 2", + "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Lenovo TAB 2 A7-30HC Build/LRX21M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TAB 2 A7-30HC", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 2 A7", + "ua": "Mozilla/5.0 (Linux; Android 7.0.99; Lenovo TAB 2 A7-30DC Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.141 Safari/537.36 OPR/45.1.2246.125351", + "expect": { + "vendor": "Lenovo", + "model": "TAB 2 A7-30DC", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 2 A10", + "ua": "Mozilla/5.0 (Linux; U; Android 5.0.1; Lenovo TAB 2 A10-70L Build/LRX21M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.116 Safari/537.36 OPR/29.0.2254.120398", + "expect": { + "vendor": "Lenovo", + "model": "TAB 2 A10-70L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 2 A10-30", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; TB2-X30F Build/LenovoTB2-X30F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Mobile Safari/537.36 EdgA/90.0.818.49", + "expect": { + "vendor": "Lenovo", + "model": "TB2-X30F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 7", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Lenovo TB3-730X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB3-730X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 7 Essential", + "ua": "Mozilla/5.0 (Linux; Android 5.1; Lenovo TB3-710I Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/85.0.4183.127 Safari/537.36 GSA/5.4.28.19.arm", + "expect": { + "vendor": "Lenovo", + "model": "TB3-710I", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 7 Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; en-US; Lenovo TB-7703X Build/S100) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.2.5.1102 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-7703X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 8 Dual", + "ua": "Mozilla/5.0 (Linux; Android 6.0; 602LV Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/93.0.4577.82 Safari/537.36 GSA/12.36.22.23.arm64", + "expect": { + "vendor": "Lenovo", + "model": "602LV", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 8 Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; zh-cn; Lenovo TB-8703F Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/9.8 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8703F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 8 Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; Lenovo TB-8703X Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/65.0.3325.109 Safari/537.36 OPR/33.0.2254.125672", + "expect": { + "vendor": "Lenovo", + "model": "TB-8703X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 10 Business", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0; Lenovo TB3-X70F Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Safari/537.36 OPR/32.0.2254.123747", + "expect": { + "vendor": "Lenovo", + "model": "TB3-X70F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 10 Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0; Lenovo TB3-X70L Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/86.0.4240.185 Safari/537.36 OPR/52.1.2254.54298", + "expect": { + "vendor": "Lenovo", + "model": "TB3-X70L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 3 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo YT3-X90F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.99 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT3-X90F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Lenovo TB-X304F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.99 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X304F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Lenovo TAB 2 A7-30HC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TAB 2 A7-30HC", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 8", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; Lenovo TB-8504F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Mobile Safari/537.36 OPR/64.2.3282.60128", + "expect": { + "vendor": "Lenovo", + "model": "TB-8504F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 8", + "ua": "Mozilla/5.0 (Linux; U; Android 8.1.0; Lenovo TB-8504X Build/OPM1.171019.019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36 OPR/52.2.2254.54723", + "expect": { + "vendor": "Lenovo", + "model": "TB-8504X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 8 Plus", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 8.1.0; Lenovo TB-8704F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 YaApp_Android/21.21.0/apad YaSearchBrowser/21.21.0/apad BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8704F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 8 Plus", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; TB-8704V Build/OPM1.171019.019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/104.0.5112.97 Safari/537.36 [FB_IAB/FB4A;FBAV/380.0.0.29.109;]", + "expect": { + "vendor": "Lenovo", + "model": "TB-8704V", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 8 Plus", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 8.1.0; Lenovo TB-8704X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 YaBrowser/19.10.4.187.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8704X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 8 REL", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Lenovo TB-8X04F Build/NMF26F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.100 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8X04F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 10", + "ua": "Mozilla/5.0 (Linux; U; Android 8.1.0; Lenovo TB-X304L Build/OPM1.171019.026; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.149 Safari/537.36 OPR/47.0.2254.146760", + "expect": { + "vendor": "Lenovo", + "model": "TB-X304L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 10 Plus", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 7.1.1; Lenovo TB-X704L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 YaBrowser/20.12.4.100.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X704L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 4 10 Plus", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 7.1.1; Lenovo TB-X704F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 YaBrowser/20.8.5.97.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X704F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 6", + "ua": "Mozilla/5.0 (Linux; Android 11; A101LV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.61 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "A101LV", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 7", + "ua": "Mozilla/5.0 (Linux; Android 7.0; Lenovo TB-7504X Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/119.0.6045.193 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/436.0.0.35.101;]", + "expect": { + "vendor": "Lenovo", + "model": "TB-7504X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 7 Essential", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; Lenovo TB-7304I Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Safari/537.36 OPR/54.0.2254.56148", + "expect": { + "vendor": "Lenovo", + "model": "TB-7304I", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 7 Essential", + "ua": "Mozilla/5.0 (Linux; Android 7.0; Lenovo TB-7304X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-7304X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab 10 10.1", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo TB-X103F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X103F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab E7", + "ua": "Mozilla/5.0 (Linux; arm; Android 8.1.0; Lenovo TB-7104I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 BroPP/1.0 SA/3 Mobile Safari/537.36 YandexSearch/7.52/apad YandexSearchBrowser/7.52", + "expect": { + "vendor": "Lenovo", + "model": "TB-7104I", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab E8", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 7.0; Lenovo TB-8304F1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.143 YaBrowser/19.7.4.97.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8304F1", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab K10", + "ua": "Mozilla/5.0 (Linux; Android 12; Lenovo TB-X6C6X Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X6C6X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab K10", + "ua": "Mozilla/5.0 (Linux; Android 12; Lenovo TB-X6C6F Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.83 Safari/537.36 [FB_IAB/FB4A;FBAV/488.0.0.78.79;IABMV/1;] FBNV/5", + "expect": { + "vendor": "Lenovo", + "model": "TB-X6C6F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab K11", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 10; Lenovo TB-J606N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 YaApp_Android/22.31.1/apad YaSearchBrowser/22.31.1/apad BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J606N", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab K11 Pro 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; Lenovo TB-J607Z Build/SKQ1.211103.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.58 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J607Z", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M7", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-7305X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.105 Mobile Safari/537.36 OPR/63.3.3216.58675", + "expect": { + "vendor": "Lenovo", + "model": "TB-7305X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M7", + "ua": "Mozilla/5.0 (Linux; arm; Android 9; Lenovo TB-7305I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 YaApp_Android/20.85.0/apad YaSearchBrowser/20.85.0/apad BroPP/1.0 SA/1 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-7305I", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M7 (Gen 3)", + "ua": "Mozilla/5.0 (Linux; Android 11; Lenovo TB-7306F Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Safari/537.36 [FB_IAB/FB4A;FBAV/489.0.0.66.81;IABMV/1;]", + "expect": { + "vendor": "Lenovo", + "model": "TB-7306F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M7 (Gen 3)", + "ua": "Mozilla/5.0 (Linux; arm; Android 11; Lenovo TB-7306X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 YaBrowser/20.12.5.127.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-7306X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8", + "ua": "Mozilla/5.0 (Linux; Android 10; Lenovo TB-8505X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8505X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-8505F Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36 GSA/10.82.8.21.arm64", + "expect": { + "vendor": "Lenovo", + "model": "TB-8505F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8 (Gen 3)", + "ua": "Mozilla/5.0 (Linux; U; Android 11; zh-TW; Lenovo TB-8506X Build/RP1A.200720.011) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.4.0.1306 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8506X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8 (Gen 4)", + "ua": "Mozilla/5.0 (Linux; Android 13; TB300FU Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Mobile Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/417.0.0.9.97;]", + "expect": { + "vendor": "Lenovo", + "model": "TB300FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8 (Gen 4) (2024)", + "ua": "Mozilla/5.0 (Linux; Android 13; TB301FU Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.170 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/472.0.0.45.79;]", + "expect": { + "vendor": "Lenovo", + "model": "TB301FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M8 FHD", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-8705X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.99 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-8705X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; Android 12; TB310FU) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB310FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; 13; TB310XU) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB310XU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 9; Lenovo TB-X606F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 YaBrowser/20.9.4.99.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X606F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Android 14; Mobile; Lenovo TB-X505F; rv:131.0) Gecko/131.0 Firefox/131.0", + "expect": { + "vendor": "Lenovo", + "model": "TB-X505F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; Android 14; Lenovo TB-X505L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6554.180 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X505L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-X605F Build/PKQ1.190319.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Safari/537.36 [FB_IAB/FB4A;FBAV/298.0.0.46.116;]", + "expect": { + "vendor": "Lenovo", + "model": "TB-X605F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10", + "ua": "Mozilla/5.0 (Linux; U; Android 10; Lenovo TB-X505X Build/QKQ1.191224.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/81.0.4044.138 Safari/537.36 OPR/52.2.2254.54723", + "expect": { + "vendor": "Lenovo", + "model": "TB-X505X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 (Gen 3) ", + "ua": "Mozilla/5.0 (Linux; Android 12; TB328XU Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.58 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB328XU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 (Gen 3) ", + "ua": "Mozilla/5.0 (Linux; Android 12; TB328FU Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.57 Safari/537.36 [FB_IAB/FB4A;FBAV/418.0.0.33.69;]", + "expect": { + "vendor": "Lenovo", + "model": "TB328FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD", + "ua": "Mozilla/5.0 (Linux; Android 13; Lenovo TB-X605FC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6481.193 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X605FC", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-X605LC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.93 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X605LC", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 9; Lenovo TB-X605L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 YaBrowser/20.12.0.141.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X605L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD Plus", + "ua": "Mozilla/5.0 (Linux; Android 14; Lenovo TB-X606F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6496.93 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X606F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD Plus", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo TB-X606FA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5813.205 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X606FA", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 FHD Plus", + "ua": "Mozilla/5.0 (Linux; Android 10; Lenovo TB-X606X Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X606X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 HD", + "ua": "Mozilla/5.0 (Linux; U; Android 10; it-it; Lenovo TB-X306F Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36 PHX/6.2", + "expect": { + "vendor": "Lenovo", + "model": "TB-X306F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 HD", + "ua": "Mozilla/5.0 (Linux; U; Android 10; it-it; Lenovo TB-X306F Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36 PHX/6.2", + "expect": { + "vendor": "Lenovo", + "model": "TB-X306F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M10 Plus (Gen 3)", + "ua": "Mozilla/5.0 (Linux; U; Android 10; Lenovo TB-X306X Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.210 Safari/537.36 OPR/55.1.2254.56965", + "expect": { + "vendor": "Lenovo", + "model": "TB-X306X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab M11", + "ua": "Mozilla/5.0 (Linux; Android 14; TB330FU Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.60 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB330FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P10", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 9; Lenovo TB-X705L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 YaBrowser/21.3.3.160.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-X705L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; Lenovo TB-J606L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.2311.135 YaBrowser/21.11.5.121.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J606L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11", + "ua": "Mozilla/5.0 (Linux; U; Android 11; zh-cn; Lenovo TB-J606F Build/RKQ1.210303.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.72 MQQBrowser/12.1 Mobile Safari/537.36 COVC/045830", + "expect": { + "vendor": "Lenovo", + "model": "TB-J606F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 Plus", + "ua": "Mozilla/5.0 (Linux; Android 12; Lenovo TB-J616X Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Safari/537.36 [FB_IAB/FB4A;FBAV/418.0.0.33.69;]", + "expect": { + "vendor": "Lenovo", + "model": "TB-J616X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 (Gen 2)", + "ua": "Mozilla/5.0 (Linux; Android 14; TB350FU Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.46 Safari/537.36 [FB_IAB/FB4A;FBAV/490.0.0.63.82;IABMV/1;]", + "expect": { + "vendor": "Lenovo", + "model": "TB350FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 Plus", + "ua": "Mozilla/5.0 (Linux; Android 12; Lenovo TB-J616F Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.9 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J616F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 11; zh-CN; Lenovo TB-J706F Build/RKQ1.201112.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 Quark/5.8.6.223 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J706F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; Lenovo TB-J706L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.134 Safari/537.36 EdgA/103.0.1264.71", + "expect": { + "vendor": "Lenovo", + "model": "TB-J706L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P11 Pro (Gen 2)", + "ua": "Mozilla/5.0 (Linux; Android 13; TB132FU Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB132FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P12", + "ua": "Mozilla/5.0 (Linux; Android 14; TB370FU Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.106 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB370FU", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P12 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; Lenovo TB-Q706F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-Q706F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab P12 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; Lenovo TB-Q706Z Build/TKQ1.221013.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.103 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-Q706Z", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab QT K11 WiFi", + "ua": "Mozilla/5.0 (Linux; arm; Android 12; Lenovo TB-J6C6F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.114 YaBrowser/22.9.3.82.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J6C6F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab QT K11 Pro WiFi", + "ua": "Mozilla/5.0 (Linux; Android 11; Lenovo TB-J607F Build/RKQ1.201217.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.86 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "TB-J607F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab V7", + "ua": "Mozilla/5.0 (Linux; U; Android 9; en-US; Lenovo PB-6505M Build/PKQ1) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.9.9.1155 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "PB-6505M", + "type": "tablet" + } + }, + { + "desc": "Lenovo Tab V7", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 9; Lenovo PB-6505Y) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 YaBrowser/20.9.3.85.00 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "PB-6505Y", + "type": "tablet" + } + }, + { + "desc": "Lenovo X3 Lite", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo X3 Lite) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.88 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "X3 Lite", + "type": "mobile" + } + }, + { + "desc": "Lenovo Yoga Smart Tab", + "ua": "Mozilla/5.0 (Android 11; Mobile; Lenovo YT-X705X; rv:129.0) Gecko/129.0 Firefox/129.0", + "expect": { + "vendor": "Lenovo", + "model": "YT-X705X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Smart Tab", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 10; Lenovo YT-X705F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 YaBrowser/20.2.4.153.01 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT-X705F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Smart Tab", + "ua": "Mozilla/5.0 (Linux; Android 9; Lenovo YT-X705L Build/PKQ1.181218.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Safari/537.36 GSA/11.38.8.23.arm64", + "expect": { + "vendor": "Lenovo", + "model": "YT-X705L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; Lenovo YT3-X50L Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.149 Safari/537.36 OPR/46.0.2254.145391", + "expect": { + "vendor": "Lenovo", + "model": "YT3-X50L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; Lenovo YT3-X50L Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.149 Safari/537.36 OPR/46.0.2254.145391", + "expect": { + "vendor": "Lenovo", + "model": "YT3-X50L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Lenovo YT3-X50M Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT3-X50M", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3 8", + "ua": "Mozilla/5.0(Linux; U; Android 5.1.1; pt-BR; Lenovo YT3-850F Build/LMY47V) AppleWebKit/537.36(KHTML, like Gecko) Version/4.0 Chrome/38.0.2125.102 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT3-850F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3 8", + "ua": "Mozilla/5.0(Linux; U; Android 5.1.1; lv-LV; Lenovo YT3-850L Build/LMY47V) AppleWebKit/537.36(KHTML, like Gecko) Version/4.0 Chrome/38.0.2125.102 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT3-850L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3 10", + "ua": "Mozilla/5.0(Linux; U; Android 5.1.1; vi-VN; Lenovo YT3-850M Build/LMY47V) AppleWebKit/537.36(KHTML, like Gecko) Version/4.0 Chrome/38.0.2125.102 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT3-850M", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3 Plus", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Lenovo YT-X703L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36 OPR/64.2.3282.60128", + "expect": { + "vendor": "Lenovo", + "model": "YT-X703L", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 3 Plus", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; YT-X703F Build/NJH47F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.141 Safari/537.36 OPR/45.0.2246.125120", + "expect": { + "vendor": "Lenovo", + "model": "YT-X703F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tab 11", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 12; Lenovo YT-J706X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.42 YaBrowser/24.1.1.42.01 (beta) Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "YT-J706X", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 8", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.2; ru-ru; Lenovo B6000; Android/4.4.2; Release/08.26.2015) AppleWebKit/534.30 (KHTML, like Gecko) Mobile Safari/534.30", + "expect": { + "vendor": "Lenovo", + "model": "B6000", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 8", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Lenovo B6000-H Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36 GSA/7.24.32.16.arm", + "expect": { + "vendor": "Lenovo", + "model": "B6000-H", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 8", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; es-us; Lenovo B6000-F/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.2.2 Mobile Safari/534.30", + "expect": { + "vendor": "Lenovo", + "model": "B6000-F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 10", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; ru-ru; Lenovo B8000-F Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Lenovo", + "model": "B8000-F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 10", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; ru-ru; Lenovo B8000-H Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30 Mobile UCBrowser/3.4.3.532", + "expect": { + "vendor": "Lenovo", + "model": "B8000-H", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 10 HD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-US; Lenovo B8080-H Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.0.0.1088 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "B8080-H", + "type": "tablet" + } + }, + { + "desc": "Lenovo Yoga Tablet 10 HD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.3; ru-ru; Lenovo B8080-F/JLS36C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.3 Mobile Safari/534.30", + "expect": { + "vendor": "Lenovo", + "model": "B8080-F", + "type": "tablet" + } + }, + { + "desc": "Lenovo Z6", + "ua": "Mozilla/5.0 (Linux; U; Android 9;zh-cn; Lenovo L78121 Build/PKQ1.190319.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.117 MobileLenovoBrowser/9.1.3 Mobile Safari/537.36", + "expect": { + "vendor": "Lenovo", + "model": "L78121", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/lg.json b/test/data/ua/device/lg.json new file mode 100644 index 000000000..9f125d72b --- /dev/null +++ b/test/data/ua/device/lg.json @@ -0,0 +1,209 @@ +[ + { + "desc": "LG V40 ThinQ", + "ua": "Mozilla/5.0 (Linux; Android 9; LM-V405) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-V405", + "type": "mobile" + } + }, + { + "desc": "LG K30", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; LM-X410.F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-X410.F", + "type": "mobile" + } + }, + { + "desc": "LG K30", + "ua": "Mozilla/5.0 (Linux; Android 9; LM-X410.FGN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-X410.FGN", + "type": "mobile" + } + }, + { + "desc": "LG K40", + "ua": "Mozilla/5.0 (Linux; Android 10; LM-X420) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-X420", + "type": "mobile" + } + }, + { + "desc": "LG Stylo 4", + "ua": "Mozilla/5.0 (Linux; Android 10; LM-Q710(FGN)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36", + "expect": { + "vendor": "undefined", + "model": "LM-Q710(FGN)", + "type": "mobile" + } + }, + { + "desc": "LG Stylo 5", + "ua": "Mozilla/5.0 (Linux; Android 9; LM-Q720) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-Q720", + "type": "mobile" + } + }, + { + "desc": "LG G7 ThinQ", + "ua": "Mozilla/5.0 (Linux; Android 9; LM-G710VM Build/PKQ1.181105.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-G710VM", + "type": "mobile" + } + }, + { + "desc": "LG K20", + "ua": "Mozilla/5.0 (Android 13; Mobile; LG-M255; rv:111.0) Gecko/111.0 Firefox/111.0", + "expect": { + "vendor": "LG", + "model": "M255", + "type": "mobile" + } + }, + { + "desc": "LG K500", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; LG-K500 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "K500", + "type": "mobile" + } + }, + { + "desc": "LG Nexus 4", + "ua": "Mozilla/5.0 (Linux; Android 4.2.1; Nexus 4 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19", + "expect": { + "vendor": "LG", + "model": "Nexus 4", + "type": "mobile" + } + }, + { + "desc": "LG Nexus 4", + "ua": "Mozilla/5.0 (Linux; U; Android 4.3; en-us; Google Nexus 4 - 4.3 - API 18 - 768x1280 Build/JLS36G) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "LG", + "model": "Nexus 4", + "type": "mobile" + } + }, + { + "desc": "LG Nexus 5", + "ua": "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Mobile Safari/535.19", + "expect": { + "vendor": "LG", + "model": "Nexus 5", + "type": "mobile" + } + }, + { + "desc": "LG Wing", + "ua": "Mozilla/5.0 (Linux; Android 10; LM-F100N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.101 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LM-F100N", + "type": "mobile" + } + }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (DirectFB; U; Linux mips; en) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+) LG Browser (; LG NetCast.TV-2011)", + "expect": { + "vendor": "LG", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (Linux; NetCast; U) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/53.0.2785 34 Safari/537.31 SmartTV/8.5", + "expect": { + "vendor": "LG", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.41 (KHTML, like Gecko) Large Screen Safari/537.41 LG Browser/7.00.00(LGE; 42LB670V-ZA; 05.05.90; 1); webOS.TV-2014; LG NetCast.TV-2013 Compatible (LGE, 42LB670V-ZA, wireless)", + "expect": { + "vendor": "LG", + "model": "42LB670V-ZA", + "type": "smarttv" + } + }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/53.0.2785.34 Safari/537.36 LG Browser/8.00.00(LGE; 32LM627BPSB; 05.40.45; 1; DTV_W19R); webOS.TV-2019; LG NetCast.TV-2013 Compatible (LGE, 32LM627BPSB, wireless)", + "expect": { + "vendor": "LG", + "model": "32LM627BPSB", + "type": "smarttv" + } + }, + { + "desc": "LG Smart TV", + "ua": "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 HbbTV/1.3.1 ( DRM; LGE; OLED55B7V_Z; WEBOS3.5 06.10.60; W3_M16P; ) FVC/2.0 (LGE; WEBOS3.5 ;)", + "expect": { + "vendor": "LG", + "model": "OLED55B7V_Z", + "type": "smarttv" + } + }, + { + "desc": "LG Android TV", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-cn; LG Android TV Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "LG", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "LG VK Series Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.0.2; VK700 Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.84 Safari/537.36", + "expect": { + "vendor": "LG", + "model": "VK700", + "type": "tablet" + } + }, + { + "desc": "LG LK Series Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.0.1; LGLK430 Build/LRX21Y) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/38.0.2125.102 Safari/537.36", + "expect": { + "vendor": "LG", + "model": "LK430", + "type": "tablet" + } + }, + { + "desc": "LG Watch Urbane", + "ua": "Mozilla/5.0 Linux; Android 7.1.1; LG Watch Urbane Build/NWD1.180306.004 AppleWebKit/537.36 KHTML, like Gecko Chrome/19.77.34.5 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "Watch Urbane", + "type": "wearable" + } + }, + { + "desc": "LG G Watch R", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; G Watch R Build/LCA44B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Crosswalk/15.44.384.12 Mobile Safari/537.36", + "expect": { + "vendor": "LG", + "model": "G Watch R", + "type": "wearable" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/meizu.json b/test/data/ua/device/meizu.json new file mode 100644 index 000000000..e18fdf080 --- /dev/null +++ b/test/data/ua/device/meizu.json @@ -0,0 +1,20 @@ +[ + { + "desc": "Meizu M5 Note", + "ua": "Mozilla/5.0 (Linux; Android 6.0; M5 Note Build/MRA58K; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.49 Mobile MQQBrowser/6.2 TBS/043024 Safari/537.36 MicroMessenger/6.5.7.1040 NetType/WIFI Language/zh_CN", + "expect": { + "vendor": "Meizu", + "model": "M5 Note", + "type": "mobile" + } + }, + { + "desc": "Meizu M3S", + "ua": "Mozilla/5.0 (X11; Linux; Android 5.1; MZ-M3s Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrom/45.0.2454.94 Mobile Safari/537.36", + "expect": { + "vendor": "Meizu", + "model": "M3s", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/micromax.json b/test/data/ua/device/micromax.json new file mode 100644 index 000000000..041642097 --- /dev/null +++ b/test/data/ua/device/micromax.json @@ -0,0 +1,30 @@ +[ + + { + "desc": "Micromax Bharat 2 Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; en-US; Micromax Q402Plus Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.12.9.1226 Mobile Safari/537.36", + "expect": { + "vendor": "Micromax", + "model": "Q402Plus", + "type": "mobile" + } + }, + { + "desc": "Micromax Canvas Infinity", + "ua": "Mozilla/5.0 (Linux; U; Android 7.1.2; en-US; Micromax HS2 Build/N2G47H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 UCBrowser/13.2.0.1296 (SpeedMode) U4/1.0 UCWEB/2.0 Mobile Safari/534.30", + "expect": { + "vendor": "Micromax", + "model": "HS2", + "type": "mobile" + } + }, + { + "desc": "Micromax In 1b", + "ua": "Mozilla/5.0 (Linux; U; Android 10; Micromax E7533 Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36 OPR/54.0.2254.56148", + "expect": { + "vendor": "Micromax", + "model": "E7533", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/microsoft.json b/test/data/ua/device/microsoft.json new file mode 100644 index 000000000..79c35111a --- /dev/null +++ b/test/data/ua/device/microsoft.json @@ -0,0 +1,47 @@ +[ + { + "desc": "Microsoft Lumia 950", + "ua": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", + "expect": { + "vendor": "Microsoft", + "model": "Lumia 950", + "type": "mobile" + } + }, + { + "desc": "Microsoft Surface Duo", + "ua": "Dalvik/2.1.0 (Linux; U; Android 10; Surface Duo Build/2020.1014.61)", + "expect": { + "vendor": "Microsoft", + "model": "Surface Duo", + "type": "tablet" + } + }, + { + "desc": "Microsoft Lumia 950", + "ua": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586", + "expect": { + "vendor": "Microsoft", + "model": "Lumia 950", + "type": "mobile" + } + }, + { + "desc": "Xbox", + "ua": "Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; Xbox)", + "expect": { + "vendor": "Microsoft", + "model": "Xbox", + "type": "console" + } + }, + { + "desc": "Xbox One", + "ua": "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; Xbox; Xbox One)", + "expect": { + "vendor": "Microsoft", + "model": "Xbox One", + "type": "console" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/motorola.json b/test/data/ua/device/motorola.json new file mode 100644 index 000000000..18d1e48a3 --- /dev/null +++ b/test/data/ua/device/motorola.json @@ -0,0 +1,299 @@ +[ + { + "desc": "Motorola Defy XT", + "ua": "Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; XT555C Build/V1.67D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", + "expect": { + "vendor": "Motorola", + "model": "XT555C", + "type": "mobile" + } + }, + { + "desc": "Motorola Droid RAZR 4G", + "ua": "Mozilla/5.0 (Linux; U; Android 2.3; xx-xx; DROID RAZR 4G Build/6.5.1-73_DHD-11_M1-29) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1", + "expect": { + "vendor": "Motorola", + "model": "DROID RAZR 4G", + "type": "mobile" + } + }, + { + "desc": "Motorola Edge 30 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; XT2201-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2201-1", + "type": "mobile" + } + }, + { + "desc": "Motorola Edge 30 Ultra", + "ua": "Mozilla/5.0 (Linux; Android 14; motorola edge 30 ultra Build/U1SQS34.52-21-1-13; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 M", + "expect": { + "vendor": "Motorola", + "model": "edge 30 ultra", + "type": "mobile" + } + }, + { + "desc": "Motorola Edge 40", + "ua": "Mozilla/5.0 (Linux; Android 14; motorola edge 40 Build/U1TL34.115-16-1-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.104 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "edge 40", + "type": "mobile" + } + }, + { + "desc": "Motorola Edge X30", + "ua": "Mozilla/5.0 (Linux; Android 14; XT2201-2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.6668.59 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2201-2", + "type": "mobile" + } + }, + { + "desc": "Motorola RAZR 2019", + "ua": "Mozilla/5.0 (Linux; Android 9; motorola razr) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/11.1 Chrome/75.0.3770.143 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "razr", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto 360", + "ua": "Mozilla/5.0 (Linux; Android 4.4; Moto 360 Build/KNX01S) AppleWebKit/537.36 (KHTML, like Gecko) WIB/0.9.8 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Moto 360", + "type": "wearable" + } + }, + { + "desc": "Motorola Moto E", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; Moto E (4) Build/NDQS26.69-64-11-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Moto E (4)", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto E6s", + "ua": "Mozilla/5.0 (Linux; Android 9; MOTOROLA E6S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "E6S", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto E7 Plus", + "ua": "Mozilla/5.0 (Linux; Android 10; moto e(7) plus Build/QPZS30.30-Q3-38-69-12; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.14 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "moto e(7) plus", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto E7 Plus", + "ua": "Mozilla/5.0 (Linux; Android 14; XT2081-1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Mobile Safari/537.36 OPR/102.0.4864.0", + "expect": { + "vendor": "Motorola", + "model": "XT2081-1", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; moto g 5G - 2023 Build/U1TPNS34.26-48-2-7; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36 EdgA/124.0.2478.64", + "expect": { + "vendor": "Motorola", + "model": "moto g 5G - 2023", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G 5G (2022)", + "ua": "Mozilla/5.0 (Linux; Android 12; moto g 5G (2022) Build/S1SAS32.47-77-9; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/111.0.5563.116 Mobile Safari/537.36 [FB_IAB/Orca-Android;FBAV/402.0.0.11.101;]", + "expect": { + "vendor": "Motorola", + "model": "moto g 5G (2022)", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto g(6) Play", + "ua": "Mozilla/5.0 (Linux; Android 9; moto g(6) play) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "moto g(6) play", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto g(7) Supra", + "ua": "Mozilla/5.0 (Linux; Android 9; moto g(7) supra Build/PCOS29.114-134-2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/73.0.3683.90 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "moto g(7) supra", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G10", + "ua": "Mozilla/5.0 (Linux; Android 12; XT2127-2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2127-2", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G24 Power", + "ua": "Mozilla/5.0 (Linux; U; Android 14; moto g24 power Build/UTAS34.82-97-1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/133.0.6943.138 Mobile Safari/537.36 OPR/89.0.2254.76420", + "expect": { + "vendor": "Motorola", + "model": "moto g24 power", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G30", + "ua": "Mozilla/5.0 (Linux; Android 14; XT2129-3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2129-3", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto G54 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; moto g54 5G Build/U1TDS34.94-12-9-10-2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.6834.122 Mobile Safari/537.36 OPX/2", + "expect": { + "vendor": "Motorola", + "model": "moto g54 5G", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; XT1097 Build/KXE21.187-38) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.109 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT1097", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2; xx-xx; XT1058 Build/13.9.0Q2.X-70-GHOST-ATT_LE-2) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Motorola", + "model": "XT1058", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X Force", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; XT1580 Build/NPKS25.200-12-9; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/65.0.3325.109 Mobile Safari/537.36 OPR/32.0.2254.123747", + "expect": { + "vendor": "Motorola", + "model": "XT1580", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto S30", + "ua": "Mozilla/5.0 (Linux; Android 13; XT2243-2 Build/T1SJC33.51-19-7; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Mobile Safari/537.36 T7/13.30 SP-engine/2.53.0 matrixstyle/0 lite baiduboxapp/6.2.0.10 (Baidu; P1 13) NABar/1.0", + "expect": { + "vendor": "Motorola", + "model": "XT2243-2", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto X30 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; XT2241-1 Build/T1SQ33.111-12-19) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/16.1.3.1264 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2241-1", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto Z3 Play", + "ua": "Mozilla/5.0 (Linux; Android 9; Moto Z3 Play) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Moto Z3 Play", + "type": "mobile" + } + }, + { + "desc": "Motorola Moto Z4", + "ua": "Mozilla/5.0 (Linux; U; Android 10; moto z4 Build/QDF30.130-42-5-17; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.128 Mobile Safari/537.36 OPR/2.3.0", + "expect": { + "vendor": "Motorola", + "model": "moto z4", + "type": "mobile" + } + }, + { + "desc": "Motorola Nexus 6", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.20 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "Nexus 6", + "type": "mobile" + } + }, + { + "desc": "Motorola One 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; motorola one 5G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "one 5G", + "type": "mobile" + } + }, + { + "desc": "Motorola P30", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; XT1943-1 Build/OPM1.171019.019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Mobile Safari/537.36 T7/13.50 SP-engine/2.88.0 baiduboxapp/13.50.5.10 (Baidu; P1 8.1.0) NABar/1.0", + "expect": { + "vendor": "Motorola", + "model": "XT1943-1", + "type": "mobile" + } + }, + { + "desc": "Motorola Razr 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; motorola razr 5G Build/S2PS32.57-23-21; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.97 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/497.0.0.47.36;IABMV/1;]", + "expect": { + "vendor": "Motorola", + "model": "razr 5G", + "type": "mobile" + } + }, + { + "desc": "Motorola Razr 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; XT2071-4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "XT2071-4", + "type": "mobile" + } + }, + { + "desc": "Motorola Razr 50 Ultra", + "ua": "Mozilla/5.0 (Linux; Android 14; motorola razr 50 ultra Build/U3UX34.56-29-2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.260 Mobile Safari/537.36", + "expect": { + "vendor": "Motorola", + "model": "razr 50 ultra", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/nintendo.json b/test/data/ua/device/nintendo.json new file mode 100644 index 000000000..eb879b322 --- /dev/null +++ b/test/data/ua/device/nintendo.json @@ -0,0 +1,47 @@ +[ + { + "desc": "Nintendo Switch", + "ua": "Mozilla/5.0 (Nintendo Switch; WifiWebAuthApplet) AppleWebKit/606.4 (KHTML, like Gecko) NF/6.0.1.15.4 NintendoBrowser/5.1.0.20393", + "expect": { + "vendor": "Nintendo", + "model": "Switch", + "type": "console" + } + }, + { + "desc": "Nintendo WiiU", + "ua": "Mozilla/5.0 (Nintendo WiiU) AppleWebKit/536.30 (KHTML, like Gecko) NX/3.0.4.2.9 NintendoBrowser/4.2.0.11146.EU", + "expect": { + "vendor": "Nintendo", + "model": "WiiU", + "type": "console" + } + }, + { + "desc": "Nintendo Wii", + "ua": "Opera/9.10 (Nintendo Wii; U; ; 1621; en)", + "expect": { + "vendor": "Nintendo", + "model": "Wii", + "type": "console" + } + }, + { + "desc": "Nintendo 3DS", + "ua": "Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7610.EU", + "expect": { + "vendor": "Nintendo", + "model": "3DS", + "type": "console" + } + }, + { + "desc": "Nintendo 3DS", + "ua": "Mozilla/5.0 (New Nintendo 3DS like iPhone) AppleWebKit/536.30 (KHTML, like Gecko) NX/3.0.0.5.15 Mobile NintendoBrowser/1.3.10126.EU", + "expect": { + "vendor": "Nintendo", + "model": "3DS", + "type": "console" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/nokia.json b/test/data/ua/device/nokia.json new file mode 100644 index 000000000..375cfcc3f --- /dev/null +++ b/test/data/ua/device/nokia.json @@ -0,0 +1,119 @@ +[ + { + "desc": "Nokia3xx", + "ua": "Nokia303/14.87 CLDC-1.1", + "expect": { + "vendor": "Nokia", + "model": "303", + "type": "mobile" + } + }, + { + "desc": "Nokia 3.2", + "ua": "Mozilla/5.0 (Linux; Android 10; Nokia 3.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "3.2", + "type": "mobile" + } + }, + { + "desc": "Nokia 5800 XpressMusic", + "ua": "Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0 Nokia5800d-1/10.4.016; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413", + "expect": { + "vendor": "Nokia", + "model": "5800d-1", + "type": "mobile" + } + }, + { + "desc": "Nokia 7", + "ua": "Mozilla/5.0 (Linux; Android 11; Nokia 7.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "7.2", + "type": "mobile" + } + }, + { + "desc": "Nokia 808 PureView", + "ua": "Mozilla/5.0 (Symbian; U; Nokia808 PureView; en-GB) AppleWebKit/534.3 (KHTML, like Gecko) Version/3.0 Mobile/1A543a Mobile Safari/534.3", + "expect": { + "vendor": "Nokia", + "model": "808 PureView", + "type": "mobile" + } + }, + { + "desc": "Nokia 808 PureView", + "ua": "Mozilla/5.0 (Symbian/3; Series60/5.5 Nokia808PureView/113.010.1508; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/535.1 (KHTML, like Gecko) NokiaBrowser/8.3.2.21 Mobile Safari/535.1 3gpp-gba", + "expect": { + "vendor": "Nokia", + "model": "808PureView", + "type": "mobile" + } + }, + { + "desc": "Nokia Lumia 630", + "ua": "UCWEB/2.0 (Windows; U; wds 8.10; en-IN; NOKIA; RM-978_1046) U2/1.0.0 UCBrowser/4.2.0.524 U2/1.0.0 Mobile", + "expect": { + "vendor": "Nokia", + "model": "RM-978", + "type": "mobile" + } + }, + { + "desc": "Nokia N9", + "ua": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", + "expect": { + "vendor": "Nokia", + "model": "N9", + "type": "mobile" + } + }, + { + "desc": "Nokia N900", + "ua": "Mozilla/5.0 (Linux; Maemo 5.0; Nokia N900; Build/4.0.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0 Mobile Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "N900", + "type": "mobile" + } + }, + { + "desc": "Nokia T20", + "ua": "Mozilla/5.0 (Linux; Android 12; Nokia T20) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "T20", + "type": "tablet" + } + }, + { + "desc": "Nokia T20", + "ua": "Mozilla/5.0 (Linux; Android 11; Nokia T20 Build/RP1A.201005.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/93.0.4577.62 Safari/537.36", + "expect": { + "vendor": "Nokia", + "model": "T20", + "type": "tablet" + } + }, + { + "desc": "Nokia T21", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; Nokia T21 Build/TP1A.220624.014)", + "expect": { + "vendor": "Nokia", + "model": "T21", + "type": "tablet" + } + }, + { + "desc": "Nokia 2720 Flip", + "ua": "Mozilla/5.0 (Mobile; Nokia_2720_Flip; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5.2", + "expect": { + "vendor": "Nokia", + "model": "2720 Flip", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/nothing.json b/test/data/ua/device/nothing.json new file mode 100644 index 000000000..12b418d2c --- /dev/null +++ b/test/data/ua/device/nothing.json @@ -0,0 +1,65 @@ +[ + { + "desc": "Nothing 1", + "ua": "Mozilla/5.0 (Linux; Android 13; A063) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/22.0 Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A063", + "type": "mobile" + } + }, + { + "desc": "Nothing 2", + "ua": "Mozilla/5.0 (Linux; Android 14; A065 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.53 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A065", + "type": "mobile" + } + }, + { + "desc": "Nothing 2", + "ua": "Mozilla/5.0 (Linux; Android 15; AIN065 Build/AQ3A.240929.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.90 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/496.0.0.45.65;IABMV/1;] FBNV/500", + "expect": { + "vendor": "Nothing", + "model": "AIN065", + "type": "mobile" + } + }, + { + "desc": "Nothing 2a", + "ua": "Mozilla/5.0 (Linux; Android 14; A142 Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.71 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A142", + "type": "mobile" + } + }, + { + "desc": "Nothing 2a Plus", + "ua": "Mozilla/5.0 (Linux; Android 14; A142P Build/UP1A.231005.007) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.107 Mobile Safari/537.36 OPX/2.6", + "expect": { + "vendor": "Nothing", + "model": "A142P", + "type": "mobile" + } + }, + { + "desc": "Nothing 3a", + "ua": "Mozilla/5.0 (Linux; Android 15; A059 Build/AQ3A.241015.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.103 Mobile Safari/537.36", + "expect": { + "vendor": "Nothing", + "model": "A059", + "type": "mobile" + } + }, + { + "desc": "Nothing 3a Pro", + "ua": "Mozilla/5.0 (Linux; Android 15; A059P Build/AQ3A.241015.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/136.0.7103.57 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/511.0.0.73.36;IABMV/1;]", + "expect": { + "vendor": "Nothing", + "model": "A059P", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/nvidia.json b/test/data/ua/device/nvidia.json new file mode 100644 index 000000000..96a2afc98 --- /dev/null +++ b/test/data/ua/device/nvidia.json @@ -0,0 +1,47 @@ +[ + { + "desc": "Tegra Note 7", + "ua": "Mozilla/5.0 (Linux; Android 5.1; TegraNote-P1640 Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Safari/537.36", + "expect": { + "vendor": "Nvidia", + "model": "TegraNote-P1640", + "type": "tablet" + } + }, + { + "desc": "Nvidia Shield", + "ua": "Mozilla/5.0 (Linux; Android 5.1; SHIELD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.74 Mobile Safari/537.36", + "expect": { + "vendor": "Nvidia", + "model": "SHIELD", + "type": "console" + } + }, + { + "desc": "Nvidia Shield Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SHIELD Tablet Build/LVY48E; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/45.0.2454.19 Safari/537.36", + "expect": { + "vendor": "Nvidia", + "model": "SHIELD Tablet", + "type": "tablet" + } + }, + { + "desc": "Nvidia Shield Tablet K1", + "ua": "Mozilla/5.0 (Linux; Android 7.0; SHIELD Tablet K1 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Safari/537.36", + "expect": { + "vendor": "Nvidia", + "model": "SHIELD Tablet K1", + "type": "tablet" + } + }, + { + "desc": "Nvidia Shield TV", + "ua": "Mozilla/5.0 (Linux; Android 11; SHIELD Android TV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36 ", + "expect": { + "vendor": "Nvidia", + "model": "SHIELD Android TV", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/oneplus.json b/test/data/ua/device/oneplus.json new file mode 100644 index 000000000..a02c0f3c9 --- /dev/null +++ b/test/data/ua/device/oneplus.json @@ -0,0 +1,344 @@ +[ + { + "desc": "OnePlus One", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; A0001 Build/KTU84Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.59 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A0001", + "type": "mobile" + } + }, + { + "desc": "OnePlus One", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; OnePlus One A0001 Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.117 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A0001", + "type": "mobile" + } + }, + { + "desc": "OnePlus 2", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; ONE A2003) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A2003", + "type": "mobile" + } + }, + { + "desc": "OnePlus 3", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ONEPLUS A3000 Build/NMF26F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A3000", + "type": "mobile" + } + }, + { + "desc": "OnePlus 6", + "ua": "Mozilla/5.0 (Linux; Android 9; ONEPLUS A6003) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.89 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A6003", + "type": "mobile" + } + }, + { + "desc": "OnePlus 6T", + "ua": "Mozilla/5.0 (Linux; Android 9; ONEPLUS A6010) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "A6010", + "type": "mobile" + } + }, + { + "desc": "OnePlus 7T Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; HD1913) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.57 Mobile Safari/537.36 EdgA/110.0.1587.66", + "expect": { + "vendor": "OnePlus", + "model": "HD1913", + "type": "mobile" + } + }, + { + "desc": "OnePlus 8 Lite", + "ua": "Mozilla/5.0 (Linux; Android 10; OnePlus 8 Lite) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4741.47 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "8 Lite", + "type": "mobile" + } + }, + { + "desc": "OnePlus 8 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; IN2025) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.119 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "IN2025", + "type": "mobile" + } + }, + { + "desc": "OnePlus 8T", + "ua": "Mozilla/5.0 (Linux; Android 11; KB2005) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "KB2005", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9", + "ua": "Mozilla/5.0 (Android 14; SDK 28; OnePlus 9) Gecko/128.0 Firefox/128.0", + "expect": { + "vendor": "OnePlus", + "model": "9", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9", + "ua": "Mozilla/5.0 (Linux; Android 13; LE2113 Build/TP1A.220905.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36 Flipboard/4.3.14/5377,4.3.14.5377", + "expect": { + "vendor": "OnePlus", + "model": "LE2113", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; LE2123) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.128 Mobile Safari/537.36 OPR/73.0.3832.69371", + "expect": { + "vendor": "OnePlus", + "model": "LE2123", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9R", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; LE2100) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 YaApp_Android/21.114.1 YaSearchBrowser/21.114.1 BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "LE2100", + "type": "mobile" + } + }, + { + "desc": "OnePlus 9RT 5G", + "ua": "Mozilla/5.0 (Linux; U; Android 11; en-in; MT2111 Build/RKQ1.210614.002) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 GSA/13.14.15.23.arm64", + "expect": { + "vendor": "OnePlus", + "model": "MT2111", + "type": "mobile" + } + }, + { + "desc": "OnePlus 10 Pro", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 12; NE2213) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.148 YaBrowser/22.7.5.90.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "NE2213", + "type": "mobile" + } + }, + { + "desc": "OnePlus 10RT", + "ua": "Mozilla/5.0 (Linux; Android 13; CPH2413) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "CPH2413", + "type": "mobile" + } + }, + { + "desc": "OnePlus 11R", + "ua": "Mozilla/5.0 (Linux; Android 14; CPH2487) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "CPH2487", + "type": "mobile" + } + }, + { + "desc": "OnePlus 13", + "ua": "Mozilla/5.0 (Linux; Android 15; CPH2653 Build/AP3A.240617.008; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/134.0.6998.92 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/504.0.0.69.64;IABMV/1;] FBNV/5", + "expect": { + "vendor": "OnePlus", + "model": "CPH2653", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-cn; PGKM10 Build/AP3A.240617.008) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.168 Mobile Safari/537.36 HeyTapBrowser/40.9.6.2", + "expect": { + "vendor": "OnePlus", + "model": "PGKM10", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 2", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-CN; PHK110 Build/UKQ1.231108.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/17.3.6.1367 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PHK110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 2 Pro", + "ua": "Mozilla/5.0 (Linux; arm; Android 13; PJA110) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 YaBrowser/23.1.0.284.00 (beta) SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PJA110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 2V", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-cn; PHP110 Build/AP3A.240617.008) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/121.0.6167.71 MQQBrowser/16.3 Mobile Safari/537.36 COVC/047707", + "expect": { + "vendor": "OnePlus", + "model": "PHP110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 3", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-CN; PJE110 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.80 Quark/7.9.7.782 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PJE110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 3 Pro 5G", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-CN; PJX110 Build/UKQ1.231108.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/123.0.6312.80 Quark/7.11.4.814 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PJX110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 3V", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-cn; PJF110 Build/UP1A.231005.007) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/90.0.4430.61 Mobile Safari/537.36 HeyTapBrowser/40.8.33.1.2beta", + "expect": { + "vendor": "OnePlus", + "model": "PJF110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace 5", + "ua": "Mozilla/5.0 (Linux; U; Android 15; zh-cn; PKG110 Build/UKQ1.231108.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.168 Mobile Safari/537.36 HeyTapBrowser/40.9.9.2", + "expect": { + "vendor": "OnePlus", + "model": "PKG110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-CN; PGP110 Build/UKQ1.230924.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/17.2.9.1360 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PGP110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Ace Racing Edition", + "ua": "Mozilla/5.0 (Linux; U; Android 13; zh-CN; PGZ110 Build/TP1A.220905.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.58 UCBrowser/17.3.6.1367 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "PGZ110", + "type": "mobile" + } + }, + { + "desc": "OnePlus Nord CE 4", + "ua": "Mozilla/5.0 (Linux; Android 14; CPH2613) AppleWebKit/537.36 (KHTML, like Gecko) JioSphere/5.0.4 Chrome/119.0.6045.193 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "CPH2613", + "type": "mobile" + } + }, + { + "desc": "OnePlus Nord N10 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; BE2029) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.185 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "BE2029", + "type": "mobile" + } + }, + { + "desc": "OnePlus Nord N30 SE 5G", + "ua": "Mozilla/5.0 (Linux; Android 14; CPH2605 Build/TP1A.220905.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/135.0.7049.92 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/509.0.0.67.28;IABMV/1;]", + "expect": { + "vendor": "OnePlus", + "model": "CPH2605", + "type": "mobile" + } + }, + { + "desc": "OnePlus Nord N100", + "ua": "Mozilla/5.0 (Linux; Android 10; BE2015 Build/QKQ1.200719.002; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.106 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "BE2015", + "type": "mobile" + } + }, + { + "desc": "OnePlus Open", + "ua": "Dalvik/2.1.0 (Linux; U; Android 15; CPH2551 Build/AP3A.240617.008)", + "expect": { + "vendor": "OnePlus", + "model": "CPH2551", + "type": "mobile" + } + }, + { + "desc": "OnePlus Pad Go 11.35", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 14; OPD2304) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.629 YaApp_Android/24.101/apad YaSearchBrowser/24.101/apad BroPP/1.0 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "OPD2304", + "type": "tablet" + } + }, + { + "desc": "OnePlus Pad 2 12.1 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 14; OPD2403 Build/UKQ1.231108.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "OPD2403", + "type": "tablet" + } + }, + { + "desc": "OnePlus Pad 11.61 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 14; OPD2203 Build/UKQ1.230924.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36", + "expect": { + "vendor": "OnePlus", + "model": "OPD2203", + "type": "tablet" + } + }, + { + "desc": "OnePlus Watch 2", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; OPWWE231 Build/TWR7.231113.001.OPWWE231_11_A.117.240703)", + "expect": { + "vendor": "OnePlus", + "model": "OPWWE231", + "type": "wearable" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/oppo.json b/test/data/ua/device/oppo.json new file mode 100644 index 000000000..ab63e2609 --- /dev/null +++ b/test/data/ua/device/oppo.json @@ -0,0 +1,164 @@ +[ + { + "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", + "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", + "expect": { + "vendor": "OPPO", + "model": "R831T", + "type": "mobile" + } + }, + { + "desc": "OPPO R7s", + "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": { + "vendor": "OPPO", + "model": "R7s", + "type": "mobile" + } + }, + { + "desc": "OPPO A3s", + "ua": "Mozilla/5.0 (Linux; Android 8.1; CPH1803 Build/OPM1.171019.026; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "CPH1803", + "type": "mobile" + } + }, + { + "desc": "OPPO A12", + "ua": "Mozilla/5.0 (Linux; Android 9; CPH2083) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "CPH2083", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno", + "ua": "Mozilla/5.0 (Linux; Android 9; PCAT00 Build/PKQ1.190101.001; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PCAT00", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno3 Pro 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; PCLM50) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.117 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PCLM50", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno4 SE", + "ua": "Mozilla/5.0 (Linux; U; Android 10; xx-xx; PEAM00 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PEAM00", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno4 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; PDPM00 Build/QKQ1.200216.002; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PDPM00", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno4 Pro 5G", + "ua": "Mozilla/5.0 (Linux; U; Android 10; xx-xx; PDNT00 Build/QKQ1.200216.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PDNT00", + "type": "mobile" + } + }, + { + "desc": "OPPO Reno5 A", + "ua": "Mozilla/5.0 (Linux; Android 11; A101OP) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "A101OP", + "type": "mobile" + } + }, + { + "desc": "OPPO Find X", + "ua": "Mozilla/5.0 (Linux; Android 8.1; PAFM00 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "PAFM00", + "type": "mobile" + } + }, + { + "desc": "OPPO Find 7a", + "ua": "Mozilla/5.0 (Linux; U; Android 4.3; xx-xx; X9007 Build/JLS36C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "OPPO", + "model": "X9007", + "type": "mobile" + } + }, + { + "desc": "OPPO F5", + "ua": "ozilla/5.0 (Linux; Android 7.1.1; CPH1723) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "CPH1723", + "type": "mobile" + } + }, + { + "desc": "OPPO Watch 46mm", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; OW19W3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.137 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "OW19W3", + "type": "wearable" + } + }, + { + "desc": "OPPO Watch 41mm", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; OW19W2 Build/OPM1.171019.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "OW19W2", + "type": "wearable" + } + }, + { + "desc": "OPPO Watch 2", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; OW20W1 Build/OPM1.171019.026.11_A.37.210713172937; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/61.0.3163.98 Mobile Safari/537.36", + "expect": { + "vendor": "OPPO", + "model": "OW20W1", + "type": "wearable" + } + }, + { + "desc": "OPPO Watch X", + "ua": "Dalvik/2.1.0 (Linux; U; Android 13; OWWE231 Build/TWR7.231113.001.OWWE231_11_A.117.240703)", + "expect": { + "vendor": "OPPO", + "model": "OWWE231", + "type": "wearable" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/ouya.json b/test/data/ua/device/ouya.json new file mode 100644 index 000000000..1fa74c0a1 --- /dev/null +++ b/test/data/ua/device/ouya.json @@ -0,0 +1,11 @@ +[ + { + "desc": "Ouya", + "ua": "Mozilla/5.0 (Linux; Android 4.1.2; OUYA Console Build/JZO54L-OUYA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.84 Safari/537.36", + "expect": { + "vendor": "OUYA", + "model": "undefined", + "type": "console" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/panasonic.json b/test/data/ua/device/panasonic.json new file mode 100644 index 000000000..568c21de2 --- /dev/null +++ b/test/data/ua/device/panasonic.json @@ -0,0 +1,47 @@ +[ + { + "desc": "Panasonic T31", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Panasonic T31 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.170 Mobile Safari/537.36 ", + "expect": { + "vendor": "Panasonic", + "model": "T31", + "type": "mobile" + } + }, + { + "desc": "Panasonic TX-32CSW514 SmartTV", + "ua": "HbbTV/1.2.1 (;Panasonic;VIERA 2015;3.014;a001-003 4000-0000;)", + "expect": { + "vendor": "Panasonic", + "model": "VIERA 2015", + "type": "smarttv" + } + }, + { + "desc": "Panasonic TX-40FXW724 SmartTV", + "ua": "HbbTV/1.4.1 (+DRM;Panasonic;SmartTV2018mid;3.024;4301-0003 0002-0000;SmartTV2018;)", + "expect": { + "vendor": "Panasonic", + "model": "SmartTV2018mid", + "type": "smarttv" + } + }, + { + "desc": "Panasonic TX-43HXW904 SmartTV", + "ua": "HbbTV/1.5.1 (+DRM;Panasonic;SmartTV2020mid;3.326;4301-0003 0008-0000;com.panasonic.SmartTV2020mid;)", + "expect": { + "vendor": "Panasonic", + "model": "SmartTV2020mid", + "type": "smarttv" + } + }, + { + "desc": "Panasonic DMR-HST130 SAT receiver", + "ua": "HbbTV/1.1.1 (+PVR;Panasonic;DIGA WebKit M8658;3.420;;)", + "expect": { + "vendor": "Panasonic", + "model": "DIGA WebKit M8658", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/philips.json b/test/data/ua/device/philips.json new file mode 100644 index 000000000..ebc5af032 --- /dev/null +++ b/test/data/ua/device/philips.json @@ -0,0 +1,83 @@ +[ + { + "desc": "Philips S616", + "ua": "Mozilla/5.0 (Linux; Android 5.1; Philips S616 Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Mobile Safari/537.36", + "expect": { + "vendor": "Philips", + "model": "S616", + "type": "mobile" + } + }, + { + "desc": "Philips W8510", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Philips W8510 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.68 Mobile Safari/537.36", + "expect": { + "vendor": "Philips", + "model": "W8510", + "type": "mobile" + } + }, + { + "desc": "Philips SmartTV", + "ua": "Mozilla/5.0 (Linux; Android 11; PHILIPS 4k TV Build/RTXC.231010.082.A1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.71 Mobile Safari/537.36", + "expect": { + "vendor": "PHILIPS", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Philips SmartTV", + "ua": "Opera/9.80 HbbTV/1.1.1 (; Philips; ; ; ; ) NETTV/4.0.2; en) Version/11.60", + "expect": { + "vendor": "Philips", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Philips 32PFL6606K/02 SmartTV (2011)", + "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", + "expect": { + "vendor": "Philips", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Philips 32PFL6606K/02 SmartTV (2013)", + "ua": "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.1.0; en) Presto/2.6.33 Version/10.70", + "expect": { + "vendor": "Philips", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Philips 32PHS5301/12 SmartTV (2016)", + "ua": "Mozilla/5.0 (Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36 OPR/29.0.1803.0 OMI/4.5.23.37.MOT2.13 HbbTV/1.2.1 (;Philips;32PHS5301/12;;_TV_MT5800;) Firmware/TPM161E_012.002.045.001 en", + "expect": { + "vendor": "Philips", + "model": "32PHS5301/12", + "type": "smarttv" + } + }, + { + "desc": "Philips PH0M_EA_T32", + "ua": "Mozilla/5.0 (Linux; Android 10; Philips FHD Android TV Build/QTG3.201102.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.186 Mobile Safari/537.36", + "expect": { + "vendor": "Philips", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Philips PH3M_AL_T32", + "ua": "Mozilla/5.0 (Linux; Android 11; Philips Google TV TA7 Build/RTM5.220609.199; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/111.0.5563.58 Mobile Safari/537.36", + "expect": { + "vendor": "Philips", + "model": "undefined", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/pico.json b/test/data/ua/device/pico.json new file mode 100644 index 000000000..029a7c153 --- /dev/null +++ b/test/data/ua/device/pico.json @@ -0,0 +1,47 @@ +[ + { + "desc": "Pico 4", + "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.8.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.38 Chrome/105.0.5195.68 VR Safari/537.36", + "expect": { + "vendor": "PICO", + "model": "4", + "type": "xr" + } + }, + { + "desc": "Pico 4", + "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": { + "vendor": "PICO", + "model": "4", + "type": "xr" + } + }, + { + "desc": "Pico Neo3 Link", + "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": { + "vendor": "Pico", + "model": "Neo3 Link", + "type": "xr" + } + }, + { + "desc": "Pico Neo 3", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Pico Neo 3 OS5.12.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.48 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", + "expect": { + "vendor": "Pico", + "model": "Neo 3", + "type": "xr" + } + }, + { + "desc": "Pico Neo 3 Pro", + "ua": "Mozilla/5.0 (X11; Linux x86_64; Pico Neo 3 Pro OS5.9.9.0 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.46 Chrome/105.0.5195.68 VR Safari/537.36 OculusBrowser/7.0", + "expect": { + "vendor": "Pico", + "model": "Neo 3 Pro", + "type": "xr" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/polytron.json b/test/data/ua/device/polytron.json new file mode 100644 index 000000000..595c27f68 --- /dev/null +++ b/test/data/ua/device/polytron.json @@ -0,0 +1,38 @@ +[ + { + "desc": "Polytron Prime 7 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 7.0; POLYTRON_P552 Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/64.0.3282.137 Mobile Safari/537.36 OPR/50.0.2254.149182", + "expect": { + "vendor": "POLYTRON", + "model": "P552", + "type": "mobile" + } + }, + { + "desc": "Polytron Rocket T1", + "ua": "Mozilla/5.0 (Linux; U; Android 5.0; en-US; POLYTRON R2501 Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/13.1.2.1293 Mobile Safari/537.36", + "expect": { + "vendor": "POLYTRON", + "model": "R2501", + "type": "mobile" + } + }, + { + "desc": "Polytron Rocket T6", + "ua": "Mozilla/5.0 (Linux; Android 7.0; POLYTRON R2509) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.92 Mobile Safari/537.36", + "expect": { + "vendor": "POLYTRON", + "model": "R2509", + "type": "mobile" + } + }, + { + "desc": "Polytron Zap 6 Posh", + "ua": "Mozilla/5.0 (Linux; U; Android 5.1; in-ID; POLYTRON_4G501 Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.10.0.1163 UCTurbo/1.9.9.900 Mobile Safari/537.36", + "expect": { + "vendor": "POLYTRON", + "model": "4G501", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/realme.json b/test/data/ua/device/realme.json new file mode 100644 index 000000000..c16d4d0ac --- /dev/null +++ b/test/data/ua/device/realme.json @@ -0,0 +1,74 @@ +[ + { + "desc": "Realme C1", + "ua": "Mozilla/5.0 (Linux; Android 8.1; RMX1811 Build/OPM1.171019.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.126 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX1811", + "type": "mobile" + } + }, + { + "desc": "Realme C2", + "ua": "Mozilla/5.0 (Linux; Android 9; RMX1941) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX1941", + "type": "mobile" + } + }, + { + "desc": "Realme Narzo 20", + "ua": "Mozilla/5.0 (Linux; U; Android 10; xx-xx; RMX2193 Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.80 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX2193", + "type": "mobile" + } + }, + { + "desc": "Realme 2 Pro", + "ua": "Mozilla/5.0 (Linux; Android 9; RMX1801) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX1801", + "type": "mobile" + } + }, + { + "desc": "Realme 3 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; RMX1851) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX1851", + "type": "mobile" + } + }, + { + "desc": "Realme 8", + "ua": "Mozilla/5.0 (Linux; Android 12; RMX3085) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX3085", + "type": "mobile" + } + }, + { + "desc": "Realme 9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; RMX3471) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX3471", + "type": "mobile" + } + }, + { + "desc": "Realme GT Master", + "ua": "Mozilla/5.0 (Linux; Android 13; RMX3363) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Realme", + "model": "RMX3363", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/retroid.json b/test/data/ua/device/retroid.json new file mode 100644 index 000000000..fc7a83daf --- /dev/null +++ b/test/data/ua/device/retroid.json @@ -0,0 +1,11 @@ +[ + { + "desc": "Retroid Pocket 4 Pro", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 13; Retroid Pocket 4 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.96 YaBrowser/24.4.3.96.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Retroid", + "model": "Pocket 4 Pro", + "type": "console" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/roku.json b/test/data/ua/device/roku.json new file mode 100644 index 000000000..a042f29b3 --- /dev/null +++ b/test/data/ua/device/roku.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Roku", + "ua": "Mozilla/5.0 (Roku) AppleWebKit/537.36 (KHTML, like Gecko) Web/1.1 Safari/537.36", + "expect": { + "vendor": "Roku", + "model": "", + "type": "smarttv" + } + }, + { + "desc": "Roku", + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36 Roku/DVP-8.10 (468.10E04145A)", + "expect": { + "vendor": "Roku", + "model": "DVP-8.10", + "type": "smarttv" + } + }, + { + "desc": "Roku", + "ua": "Roku4640X/DVP-7.70 (297.70E04154A)", + "expect": { + "vendor": "Roku", + "model": "DVP-7.70", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/samsung.json b/test/data/ua/device/samsung.json new file mode 100644 index 000000000..36b68db0c --- /dev/null +++ b/test/data/ua/device/samsung.json @@ -0,0 +1,398 @@ +[ + { + "desc": "Samsung Galaxy A21s", + "ua": "Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-A217F) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/11.0 Chrome/75.0.3770.143 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A217F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy A31", + "ua": "Mozilla/5.0 (Linux; Android 10; SM-A315G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A315G", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy A50", + "ua": "Mozilla/5.0 (Linux; Android 9; SM-A505F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.105 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A505F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy A50s", + "ua": "Mozilla/5.0 (Linux; Android 11; SM-A507FN) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A507FN", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy A52s", + "ua": "Mozilla/5.0 (Linux; Android 13; SM-A528B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A528B", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy A80", + "ua": "Mozilla/5.0 (Linux; Android 9; SM-A805F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.112 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-A805F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Fold", + "ua": "Mozilla/5.0 (Linux; Android 9; SAMSUNG SM-F900U Build/PPR1.180610.011) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/9.2 Chrome/67.0.3396.87 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-F900U", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Z Flip", + "ua": "Mozilla/5.0 (Linux; Android 10; SM-F700N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-F700N", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Z Fold2", + "ua": "Mozilla/5.0 (Linux; Android 10; SM-F916B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-F916B", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy S10E", + "ua": "Mozilla/5.0 (Linux; Android 9; SM-G970F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-G970F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy S20 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; SCG01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SCG01", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Note 10+", + "ua": "Mozilla/5.0 (Linux; Android 9; SM-N976V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.89 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-N976V", + "type": "mobile" + } + }, + { + "desc": "Samsung SM-C5000", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; SM-C5000 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/51.0.2704.81 Mobile Safari/537.36 wkbrowser 4.1.35 3065", + "expect": { + "vendor": "Samsung", + "model": "SM-C5000", + "type": "mobile" + } + }, + { + "desc": "Samsung C8", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; SM-C7108) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-C7108", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Note 8", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; GT-N5100 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.141 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "GT-N5100", + "type": "tablet" + } + }, + { + "desc": "Samsung SM-T231", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-T231 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-T231", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab 6 Lite", + "ua": "Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-P610) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/12.0 Chrome/79.0.3945.136 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-P610", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab A 9.7", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; SM-P550 Build/NMF26X; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.90 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-P550", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab A 10.1", + "ua": " Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-T515) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/13.0 Chrome/83.0.4103.106 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-T515", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab S7", + "ua": "Mozilla/5.0 (Linux; Android 10; SM-T870) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-T870", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab S8", + "ua": "Mozilla/5.0 (Linux; Android 12; SM-X706B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-X706B", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab S", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-T700 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-T700", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Tab Pro 10.1", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-T520 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-T520", + "type": "tablet" + } + }, + { + "desc": "Samsung Galaxy Watch", + "ua": "Mozilla/5.0 (Linux; Tizen 5.5; SAMSUNG SM-R805W) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.0 Chrome/69.0.3497.106 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R805W", + "type": "wearable" + } + }, + { + "desc": "Samsung Galaxy Watch Active 2", + "ua": "Mozilla/5.0 (Linux; Tizen 5.5; SAMSUNG SM-R820) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.0 Chrome/69.0.3497.106 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R820", + "type": "wearable" + } + }, + { + "desc": "Samsung Galaxy Watch4", + "ua": "Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-R875U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/2.2. Chrome/102.0.5005.125 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R875U", + "type": "wearable" + } + }, + { + "desc": "Samsung Galaxy Watch5 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; SAMSUNG SM-R925U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/3.2. Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-R925U", + "type": "wearable" + } + }, + { + "desc": "Samsung Galaxy Watch7", + "ua": "Dalvik/2.1.0 (Linux; U; Android 14; SM-L300 Build/AW2E.240318.016)", + "expect": { + "vendor": "Samsung", + "model": "SM-L300", + "type": "wearable" + } + }, + { + "desc": "Samsung Note 10.1", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SM-P605) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-P605", + "type": "tablet" + } + }, + { + "desc": "Samsung SmartTV2011", + "ua": "HbbTV/1.1.1 (;;;;;) Maple;2011", + "expect": { + "vendor": "Samsung", + "model": "SmartTV2011", + "type": "smarttv" + } + }, + { + "desc": "Samsung SmartTV2012", + "ua": "HbbTV/1.1.1 (;Samsung;SmartTV2012;;;) WebKit", + "expect": { + "vendor": "Samsung", + "model": "SmartTV2012", + "type": "smarttv" + } + }, + { + "desc": "Samsung SmartTV2014", + "ua": "HbbTV/1.1.1 (;Samsung;SmartTV2014;T-NT14UDEUC-1060.4;;) WebKit", + "expect": { + "vendor": "Samsung", + "model": "SmartTV2014", + "type": "smarttv" + } + }, + { + "desc": "Samsung SmartTV", + "ua": "Mozilla/5.0 (SMART-TV; Linux; Tizen 2.3) AppleWebkit/538.1 (KHTML, like Gecko) SamsungBrowser/1.0 TV Safari/538.1", + "expect": { + "vendor": "Samsung", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Samsung SmartTV", + "ua": "Mozilla/5.0 (SMART-TV; Linux; Tizen 8.0) AppleWebKit/537.36 (KHTML, like Gecko) Samsung Browser/7.0 Chrome/108.0.5359.1 TV Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "undefined", + "type": "smarttv" + } + }, + { + "desc": "Samsung SmartTV HBBTV", + "ua": "HbbTV/1.5.1 (+DRM;Samsung;SmartTV2021:UAU7000;T-KSU2EDEUC-1506.0;KantSU2e;urn:samsungtv:familyname:21_KANTSU2E_UHD_BASIC:2021;) Tizen/6.0 (+TVPLUS+SmartHubLink) Chrome/76 LaTivu_1.0.1_2021 RVID/17", + "expect": { + "vendor": "Samsung", + "model": "SmartTV2021:UAU7000", + "type": "smarttv" + } + }, + { + "desc": "Galaxy Nexus", + "ua": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19", + "expect": { + "vendor": "Samsung", + "model": "Galaxy Nexus", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy C9 Pro", + "ua": "Mozilla/5.0 (Linux; Android 6.0; SAMSUNG SM-C900F Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.2 Chrome/44.0.2403.133 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-C900F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy S5", + "ua": "Mozilla/5.0 (Linux; Android 5.0; SM-G900F Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.78 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-G900F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy J7 Prime", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; SM-G610F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-G610F", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy S6", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-G920I Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-G920I", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy S6 Edge", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; SM-G925I Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.135 Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-G925I", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Note 5 Chrome", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SM-N920C Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-N920C", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Note 5 Samsung Browser", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SAMSUNG SM-N920C Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/4.0 Chrome/44.0.2403.133 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "SM-N920C", + "type": "mobile" + } + }, + { + "desc": "Samsung Galaxy Gear Live", + "ua": "Mozilla/5.0 (Linux; Android 4.4; Gear Live Build/KMV78X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/19.77.34.5 Mobile Safari/537.36", + "expect": { + "vendor": "Samsung", + "model": "Gear Live", + "type": "wearable" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/sharp.json b/test/data/ua/device/sharp.json new file mode 100644 index 000000000..32d53409f --- /dev/null +++ b/test/data/ua/device/sharp.json @@ -0,0 +1,56 @@ +[ + { + "desc": "Sharp AQUOS-TVX19B", + "ua": "Mozilla/5.0 (Linux; Android 9; AQUOS-TVX19B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "AQUOS-TVX19B", + "type": "smarttv" + } + }, + { + "desc": "Sharp Aquos B10", + "ua": "Mozilla/5.0 (Linux; Android 7.0; SH-A01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "SH-A01", + "type": "mobile" + } + }, + { + "desc": "Sharp Aquos L2", + "ua": "Mozilla/5.0 (Linux; Android 7.0; SH-L02 Build/S4045) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "SH-L02", + "type": "mobile" + } + }, + { + "desc": "Sharp Aquos L2", + "ua": "Mozilla/5.0 (Linux; Android 7.0; SH-L02) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "SH-L02", + "type": "mobile" + } + }, + { + "desc": "Sharp Aquos R2", + "ua": "Mozilla/5.0 (Linux; Android 8.0; SHV42) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "SHV42", + "type": "mobile" + } + }, + { + "desc": "Docomo SH-02M", + "ua": "Mozilla/5.0 (Linux; Android 9; SH-02M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "Sharp", + "model": "SH-02M", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/smartfren.json b/test/data/ua/device/smartfren.json new file mode 100644 index 000000000..5413bb70b --- /dev/null +++ b/test/data/ua/device/smartfren.json @@ -0,0 +1,29 @@ +[ + { + "desc": "Smartfren Andromax L", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Andromax B26D2H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Smartfren", + "model": "Andromax B26D2H", + "type": "mobile" + } + }, + { + "desc": "Smartfren Andromax G2", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Smartfren Andromax AD9A1H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.83 Mobile Safari/537.36", + "expect": { + "vendor": "Smartfren", + "model": "Andromax AD9A1H", + "type": "mobile" + } + }, + { + "desc": "Smartfren New Andromax I", + "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; id-id; New Andromax-i Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Smartfren", + "model": "New Andromax-i", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/sony.json b/test/data/ua/device/sony.json new file mode 100644 index 000000000..d4a3c21cb --- /dev/null +++ b/test/data/ua/device/sony.json @@ -0,0 +1,263 @@ +[ + { + "desc": "SONY Xperia 1 III", + "ua": "Mozilla/5.0 (Linux; Android 11; A101SO) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "A101SO", + "type": "mobile" + } + }, + { + "desc": "Sony G8141 (Xperia XZ1)", + "ua": "Mozilla/5.0 (Linux; Android 9; SO-01K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "SO-01K", + "type": "mobile" + } + }, + { + "desc": "Sony G8141 (Xperia XZ Premium)", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; G8141) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "G8141", + "type": "mobile" + } + }, + { + "desc": "Sony C5303 (Xperia SP)", + "ua": "Mozilla/5.0 (Linux; Android 4.3; C5303 Build/12.1.A.1.205) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.93 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "C5303", + "type": "mobile" + } + }, + { + "desc": "Sony SO-02F (Xperia Z1 F)", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; SO-02F Build/14.1.H.2.119) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "SO-02F", + "type": "mobile" + } + }, + { + "desc": "Sony D6653 (Xperia Z3)", + "ua": "Mozilla/5.0 (Linux; Android 4.4; D6653 Build/23.0.A.0.376) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.141 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "D6653", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia SOL25 (ZL2)", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4; SOL25 Build/17.1.1.C.1.64) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Sony", + "model": "SOL25", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia SP", + "ua": "Mozilla/5.0 (Linux; Android 4.3; C5302 Build/12.1.A.1.201) AppleWebkit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "C5302", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia L4", + "ua": "Mozilla/5.0 (Linux; Android 9; XQ-AD51) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.83 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-AD51", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia 1ii", + "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AT51) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-AT51", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia 1ii", + "ua": "Mozilla/5.0 (Linux; Android 10; SOG01) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.127 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "SOG01", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia 1 VI", + "ua": "Mozilla/5.0 (Linux; Android 14; XQ-EC44) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-EC44", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia 10ii", + "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AU52) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-AU52", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia 10 III Lite", + "ua": "Mozilla/5.0 (Linux; Android 12; XQ-BT44 Build/62.1.A.1.227; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.61 Mobile Safari/537.36 Sleipnir/3.6.0", + "expect": { + "vendor": "Sony", + "model": "XQ-BT44", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia 10 VI", + "ua": "Mozilla/5.0 (Linux; Android 14; XQ-ES72 Build/70.0.A.2.233; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.71 Mobile Safari/537.36 Line/14.16.0/IAB", + "expect": { + "vendor": "Sony", + "model": "XQ-ES72", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; XQ-AQ52) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.185 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-AQ52", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; Sony XQ-AQ52) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-AQ52", + "type": "mobile" + } + }, + { + "desc": "Sony Xperia Pro I", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; XQ-BE72) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.148 YaBrowser/22.7.3.82.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "XQ-BE72", + "type": "mobile" + } + }, + { + "desc": "Sony SGP521 (Xperia Z2 Tablet)", + "ua": "Mozilla/5.0 (Linux; Android 4.4; SGP521 Build/17.1.A.0.432) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "Xperia Tablet", + "type": "tablet" + } + }, + { + "desc": "Sony Xperia Z2 Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; SGP561) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.99 Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "Xperia Tablet", + "type": "tablet" + } + }, + { + "desc": "Sony Tablet S", + "ua": "Mozilla/5.0 (Linux; U; Android 3.1; Sony Tablet S Build/THMAS10000) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13", + "expect": { + "vendor": "Sony", + "model": "Xperia Tablet", + "type": "tablet" + } + }, + { + "desc": "Sony Tablet Z LTE", + "ua": "Mozilla/5.0 (Linux; U; Android 4.1; SonySGP321 Build/10.2.C.0.143) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "Sony", + "model": "Xperia Tablet", + "type": "tablet" + } + }, + { + "desc": "Sony BRAVIA 4K GB ATV3", + "ua": "Mozilla/5.0 (Linux; Andr0id 9; BRAVIA 4K GB ATV3 Build/PTT1.190515.001.S38) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 OPR/46.0.2207.0 OMI/4.13.0.180.DIA5.104 Model/Sony-BRAVIA-4K-GB-ATV3", + "expect": { + "vendor": "Sony", + "model": "BRAVIA 4K GB ATV3", + "type": "smarttv" + } + }, + { + "desc": "Sony BRAVIA 4K GB ATV3", + "ua": "Mozilla/5.0 (Linux; Android 9; BRAVIA 4K GB ATV3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "BRAVIA 4K GB ATV3", + "type": "smarttv" + } + }, + { + "desc": "Sony Bravia 4k UR2", + "ua": "Mozilla/5.0 (Linux: Andr0id 9: BRAVIA 4K UR2 Build/PTT1.190515.001.S104) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 OPR/46.0.2207.0 OMI/4.13.5.431.DIA5HBBTV.250 Model/Sony-BRAVIA-4K-UR2", + "expect": { + "vendor": "Sony", + "model": "BRAVIA 4K UR2", + "type": "smarttv" + } + }, + { + "desc": "PlayStation 4", + "ua": "Mozilla/5.0 (PlayStation 4 3.00) AppleWebKit/537.73 (KHTML, like Gecko)", + "expect": { + "vendor": "Sony", + "model": "PlayStation 4", + "type": "console" + } + }, + { + "desc": "PlayStation 5", + "ua": "Mozilla/5.0 (Playstation; Playstation 5/1.05) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Safari/605.1.15", + "expect": { + "vendor": "Sony", + "model": "Playstation 5", + "type": "console" + } + }, + { + "desc": "PlayStation Vita", + "ua": "Mozilla/5.0 (PlayStation Vita 3.52) AppleWebKit/537.73 (KHTML, like Gecko) Silk/3.2", + "expect": { + "vendor": "Sony", + "model": "PlayStation Vita", + "type": "console" + } + }, + { + "desc": "Sony SmartWatch 3", + "ua": "Mozilla/5.0 (Linux; Android 5.0.2; SmartWatch 3 Build/LWX49K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/19.77.34.5 Mobile Safari/537.36", + "expect": { + "vendor": "Sony", + "model": "SmartWatch 3", + "type": "wearable" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/tcl.json b/test/data/ua/device/tcl.json new file mode 100644 index 000000000..8c805371d --- /dev/null +++ b/test/data/ua/device/tcl.json @@ -0,0 +1,488 @@ +[ + { + "desc": "TCL 10 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T790Y) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36 EdgA/114.0.1823.43", + "expect": { + "vendor": "TCL", + "model": "T790Y", + "type": "mobile" + } + }, + { + "desc": "TCL 10 5G UW", + "ua": "Mozilla/5.0 (Linux; Android 10; T790S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T790S", + "type": "mobile" + } + }, + { + "desc": "TCL 10 Plus", + "ua": "Mozilla/5.0 (Linux; Android 11; T782H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Mobile Safari/537.36 OPR/64.3.3282.60839", + "expect": { + "vendor": "TCL", + "model": "T782H", + "type": "mobile" + } + }, + { + "desc": "TCL 10 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; T799B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.87 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T799B", + "type": "mobile" + } + }, + { + "desc": "TCL 10 SE", + "ua": "Mozilla/5.0 (Linux; Android 10; T766H_RU) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T766H", + "type": "mobile" + } + }, + { + "desc": "TCL 10 TabMax", + "ua": "Mozilla/5.0 (Linux; Android 11; 9296Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9296Q", + "type": "tablet" + } + }, + { + "desc": "TCL 10 TabMax 4G", + "ua": "Mozilla/5.0 (Linux; Android 10; 9295G_EEA) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9295G", + "type": "tablet" + } + }, + { + "desc": "TCL 10 TabMax WiFi", + "ua": "Mozilla/5.0 (Linux; Android 10; 9296G_TR) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9296G", + "type": "tablet" + } + }, + { + "desc": "TCL 10L", + "ua": "Mozilla/5.0 (Linux; Android 10; T770B Build/QKQ1.200329.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36 GSA/11.41.10.23.arm64", + "expect": { + "vendor": "TCL", + "model": "T770B", + "type": "mobile" + } + }, + { + "desc": "TCL 10L", + "ua": "Mozilla/5.0 (Linux; Android 11; T770H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T770H", + "type": "mobile" + } + }, + { + "desc": "TCL 20 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T781) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T781", + "type": "mobile" + } + }, + { + "desc": "TCL 20 Pro 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T810S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36 EdgA/113.0.1774.63", + "expect": { + "vendor": "TCL", + "model": "T810S", + "type": "mobile" + } + }, + { + "desc": "TCL 20 SE", + "ua": "Mozilla/5.0 (Linux; Android 11; T671H Build/RKQ1.201112.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/99.0.4844.73 Mobile Safari/537.36 GoogleApp/13.9.7.23.arm64", + "expect": { + "vendor": "TCL", + "model": "T671H", + "type": "mobile" + } + }, + { + "desc": "TCL 20 XE", + "ua": "Mozilla/5.0 (Linux; Android 11; 5087Z) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "5087Z", + "type": "mobile" + } + }, + { + "desc": "TCL 20B", + "ua": "Mozilla/5.0 (Linux; Android 11; 6159K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6159K", + "type": "mobile" + } + }, + { + "desc": "TCL 205", + "ua": "Mozilla/5.0 (Linux; Android 11; 4187D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "4187D", + "type": "mobile" + } + }, + { + "desc": "TCL 20E", + "ua": "Mozilla/5.0 (Linux; Android 11; 6125A) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/18.0 Chrome/99.0.4844.88 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6125A", + "type": "mobile" + } + }, + { + "desc": "TCL 20L", + "ua": "Mozilla/5.0 (Linux; Android 11; T774H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.59 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T774H", + "type": "mobile" + } + }, + { + "desc": "TCL 20L Plus", + "ua": "Mozilla/5.0 (Linux; Android 11; T775H Build/RKQ1.210107.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.61 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T775H", + "type": "mobile" + } + }, + { + "desc": "TCL 20R 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T767H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.97 Mobile Safari/537.36 OPR/71.3.3718.67322", + "expect": { + "vendor": "TCL", + "model": "T767H", + "type": "mobile" + } + }, + { + "desc": "TCL 20S", + "ua": "Mozilla/5.0 (Linux; Android 11; T773O) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T773O", + "type": "mobile" + } + }, + { + "desc": "TCL 20Y", + "ua": "Mozilla/5.0 (Linux; Android 11; 6156D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.134 Mobile Safari/537.36 OPR/70.3.3653.66287", + "expect": { + "vendor": "TCL", + "model": "6156D", + "type": "mobile" + } + }, + { + "desc": "TCL 30 V 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; T781S Build/RKQ1.210614.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/369.0.0.5.110;]", + "expect": { + "vendor": "TCL", + "model": "T781S", + "type": "mobile" + } + }, + { + "desc": "TCL 30 XE 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T767W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/416.0.0.35.85;]", + "expect": { + "vendor": "TCL", + "model": "T767W", + "type": "mobile" + } + }, + { + "desc": "TCL 305", + "ua": "Mozilla/5.0 (Linux; arm; Android 11; 6102D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.167 YaBrowser/22.7.6.96.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6102D", + "type": "mobile" + } + }, + { + "desc": "TCL 306", + "ua": "Mozilla/5.0 (Linux; Android 12; 6102H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/107.0.5304.141 Mobile Safari/537.36[FBAN/EMA;FBLC/it_IT;FBAV/332.0.0.22.108;]", + "expect": { + "vendor": "TCL", + "model": "6102H", + "type": "mobile" + } + }, + { + "desc": "TCL 30", + "ua": "Mozilla/5.0 (Linux; Android 12; T676H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T676H", + "type": "mobile" + } + }, + { + "desc": "TCL 30+", + "ua": "Mozilla/5.0 (Linux; Android 12; T676J) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T676J", + "type": "mobile" + } + }, + { + "desc": "TCL 30 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T776H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.104 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T776H", + "type": "mobile" + } + }, + { + "desc": "TCL 30 LE", + "ua": "Mozilla/5.0 (Linux; Android 12; 4188V Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36[FBAN/EMA;FBLC/en_US;FBAV/352.0.0.14.108;]", + "expect": { + "vendor": "TCL", + "model": "4188V", + "type": "mobile" + } + }, + { + "desc": "TCL 30 SE", + "ua": "Mozilla/5.0 (Linux; Android 12; 6165H Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/108.0.5359.128 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/396.1.0.28.104;]", + "expect": { + "vendor": "TCL", + "model": "6165H", + "type": "mobile" + } + }, + { + "desc": "TCL 30E", + "ua": "Mozilla/5.0 (Linux; Android 12; 6127I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "6127I", + "type": "mobile" + } + }, + { + "desc": "TCL 40 NxtPaper", + "ua": "Mozilla/5.0 (Linux; Android 13; T612B Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.53 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T612B", + "type": "mobile" + } + }, + { + "desc": "TCL 4056S", + "ua": "Mozilla/5.0 (Mobile; TCL 4056S; rv:84.0) Gecko/84.0 Firefox/84.0 KAIOS/3.0", + "expect": { + "vendor": "TCL", + "model": "4056S", + "type": "mobile" + } + }, + { + "desc": "TCL A3", + "ua": "Mozilla/5.0 (Linux; Android 11; A509DL Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/101.0.4951.61 Mobile Safari/537.36 GSA/13.18.7.23.arm64", + "expect": { + "vendor": "TCL", + "model": "A509DL", + "type": "mobile" + } + }, + { + "desc": "TCL A30", + "ua": "Mozilla/5.0 (Linux; Android 11; 5102L Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/413.0.0.30.104;]", + "expect": { + "vendor": "TCL", + "model": "5102L", + "type": "mobile" + } + }, + { + "desc": "TCL 40 SE", + "ua": "Mozilla/5.0 (Linux; Android 13; T610K Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T610K", + "type": "mobile" + } + }, + { + "desc": "TCL 40 XE 5G", + "ua": "Mozilla/5.0 (Linux; Android 13; T609DL Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/125.0.6422.136 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/466.1.0.57.85;]", + "expect": { + "vendor": "TCL", + "model": "T609DL", + "type": "mobile" + } + }, + { + "desc": "TCL 403", + "ua": "Mozilla/5.0 (Linux; Android 12; T431D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T431D", + "type": "mobile" + } + }, + { + "desc": "TCL 405", + "ua": "Mozilla/5.0 (Linux; Android 12; T506D Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.162 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/418.0.0.33.69;]", + "expect": { + "vendor": "TCL", + "model": "T506D", + "type": "mobile" + } + }, + { + "desc": "TCL 408", + "ua": "Mozilla/5.0 (Linux; U; Android 12; T507U Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/105.0.5195.136 Mobile Safari/537.36 OPR/75.0.2254.68857", + "expect": { + "vendor": "TCL", + "model": "T507U", + "type": "mobile" + } + }, + { + "desc": "TCL 40R 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T771K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36 EdgA/114.0.1823.37", + "expect": { + "vendor": "TCL", + "model": "T771K", + "type": "mobile" + } + }, + { + "desc": "TCL Ion X", + "ua": "Mozilla/5.0 (Linux; Android 12; T430W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.60 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T430W", + "type": "mobile" + } + }, + { + "desc": "TCL NxtPaper 11", + "ua": "Mozilla/5.0 (Linux; Android 13; 9466X Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.179 Safari/537.36 [FB_IAB/FB4A;FBAV/473.0.0.41.81;]", + "expect": { + "vendor": "TCL", + "model": "9466X", + "type": "tablet" + } + }, + { + "desc": "TCL Stylus 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; T779W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.2 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "T779W", + "type": "mobile" + } + }, + { + "desc": "TCL Tab 8 4G", + "ua": "Mozilla/5.0 (Linux; Android 10; 9048S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9048S", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 8 LE", + "ua": "Mozilla/5.0 (Linux; Android 12; 9137W Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.61 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9137W", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 FHD 4G", + "ua": "Mozilla/5.0 (Linux; Android 11; 9060G Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.196 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9060G", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 HD 4G", + "ua": "Mozilla/5.0 (Linux; Android 11; 9060X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9060X", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 LTE", + "ua": "Mozilla/5.0 (Linux; Android 13; 8196G Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/126.0.6478.162 Safari/537.36 [FB_IAB/FB4A;FBAV/471.0.0.35.80;]", + "expect": { + "vendor": "TCL", + "model": "8196G", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 13; 8496G Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.61 Safari/537.36 [FB_IAB/FB4A;FBAV/474.0.0.52.74;]", + "expect": { + "vendor": "TCL", + "model": "8496G", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10L", + "ua": "Mozilla/5.0 (Linux; Android 11; 8491X_EEA Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/107.0.5304.105 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "8491X", + "type": "tablet" + } + }, + { + "desc": "TCL Tab 10s 4G", + "ua": "Mozilla/5.0 (Linux; Android 11; 9080G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "9080G", + "type": "tablet" + } + }, + { + "desc": "TCL Xess P17AA", + "ua": "Mozilla/5.0 (Linux; Android 5.1; TCL Xess P17AA Build/LMY47D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.85 Safari/537.36", + "expect": { + "vendor": "TCL", + "model": "Xess P17AA", + "type": "tablet" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/technisat.json b/test/data/ua/device/technisat.json new file mode 100644 index 000000000..e9dca81b1 --- /dev/null +++ b/test/data/ua/device/technisat.json @@ -0,0 +1,20 @@ +[ + { + "desc": "TechniSAT Digit ISIO S SAT receiver", + "ua": "Opera/9.80 (Linux sh4; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat Digit ISIO S; de) Presto/2.9.167 Version/11.50", + "expect": { + "vendor": "TechniSat", + "model": "Digit ISIO S", + "type": "smarttv" + } + }, + { + "desc": "TechniSAT MultyVision SmartTV", + "ua": "Opera/9.80 (Linux i686; U; HbbTV/1.1.1 (;;;;;); CE-HTML; TechniSat MultyVision ISIO; de) Presto/2.9.167 Version/11.50", + "expect": { + "vendor": "TechniSat", + "model": "MultyVision ISIO", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/tecno.json b/test/data/ua/device/tecno.json new file mode 100644 index 000000000..4413f9531 --- /dev/null +++ b/test/data/ua/device/tecno.json @@ -0,0 +1,47 @@ +[ + { + "desc": "Tecno DroiPad 8D", + "ua": "Mozilla/5.0 (Linux; U; Android 5.1; TECNO DP8D Build/LMY47D; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Safari/537.36 OPR/42.0.2254.139280", + "expect": { + "vendor": "TECNO", + "model": "DP8D", + "type": "tablet" + } + }, + { + "desc": "Tecno DroiPad 10A Pro", + "ua": "Mozilla/5.0 (Linux; Android 5.1; TECNO DP10A Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.74 Safari/537.36", + "expect": { + "vendor": "TECNO", + "model": "DP10A Pro", + "type": "tablet" + } + }, + { + "desc": "Tecno DroiPad 10D 4G", + "ua": "Mozilla/5.0 (Linux; Android 7.0; TECNO P904) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36 OPR/77.0.4054.90", + "expect": { + "vendor": "TECNO", + "model": "P904", + "type": "tablet" + } + }, + { + "desc": "Tecno KC8", + "ua": "Mozilla/5.0 (Linux; Android 10; TECNO KC8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TECNO", + "model": "KC8", + "type": "mobile" + } + }, + { + "desc": "Tecno Spark 8C", + "ua": "Mozilla/5.0 (Linux; Android 11; TECNO KG5n) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "TECNO", + "model": "KG5n", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/tesla.json b/test/data/ua/device/tesla.json new file mode 100644 index 000000000..34faa3552 --- /dev/null +++ b/test/data/ua/device/tesla.json @@ -0,0 +1,20 @@ +[ + { + "desc": "Tesla", + "ua": "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/601.1 (KHTML, like Gecko) Tesla QtCarBrowser Safari/601.1", + "expect": { + "vendor": "Tesla", + "model": "undefined", + "type": "embedded" + } + }, + { + "desc": "Tesla", + "ua": "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/79.0.3945.130 Chrome/79.0.3945.130 Safari/537.36 Tesla/2020.16.2.1-e99c70fff409", + "expect": { + "vendor": "Tesla", + "model": "undefined", + "type": "embedded" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/ulefone.json b/test/data/ua/device/ulefone.json new file mode 100644 index 000000000..c7aff3261 --- /dev/null +++ b/test/data/ua/device/ulefone.json @@ -0,0 +1,92 @@ +[ + { + "desc": "Ulefone Armor", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Armor Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.107 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 6.0; Armor) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 YaBrowser/20.4.2.101.00 SA/1 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor 8 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; Armor 8 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.192 Mobile Safari/537.36 OPR/74.1.3922.71199", + "expect": { + "vendor": "Ulefone", + "model": "Armor 8 Pro", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor 12 5G", + "ua": "Mozilla/5.0 (Linux; Android 11; Armor 12 5G Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.166 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor 12 5G", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor 20WT", + "ua": "Mozilla/5.0 (Linux; Android 12; Armor 20WT) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/22.0 Chrome/111.0.5563.116 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Armor 20WT", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor Pad", + "ua": "Mozilla/5.0 (Linux; Android 12; Armor Pad Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/431.0.0.30.108;]", + "expect": { + "vendor": "Ulefone", + "model": "Armor Pad", + "type": "mobile" + } + }, + { + "desc": "Ulefone Armor X5 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; Armor X5 Pro Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/430.0.0.23.113;]", + "expect": { + "vendor": "Ulefone", + "model": "Armor X5 Pro", + "type": "mobile" + } + }, + { + "desc": "Ulefone Power Armor 14 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor14 Pro Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/115.0.5790.138 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Power Armor14 Pro", + "type": "mobile" + } + }, + { + "desc": "Ulefone Power Armor 18T", + "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor 18T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Ulefone", + "model": "Power Armor 18T", + "type": "mobile" + } + }, + { + "desc": "Ulefone Power Armor 19T", + "ua": "Mozilla/5.0 (Linux; Android 12; Power Armor 19T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.192 Mobile Safari/537.36 OPR/74.3.3922.71982", + "expect": { + "vendor": "Ulefone", + "model": "Power Armor 19T", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/vivo.json b/test/data/ua/device/vivo.json new file mode 100644 index 000000000..cad62f9d5 --- /dev/null +++ b/test/data/ua/device/vivo.json @@ -0,0 +1,74 @@ +[ + { + "desc": "Vivo S1 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; vivo 1920) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Vivo", + "model": "1920", + "type": "mobile" + } + }, + { + "desc": "Vivo Y52s", + "ua": "Mozilla/5.0 (Linux; Android 10; V2057A Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/76.0.3809.89 Mobile Safari/537.36 T7/12.10 SP-engine/2.28.0 baiduboxapp/12.10.0.10 (Baidu; P1 10) NABar/1.0", + "expect": { + "vendor": "Vivo", + "model": "V2057A", + "type": "mobile" + } + }, + { + "desc": "Vivo X60", + "ua": "Mozilla/5.0 (Linux; Android 11; V2046A; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/8.8.71.0", + "expect": { + "vendor": "Vivo", + "model": "V2046A", + "type": "mobile" + } + }, + { + "desc": "Vivo Y79A", + "ua": "Mozilla/5.0 (Linux; Android 7.1.2; vivo Y79A Build/N2G47H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/9.0.14.0", + "expect": { + "vendor": "Vivo", + "model": "Y79A", + "type": "mobile" + } + }, + { + "desc": "Vivo Y93", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; vivo 1814) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Vivo", + "model": "1814", + "type": "mobile" + } + }, + { + "desc": "Vivo Y97", + "ua": "Mozilla/5.0 (Linux; Android 8.1.0; V1813T Build/O11019; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/9.0.14.0", + "expect": { + "vendor": "Vivo", + "model": "V1813T", + "type": "mobile" + } + }, + { + "desc": "Vivo iQOO Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; V1916A; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36 VivoBrowser/9.1.10.6", + "expect": { + "vendor": "Vivo", + "model": "V1916A", + "type": "mobile" + } + }, + { + "desc": "Vivo 1906 (Y11)", + "ua": "Mozilla/5.0 (Linux; Android 11; vivo 1906) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Vivo", + "model": "1906", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/vizio.json b/test/data/ua/device/vizio.json new file mode 100644 index 000000000..d36ae4d30 --- /dev/null +++ b/test/data/ua/device/vizio.json @@ -0,0 +1,20 @@ +[ + { + "desc": "VIZIO", + "ua": "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36 CrKey/1.0.999999 VIZIO SmartCast(Conjure/SX7B-4.6.419.12 FW/7.0.23.2-4 Model/M557-G0)", + "expect": { + "vendor": "VIZIO", + "model": "M557-G0", + "type": "smarttv" + } + }, + { + "desc": "VIZIO", + "ua": "VIZIO V506-J09 ViziOS/1.4.512.847.1 WatchFree/24.06.13.2 FancyPlayer/1.1.30-qa ", + "expect": { + "vendor": "VIZIO", + "model": "V506-J09", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/xiaomi.json b/test/data/ua/device/xiaomi.json new file mode 100644 index 000000000..86593efc0 --- /dev/null +++ b/test/data/ua/device/xiaomi.json @@ -0,0 +1,569 @@ +[ + { + "desc": "MIUI Xiaomi Mi MIX 3 5G", + "ua": "Dalvik/2.1.0 (Linux; U; Android 9; Mi MIX 3 5G MIUI/V10.3.2.0.PEMEUVF)", + "expect": { + "vendor": "Xiaomi", + "model": "Mi MIX 3 5G", + "type": "mobile" + } + }, + { + "desc": "MIUI POCOPHONE F1", + "ua": "Dalvik/2.1.0 (Linux; U; Android 9; POCOPHONE F1 MIUI/9.6.27)", + "expect": { + "vendor": "Xiaomi", + "model": "POCOPHONE F1", + "type": "mobile" + } + }, + { + "desc": "MIUI Xiaomi M2006C3MT", + "ua": "Dalvik/2.1.0 (Linux; U; Android 10; M2006C3MT MIUI/V12.0.7.0.QCRMIXM)", + "expect": { + "vendor": "Xiaomi", + "model": "M2006C3MT", + "type": "mobile" + } + }, + { + "desc": "Xiaomi 2201117TG", + "ua": "Mozilla/5.0 (Linux; Android 11; 2201117TG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.98 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "2201117TG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi M2004J19C", + "ua": "Mozilla/5.0 (Linux; Android 11; M2004J19C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/113.0.5672.77 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2004J19C", + "type": "mobile" + } + }, + { + "desc": "Xiaomi M2006C3MNG", + "ua": "Mozilla/5.0 (Linux; Android 11; M2006C3MNG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.85 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2006C3MNG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi 21061119DG", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; 21061119DG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 YaBrowser/23.3.7.24.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "21061119DG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi 2013023", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; en-US; 2013023 Build/HM2013023) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/10.0.1.512 U3/0.8.0 Mobile Safari/533.1", + "expect": { + "vendor": "Xiaomi", + "model": "2013023", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Hongmi Note 1W", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.2; zh-CN; HM NOTE 1W Build/JDQ39) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/9.7.9.439 U3/0.8.0 Mobile Safari/533.1", + "expect": { + "vendor": "Xiaomi", + "model": "HM NOTE 1W", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 3C", + "ua": "Mozilla/5.0 (Linux; U; Android 4.3; zh-CN; MI 3C Build/JLS36C) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 UCBrowser/9.7.9.439 U3/0.8.0 Mobile Safari/533.1", + "expect": { + "vendor": "Xiaomi", + "model": "MI 3C", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 5", + "ua": "Mozilla/5.0 (Linux; Android 7.0; MI 5 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.83 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI 5", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 6", + "ua": "Mozilla/5.0 (Linux; Android 7.1; MI 6 Build/NMF26X; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/59.0.3071.125 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI 6", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 10 Pro", + "ua": "Linux; U; Android 13; Mi 10 Pro Build/TKQ1.221114.001", + "expect": { + "vendor": "Xiaomi", + "model": "Mi 10 Pro", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 5s Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; zh-cn; MI 5s Plus Build/MXB48T) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/8.7.1", + "expect": { + "vendor": "Xiaomi", + "model": "MI 5s Plus", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi A1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; Mi A1 Build/OPR1.170623.026) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Mi A1", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi Note", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; MI NOTE LTE Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI NOTE LTE", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi One Plus", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; MI-ONE Plus Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Xiaomi", + "model": "MI-ONE Plus", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi Max 3", + "ua": "Mozilla/5.0 (Linux; Android 9; MI MAX 3 Build/PKQ1.181007.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI MAX 3", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi A1", + "ua": "Mozilla/5.0 (Linux; Android 9; Mi A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.101 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Mi A1", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi A2 Lite", + "ua": "Mozilla/5.0 (Linux; Android 9; Mi A2 Lite) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.62 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Mi A2 Lite", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi 9 SE", + "ua": "Mozilla/5.0 (Linux; Android 9; Mi 9 SE) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.136 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Mi 9 SE", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi A2", + "ua": "Mozilla/5.0 (Linux; Android 9; Mi A2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Mi A2", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Mi CC9", + "ua": "Mozilla/5.0 (Linux; U; Android 11; zh-cn; MI CC 9 Build/RKQ1.200826.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/89.0.4389.116 Mobile Safari/537.36 XiaoMi/MiuiBrowser/15.5.18", + "expect": { + "vendor": "Xiaomi", + "model": "MI CC 9", + "type": "mobile" + } + }, + { + "desc": "Xiaomi MI PAD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.4.4; en-us; MI PAD Build/KTU84P) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.146 Mobile Safari/537.36 XiaoMi/MiuiBrowser/9.3.2", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 2", + "ua": "Mozilla/5.0 (Linux; Android 5.1; MI PAD 2 Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Safari/537.36 [FB_IAB/FB4A;FBAV/137.0.0.24.91;]", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 2", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 2", + "ua": "Mozilla/5.0 (Linux; x86_64; Android 5.1; MI PAD 2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 YaBrowser/20.11.2.69.01 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 2", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 3", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 7.0; MI PAD 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.216 YaBrowser/21.5.6.56.01 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 3", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 4", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 8.1.0; MI PAD 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 YaBrowser/19.9.1.126.01 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 4", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 4 PLUS", + "ua": "Mozilla/5.0 (Linux; Android 8.1; MI PAD 4 PLUS) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MI PAD 4 PLUS", + "type": "tablet" + } + }, + { + "desc": "Xiaomi MI PAD 4 WiFi", + "ua": "Mozilla/5.0 (Linux; Android 8.1; Mi Pad4 Wi-Fi) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Mobile Safari/537.36 EdgA/86.0.622.61", + "expect": { + "vendor": "Xiaomi", + "model": "Mi Pad4 Wi-Fi", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 5", + "ua": "Mozilla/5.0 (Linux; Android 13; 21051182G Build/TKQ1.221013.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", + "expect": { + "vendor": "Xiaomi", + "model": "21051182G", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 5 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; M2105K81AC Build/RKQ1.200826.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Safari/537.36 Line/13.15.1/IAB", + "expect": { + "vendor": "Xiaomi", + "model": "M2105K81AC", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 5 Pro 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; M2105K81C) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/23.0 Chrome/115.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2105K81C", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 6 Max 14", + "ua": "Mozilla/5.0 (Linux; U; Android 14; zh-tw; 2307BRPDCC Build/UKQ1.230804.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/109.0.5414.118 Mobile Safari/537.36 Device/yudi Model/2307BRPDCC XiaoMi/MiuiBrowser/14.10.6", + "expect": { + "vendor": "Xiaomi", + "model": "2307BRPDCC", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Mi Pad 6 Pro", + "ua": "Mozilla/5.0 (Linux; U; Android 13; en-US; 23046RP50C Build/TKQ1.221114.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.6.2.1316 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "23046RP50C", + "type": "tablet" + } + }, + { + "desc": "Xiaomi Pad 6S Pro 12.4", + "ua": "Mozilla/5.0 (Linux; Android 14; 24018RPACC) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "24018RPACC", + "type": "tablet" + } + }, + { + "desc": "Xiaomi POCO X2", + "ua": "Mozilla/5.0 (Linux; Android 10; POCO X2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "POCO X2", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO X3 Pro", + "ua": "Mozilla/5.0 (Linux; Android 11; M2102J20SI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2102J20SI", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO X3 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; M2102J20SG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2102J20SG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO X3 NFC", + "ua": "Mozilla/5.0 (Linux; Android 12; M2007J20CG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2007J20CG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO M2 Pro", + "ua": "Mozilla/5.0 (Linux; arm_64; Android 11; POCO M2 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 YaBrowser/22.11.7.42.00 SA/3 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "POCO M2 Pro", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCO M3", + "ua": "Mozilla/5.0 (Linux; Android 10; M2010J19CI) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2010J19CI", + "type": "mobile" + } + }, + { + "desc": "Xiaomi POCOPHONE F1", + "ua": "Mozilla/5.0 (Linux; Android 10; POCOPHONE F1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "POCOPHONE F1", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi 4A", + "ua": "Mozilla/5.0 (Linux; Android 6.0; Redmi 4A Build/MMB29M; xx-xx) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi 4A", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi 10C", + "ua": "Mozilla/5.0 (Linux; Android 12; 220333QAG) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "220333QAG", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi K30 5G", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi K30 5G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.96 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi K30 5G", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi K30 Pro", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi K30 Pro) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi K30 Pro", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi Note 3", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; Redmi Note 3 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.116 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Note 3", + "type": "mobile" + } + }, + { + "desc": "Xiaomi Redmi Note 9 Pro Max", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 9 Pro Max) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.99 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Note 9 Pro Max", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 9S", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 9S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Note 9S", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 10 5G", + "ua": "Mozilla/5.0 (Linux; Android 12; M2103K19C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.88 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2103K19C", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 10 Pro", + "ua": "Mozilla/5.0 (Linux; Android 13; M2101K6P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2101K6P", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 10 Pro", + "ua": "Mozilla/5.0 (Linux; Android 12; M2101K6G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "M2101K6G", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 8", + "ua": "Mozilla/5.0 (Linux; Android 10; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Note 8", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Note 12 Turbo", + "ua": "Mozilla/5.0 (Linux; Android 13; 23049RAD8C; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36 VivoBrowser/16.7.1.1", + "expect": { + "vendor": "Xiaomi", + "model": "23049RAD8C", + "type": "mobile" + } + }, + { + "desc": "XiaoMi Redmi Pad", + "ua": "Mozilla/5.0 (Linux; U; Android 12; id-id; Redmi Pad Build/SP1A.210812.016) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/112.0.5615.136 Mobile Safari/537.36 XiaoMi/MiuiBrowser/14.1.1-gn", + "expect": { + "vendor": "Xiaomi", + "model": "Redmi Pad", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad", + "ua": "Mozilla/5.0 (Linux; Android 14; 22081283G Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Safari/537.36 Flipboard/4.3.31/5486,4.3.31.5486", + "expect": { + "vendor": "Xiaomi", + "model": "22081283G", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad Pro", + "ua": "Mozilla/5.0 (Linux; Android 14; 2405CRPFDG Build/UKQ1.240116.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/127.0.6533.97 Safari/537.36 [FB_IAB/FB4A;FBAV/476.0.0.49.74;] FBNV/1", + "expect": { + "vendor": "Xiaomi", + "model": "2405CRPFDG", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad SE", + "ua": "Dalvik/2.1.0 (Linux; U; Android 14; 23073RPBFG Build/UKQ1.231003.002)", + "expect": { + "vendor": "Xiaomi", + "model": "23073RPBFG", + "type": "tablet" + } + }, + { + "desc": "XiaoMi Redmi Pad SE 8.7", + "ua": "Mozilla/5.0 (Linux; Android 14; 24076RP19G Build/UP1A.231005.007; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.107 Mobile Safari/537.36 Line/14.18.1/IAB", + "expect": { + "vendor": "Xiaomi", + "model": "24076RP19G", + "type": "tablet" + } + }, + { + "desc": "Xiaomi TV", + "ua": "Mozilla/5.0 (Linux; Android 10; MiTV-MOOQ0 Build/QTG3.200305.006; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/94.0.4606.61 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MiTV-MOOQ0", + "type": "smarttv" + } + }, + { + "desc": "Xiaomi Mi TV", + "ua": "Mozilla/5.0 (Linux; Android 9; MiTV4I Build/PI; en-in) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36 Puffin/7.8.3.40913AP", + "expect": { + "vendor": "Xiaomi", + "model": "MiTV4I", + "type": "smarttv" + } + }, + { + "desc": "Xiaomi Mi Box", + "ua": "Mozilla/5.0 (Linux; Android 9; MIBOX3 Build/PI; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/119.0.6045.193 Mobile Safari/537.36", + "expect": { + "vendor": "Xiaomi", + "model": "MIBOX3", + "type": "smarttv" + } + } +] \ No newline at end of file diff --git a/test/data/ua/device/zte.json b/test/data/ua/device/zte.json new file mode 100644 index 000000000..5b43beae4 --- /dev/null +++ b/test/data/ua/device/zte.json @@ -0,0 +1,11 @@ +[ + { + "desc": "ZTE Blade A6", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ZTE BLADE A0620 Build/NMF26F; ru-ru) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.136 Mobile Safari/537.36 Puffin/9.2.0.50586AP", + "expect": { + "vendor": "ZTE", + "model": "BLADE A0620", + "type": "mobile" + } + } +] \ No newline at end of file diff --git a/test/engine-test.json b/test/data/ua/engine/engine-all.json similarity index 82% rename from test/engine-test.json rename to test/data/ua/engine/engine-all.json index 6726d27d9..c68603112 100644 --- a/test/engine-test.json +++ b/test/data/ua/engine/engine-all.json @@ -1,4 +1,13 @@ [ + { + "desc" : "ArkWeb", + "ua" : "Mozilla/5.0 (Phone; OpenHarmony 4.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile", + "expect" : + { + "name" : "ArkWeb", + "version" : "4.1.6.1" + } + }, { "desc" : "Blink", "ua" : "Mozilla/5.0 (Linux; Android 7.0; SM-G920I Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/3.4.9 SamsungBrowser/4.0 Chrome/57.0.2987.146 Mobile VR Safari/537.36", @@ -53,6 +62,24 @@ "version" : "4.5.4" } }, + { + "desc" : "LibWeb", + "ua" : "Mozilla/5.0 (Linux; x86_64) Ladybird/1.0", + "expect" : + { + "name" : "LibWeb", + "version" : "undefined" + } + }, + { + "desc" : "LibWeb", + "ua" : "Mozilla/4.0 (SerenityOS; x86) LibWeb+LibJS (Not KHTML, nor Gecko) LibWeb", + "expect" : + { + "name" : "LibWeb", + "version" : "undefined" + } + }, { "desc" : "NetFront", "ua" : "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", @@ -71,6 +98,15 @@ "version" : "2.8.149" } }, + { + "desc" : "Servo", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Servo/1.0 Firefox/111.0", + "expect" : + { + "name" : "Servo", + "version" : "1.0" + } + }, { "desc" : "Tasman", "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; PPC Mac OS X 10.4.7; Tasman 1.0)", diff --git a/test/data/ua/extension/cli.json b/test/data/ua/extension/cli.json new file mode 100644 index 000000000..ce89135d9 --- /dev/null +++ b/test/data/ua/extension/cli.json @@ -0,0 +1,72 @@ +[ + { + "desc" : "curl", + "ua" : "curl/7.38.0", + "expect" : + { + "name" : "curl", + "version" : "7.38.0", + "type" : "cli" + } + }, + { + "desc" : "ELinks", + "ua" : "ELinks/0.11.4-3-lite (textmode; Debian; Linux 2.6.26-1-686 i686;", + "expect" : + { + "name" : "ELinks", + "version" : "0.11.4-3-lite", + "type" : "cli" + } + }, + { + "desc" : "ELinks", + "ua" : "ELinks (0.11.3; Linux 2.6.23-hardened-r4 i686; 166x55)", + "expect" : + { + "name" : "ELinks", + "version" : "0.11.3", + "type" : "cli" + } + }, + { + "desc" : "HTTPie", + "ua" : "HTTPie/0.9.9", + "expect" : + { + "name" : "HTTPie", + "version" : "0.9.9", + "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" + } + } +] diff --git a/test/data/ua/extension/crawler.json b/test/data/ua/extension/crawler.json new file mode 100644 index 000000000..f754b6650 --- /dev/null +++ b/test/data/ua/extension/crawler.json @@ -0,0 +1,1063 @@ +[ + { + "desc" : "360Spider", + "ua" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0); 360Spider", + "expect" : + { + "name" : "360Spider", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "AdIdxBot", + "ua" : "Mozilla/5.0 (compatible; adidxbot/2.0; +http://www.bing.com/bingbot.htm)", + "expect" : + { + "name" : "adidxbot", + "version" : "2.0", + "type" : "crawler" + } + }, + { + "desc" : "AdsBot Mobile Web", + "ua" : "AdsBot-Google (+http://www.google.com/adsbot.html)", + "expect" : + { + "name" : "AdsBot-Google", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "AdsBot Mobile Web", + "ua" : "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; AdsBot-Google-Mobile; +http://www.google.com/mobile/adsbot.html)", + "expect" : + { + "name" : "AdsBot-Google-Mobile", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "AdSense", + "ua" : "Mediapartners-Google", + "expect" : + { + "name" : "Mediapartners-Google", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "AhrefsBot", + "ua" : "Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)", + "expect" : + { + "name" : "AhrefsBot", + "version" : "7.0", + "type" : "crawler" + } + }, + { + "desc" : "AI2Bot", + "ua" : "Mozilla/5.0 (compatible) AI2Bot (+https://www.allenai.org/crawler)", + "expect" : + { + "name" : "AI2Bot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "aiHitBot", + "ua" : "Mozilla/5.0 (compatible; aiHitBot/2.9; +https://www.aihitdata.com/about)", + "expect" : + { + "name" : "aiHitBot", + "version" : "2.9", + "type" : "crawler" + } + }, + { + "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" : "Applebot-Extended", + "ua" : "Applebot-Extended", + "expect" : + { + "name" : "Applebot-Extended", + "version" : "undefined", + "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" : "Anthropic AI", + "ua" : "anthropic-ai", + "expect" : + { + "name" : "anthropic-ai", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Archive.org Bot", + "ua" : "ia_archiver/8.1 (Windows 2000 1.9; en-US;)", + "expect" : + { + "name" : "ia_archiver", + "version" : "8.1", + "type" : "crawler" + } + }, + { + "desc" : "Archive.org Bot", + "ua" : "Mozilla/5.0 (compatible; archive.org_bot/3.3.0 +https://archive.org/details/archive.org_bot)", + "expect" : + { + "name" : "archive.org_bot", + "version" : "3.3.0", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider", + "ua" : "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)", + "expect" : + { + "name" : "Baiduspider", + "version" : "2.0", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-ads", + "ua" : "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:17.0; Baiduspider-ads) Gecko/17.0 Firefox/17.0", + "expect" : + { + "name" : "Baiduspider-ads", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-cpro", + "ua" : "Mozilla/5.0 (compatible; Baiduspider-cpro; +http://www.baidu.com/search/spider.html)", + "expect" : + { + "name" : "Baiduspider-cpro", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-favo", + "ua" : "Baiduspider-favo", + "expect" : + { + "name" : "Baiduspider-favo", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-image", + "ua" : "Baiduspider-image+(+http://www.baidu.com/search/spider.htm)", + "expect" : + { + "name" : "Baiduspider-image", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-news", + "ua" : "Baiduspider-news", + "expect" : + { + "name" : "Baiduspider-news", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-render", + "ua" : "Mozilla/5.0 (compatible; Baiduspider-render/2.0; +http://www.baidu.com/search/spider.html)", + "expect" : + { + "name" : "Baiduspider-render", + "version" : "2.0", + "type" : "crawler" + } + }, + { + "desc" : "Baiduspider-video", + "ua" : "Baiduspider-video", + "expect" : + { + "name" : "Baiduspider-video", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Bingbot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm) Chrome/", + "expect" : + { + "name" : "bingbot", + "version" : "2.0", + "type" : "crawler" + } + }, + { + "desc" : "Bravebot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Bravebot/1.0; +https://search.brave.com/help/brave-search-crawler) Chrome/W.X.Y.Z Safari/537.36", + "expect" : + { + "name" : "Bravebot", + "version" : "1.0", + "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" : "CCBot", + "ua" : "CCBot/1.0 (+https://commoncrawl.org/bot.html)", + "expect" : + { + "name" : "CCBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "ChatGLM-Spider", + "ua" : "Mozilla/5.0 (compatible; ChatGLM-Spider/1.0; +https://chatglm.cn/)", + "expect" : + { + "name" : "ChatGLM-Spider", + "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" : "Coc Coc Bot (web)", + "ua" : "Mozilla/5.0 (compatible; coccocbot-web/1.0; +http://help.coccoc.com/searchengine)", + "expect" : + { + "name" : "coccocbot-web", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Coc Coc Bot (image)", + "ua" : "Mozilla/5.0 (compatible; coccocbot-image/1.0; +http://help.coccoc.com/searchengine)", + "expect" : + { + "name" : "coccocbot-image", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "cohere-training-data-crawler", + "ua" : "cohere-training-data-crawler (+crawler@cohere.ai)", + "expect" : + { + "name" : "cohere-training-data-crawler", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Cotoyogi", + "ua" : "Mozilla/5.0 (compatible; Cotoyogi/4.0; +https://ds.rois.ac.jp/center8/crawler/)", + "expect" : + { + "name" : "Cotoyogi", + "version" : "4.0", + "type" : "crawler" + } + }, + { + "desc" : "Coveobot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) (compatible; Coveobot/2.0;+http://www.coveo.com/bot.html)", + "expect" : + { + "name" : "Coveobot", + "version" : "2.0", + "type" : "crawler" + } + }, + { + "desc" : "CriteoBot", + "ua" : "CriteoBot/0.1 (+https://www.criteo.com/criteo-crawler/)", + "expect" : + { + "name" : "CriteoBot", + "version" : "0.1", + "type" : "crawler" + } + }, + { + "desc" : "DataForSEO", + "ua" : "Mozilla/5.0 (compatible; DataForSeoBot; +https://dataforseo.com/dataforseo-bot)", + "expect" : + { + "name" : "DataForSeoBot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Daum", + "ua" : "Mozilla/5.0 (compatible; MSIE or Firefox mutant;) Daum 4.1", + "expect" : + { + "name" : "Daum", + "version" : "4.1", + "type" : "crawler" + } + }, + { + "desc" : "Daumoa", + "ua" : "Mozilla/5.0 (compatible; MSIE or Firefox mutant; not on Windows server;) Daumoa 4.0", + "expect" : + { + "name" : "Daumoa", + "version" : "4.0", + "type" : "crawler" + } + }, + { + "desc" : "Daumoa-image", + "ua" : "Mozilla/5.0 (compatible; MSIE or Firefox mutant; not on Windows server;) Daumoa-image/1.0", + "expect" : + { + "name" : "Daumoa-image", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "DeepSeekBot", + "ua" : "DeepSeekBot", + "expect" : + { + "name" : "DeepSeekBot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Diffbot", + "ua" : "Diffbot/0.1", + "expect" : + { + "name" : "Diffbot", + "version" : "0.1", + "type" : "crawler" + } + }, + { + "desc" : "Dotbot", + "ua" : "Mozilla/5.0 (compatible; DotBot/1.2; +https://opensiteexplorer.org/dotbot; help@moz.com)", + "expect" : + { + "name" : "DotBot", + "version" : "1.2", + "type" : "crawler" + } + }, + { + "desc" : "DuckDuckBot", + "ua" : "DuckDuckBot/1.1; ( http://duckduckgo.com/duckduckbot.html)", + "expect" : + { + "name" : "DuckDuckBot", + "version" : "1.1", + "type" : "crawler" + } + }, + { + "desc" : "DuckDuckGo-Favicons-Bot", + "ua" : "DuckDuckGo-Favicons-Bot/1.0", + "expect" : + { + "name" : "DuckDuckGo-Favicons-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Elastic", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/124.0.6367.29 Safari/537.36 Elastic/Synthetics", + "expect" : + { + "name" : "Elastic", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Exabot", + "ua" : "Mozilla/5.0 (compatible; Exabot/3.0; +http://www.exabot.com/go/robot)", + "expect" : + { + "name" : "Exabot", + "version" : "3.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" : "FacebookExternalHit", + "ua" : "facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)", + "expect" : + { + "name" : "facebookexternalhit", + "version" : "1.1", + "type" : "crawler" + } + }, + { + "desc" : "FacebookExternalHit", + "ua" : "facebookcatalog/1.0", + "expect" : + { + "name" : "facebookcatalog", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "FirecrawlAgent", + "ua" : "Mozilla/5.0 (compatible; FirecrawlAgent/1.0)", + "expect" : + { + "name" : "FirecrawlAgent", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Googlebot-Video", + "ua" : "Googlebot-Video/1.0", + "expect" : + { + "name" : "Googlebot-Video", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Googlebot", + "ua" : "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)", + "expect" : + { + "name" : "Googlebot", + "version" : "2.1", + "type" : "crawler" + } + }, + { + "desc" : "Googlebot Image", + "ua" : "Googlebot-Image/1.0", + "expect" : + { + "name" : "Googlebot-Image", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Googlebot Video", + "ua" : "Googlebot-Video/1.0", + "expect" : + { + "name" : "Googlebot-Video", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Googlebot News", + "ua" : "Googlebot-News/1.0", + "expect" : + { + "name" : "Googlebot-News", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Google Storebot", + "ua" : "Storebot-Google/1.0", + "expect" : + { + "name" : "Storebot-Google", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Google InspectionTool", + "ua" : "Mozilla/5.0 (compatible; Google-InspectionTool/1.0;)", + "expect" : + { + "name" : "Google-InspectionTool", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "GoogleOther", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GoogleOther) Chrome/41.0.2272.96 Safari/537.36", + "expect" : + { + "name" : "GoogleOther", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "GoogleOther-Image", + "ua" : "GoogleOther-Image/1.0", + "expect" : + { + "name" : "GoogleOther-Image", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "GoogleOther-Video", + "ua" : "GoogleOther-Video/1.0", + "expect" : + { + "name" : "GoogleOther-Video", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Google-Safety", + "ua" : "Google-Safety", + "expect" : + { + "name" : "Google-Safety", + "version" : "undefined", + "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" : "HuggingFace-Bot", + "ua" : "Mozilla/5.0 (compatible; HuggingFace-Bot/1.0; +https://huggingface.co/)", + "expect" : + { + "name" : "HuggingFace-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "iAskBot", + "ua" : "Mozilla/5.0 AppleWebKit/605.1.15 (KHTML, like Gecko; compatible; iAskBot/1.0; +https://iask.ai/) Chrome/120.0.6099.119 Safari/605.1.15", + "expect" : + { + "name" : "iAskBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "ImagesiftBot", + "ua" : "Mozilla/5.0 (compatible; ImagesiftBot; +imagesift.com)", + "expect" : + { + "name" : "ImagesiftBot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Kangaroo Bot", + "ua" : "Mozilla/5.0 (compatible; Kangaroo Bot/1.0)", + "expect" : + { + "name" : "Kangaroo Bot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Linespider", + "ua" : "Mozilla/5.0 (compatible; Linespider/1.1; +https://lin.ee/4dwXkTH)", + "expect" : + { + "name" : "Linespider", + "version" : "1.1", + "type" : "crawler" + } + }, + { + "desc" : "LinkedInBot", + "ua" : "LinkedInBot/1.0 (compatible; Mozilla/5.0; Apache-HttpClient +http://www.linkedin.com)", + "expect" : + { + "name" : "LinkedInBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "magpie-crawler", + "ua" : "magpie-crawler/1.1 (robots-txt-checker; +http://www.brandwatch.net)", + "expect" : + { + "name" : "magpie-crawler", + "version" : "1.1", + "type" : "crawler" + } + }, + { + "desc" : "Meta-ExternalAgent", + "ua" : "meta-externalagent/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)", + "expect" : + { + "name" : "meta-externalagent", + "version" : "1.1", + "type" : "crawler" + } + }, + { + "desc" : "MJ12bot", + "ua" : "Mozilla/5.0 (compatible; MJ12bot/v1.4.8; http://mj12bot.com/)", + "expect" : + { + "name" : "MJ12bot", + "version" : "v1.4.8", + "major" : "1", + "type" : "crawler" + } + }, + { + "desc" : "MojeekBot", + "ua" : "Mozilla/5.0 (compatible; MojeekBot/0.11; +https://www.mojeek.com/bot.html)", + "expect" : + { + "name" : "MojeekBot", + "version" : "0.11", + "type" : "crawler" + } + }, + { + "desc" : "Omgili", + "ua" : "omgili/0.5 +https://omgili.com", + "expect" : + { + "name" : "omgili", + "version" : "0.5", + "type" : "crawler" + } + }, + { + "desc" : "Omgilibot", + "ua" : "omgilibot/0.3 +http://www.omgili.com/Crawler.html", + "expect" : + { + "name" : "omgilibot", + "version" : "0.3", + "type" : "crawler" + } + }, + { + "desc" : "Onespot", + "ua" : "Mozilla/5.0 (compatible; Onespot-ScraperBot/1.0; +https://www.onespot.com/identifying-traffic.html)", + "expect" : + { + "name" : "Onespot-ScraperBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "OpenAI Search", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot", + "expect" : + { + "name" : "OAI-SearchBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "PanguBot", + "ua" : "Mozilla/5.0 (compatible; PanguBot/1.0)", + "expect" : + { + "name" : "PanguBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "PerplexityBot", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; PerplexityBot/1.0; +https://perplexity.ai/perplexitybot)", + "expect" : + { + "name" : "PerplexityBot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "PetalBot", + "ua" : "Mozilla/5.0 (compatible;PetalBot; +https://webmaster.petalsearch.com/site/petalbot) ", + "expect" : + { + "name" : "PetalBot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "PetalBot", + "ua" : "Mozilla/5.0 (Linux; Android 7.0;) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; PetalBot;+https://webmaster.petalsearch.com/site/petalbot)", + "expect" : + { + "name" : "PetalBot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Qwantbot", + "ua" : "Mozilla/5.0 (compatible; Qwantbot/1.0_12345; +https://help.qwant.com/bot/)", + "expect" : + { + "name" : "Qwantbot", + "version" : "1.0_12345", + "type" : "crawler" + } + }, + { + "desc" : "Qwantbot", + "ua" : "Mozilla/5.0 (compatible; Qwantbot-prod51071/1.0; +Qwantbot@qwant.com)", + "expect" : + { + "name" : "Qwantbot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Qwantbot", + "ua" : "Mozilla/5.0 (compatible; Qwantbot-news/2.0; +https://help.qwant.com/bot/)", + "expect" : + { + "name" : "Qwantbot", + "version" : "2.0", + "type" : "crawler" + } + }, + { + "desc" : "Replicate-Bot", + "ua" : "Mozilla/5.0 (compatible; Replicate-Bot/1.0; +https://replicate.com/)", + "expect" : + { + "name" : "Replicate-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "RunPod-Bot", + "ua" : "Mozilla/5.0 (compatible; RunPod-Bot/1.0; +https://runpod.io/)", + "expect" : + { + "name" : "RunPod-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "SemrushBot", + "ua" : "Mozilla/5.0 (compatible; SemrushBot/7~bl; +http://www.semrush.com/bot.html)", + "expect" : + { + "name" : "SemrushBot", + "version" : "7", + "type" : "crawler" + } + }, + { + "desc" : "SemrushBot for Backlink Audit tool", + "ua" : "Mozilla/5.0 (compatible; SemrushBot-BA; +http://www.semrush.com/bot.html)", + "expect" : + { + "name" : "SemrushBot-BA", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "SemrushBot for On Page SEO Checker tool", + "ua" : "Mozilla/5.0 (compatible; SemrushBot-SI/0.97; +http://www.semrush.com/bot.html)", + "expect" : + { + "name" : "SemrushBot-SI", + "version" : "0.97", + "type" : "crawler" + } + }, + { + "desc" : "SemrushBot for ContentShake AI tool", + "ua" : "Mozilla/5.0 (compatible; SemrushBot-OCOB/1; +https://www.semrush.com/bot/)", + "expect" : + { + "name" : "SemrushBot-OCOB", + "version" : "1", + "type" : "crawler" + } + }, + { + "desc" : "SeznamBot", + "ua" : "Mozilla/5.0 (compatible; SeznamBot/4.0-RC1; +http://napoveda.seznam.cz/seznambot-intro/)", + "expect" : + { + "name" : "SeznamBot", + "version" : "4.0-RC1", + "type" : "crawler" + } + }, + { + "desc" : "Sogou", + "ua" : "Sogou web spider/4.0(+http://www.sogou.com/docs/help/webmasters.htm#07)", + "expect" : + { + "name" : "Sogou web spider", + "version" : "4.0", + "type" : "crawler" + } + }, + { + "desc" : "Startpage", + "ua" : "StartpagePrivateImageProxy/3.0 (https://www.startpage.com/; support@startpage.com) aiohttp.client/3.11.11", + "expect" : + { + "name" : "StartpagePrivateImageProxy", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "Teoma", + "ua" : "Mozilla/2.0 (compatible; Ask Jeeves/Teoma; +http://sp.ask.com/docs/about/tech_crawling.html)", + "expect" : + { + "name" : "Teoma", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Timpibot", + "ua" : "Timpibot/0.8 (+http://www.timpi.io)", + "expect" : + { + "name" : "Timpibot", + "version" : "0.8", + "type" : "crawler" + } + }, + { + "desc" : "Together-Bot", + "ua" : "Mozilla/5.0 (compatible; Together-Bot/1.0; +https://together.ai/)", + "expect" : + { + "name" : "Together-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "TurnitinBot", + "ua" : "TurnitinBot (https://turnitin.com/robot/crawlerinfo.html)", + "expect" : + { + "name" : "TurnitinBot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "xAI-Bot", + "ua" : "Mozilla/5.0 (compatible; xAI-Bot/1.0; +https://x.ai/)", + "expect" : + { + "name" : "xAI-Bot", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "VelenPublicWebCrawler", + "ua" : "Mozilla/5.0 (compatible; VelenPublicWebCrawler/1.0; +https://velen.io)", + "expect" : + { + "name" : "VelenPublicWebCrawler", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "v0bot", + "ua" : "v0bot", + "expect" : + { + "name" : "v0bot", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "Yahoo! Japan", + "ua" : "Y!J-BRW/1.0 (https://www.yahoo-help.jp/app/answers/detail/p/595/a_id/42716)", + "expect" : + { + "name" : "Y!J-BRW", + "version" : "1.0", + "type" : "crawler" + } + }, + { + "desc" : "Yahoo! Slurp", + "ua" : "Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp) ", + "expect" : + { + "name" : "Yahoo! Slurp", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "YandexBot", + "ua" : "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)", + "expect" : + { + "name" : "YandexBot", + "version" : "3.0", + "type" : "crawler" + } + }, + { + "desc" : "Yeti", + "ua" : "Mozilla/5.0 (compatible; Yeti/1.1; +http://naver.me/spd)", + "expect" : + { + "name" : "Yeti", + "version" : "1.1", + "type" : "crawler" + } + }, + { + "desc" : "YisouSpider", + "ua" : "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 YisouSpider/5.0 Safari/537.36", + "expect" : + { + "name" : "YisouSpider", + "version" : "5.0", + "type" : "crawler" + } + }, + { + "desc" : "YisouSpider", + "ua" : "YisouSpider", + "expect" : + { + "name" : "YisouSpider", + "version" : "undefined", + "type" : "crawler" + } + }, + { + "desc" : "YouBot", + "ua" : "YouBot (+http://www.you.com)", + "expect" : + { + "name" : "YouBot", + "version" : "undefined", + "type" : "crawler" + } + } +] diff --git a/test/data/ua/extension/email.json b/test/data/ua/extension/email.json new file mode 100644 index 000000000..8acd5d4e7 --- /dev/null +++ b/test/data/ua/extension/email.json @@ -0,0 +1,232 @@ +[ + { + "desc" : "Airmail", + "ua" : "Airmail 1.0 rv:148 (Macintosh; Mac OS X 10.8.3; en_BE)", + "expect" : + { + "name" : "Airmail", + "version" : "1.0", + "type" : "email" + } + }, + { + "desc" : "Apple Mail", + "ua" : "Mail/3826.500.181.1.5 CFNetwork/3826.500.111.1.1 Darwin/24.4.0", + "expect" : + { + "name" : "Mail", + "version" : "3826.500.181.1.5", + "type" : "email" + } + }, + { + "desc" : "BlueMail", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) BlueMail/0.10.31 Chrome/61.0.3163.100 Electron/2.0.18 Safari/537.36", + "expect" : + { + "name" : "BlueMail", + "version" : "0.10.31", + "type" : "email" + } + }, + { + "desc" : "BlueMail", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16A405 BlueMail iOS", + "expect" : + { + "name" : "BlueMail", + "version" : "iOS", + "type" : "email" + } + }, + { + "desc" : "DaumMail", + "ua" : "DaumMobileApp (LG-KU5400; U; Android 2.3.7|10; ko-kr) DaumMail/1.0.8 ", + "expect" : + { + "name" : "DaumMail", + "version" : "1.0.8", + "type" : "email" + } + }, + { + "desc" : "Evolution", + "ua" : "Evolution/3.52.3", + "expect" : + { + "name" : "Evolution", + "version" : "3.52.3", + "type" : "email" + } + }, + { + "desc" : "eM Client", + "ua" : "eMClient/9.2.2157.0", + "expect" : + { + "name" : "eMClient", + "version" : "9.2.2157.0", + "type" : "email" + } + }, + { + "desc" : "Foxmail", + "ua" : "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36 foxmail/7.2.15.80", + "expect" : + { + "name" : "foxmail", + "version" : "7.2.15.80", + "type" : "email" + } + }, + { + "desc" : "KMail", + "ua" : "KMail/4.14.10 (FreeBSD/12.0-CURRENT; KDE/4.14.10; amd64; ; )", + "expect" : + { + "name" : "KMail", + "version" : "4.14.10", + "type" : "email" + } + }, + { + "desc" : "KMail2", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) kmail2/5.7.3 Safari/534.34", + "expect" : + { + "name" : "kmail2", + "version" : "5.7.3", + "type" : "email" + } + }, + { + "desc" : "Kontact", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) kontact/4.13.3 Safari/534.34", + "expect" : + { + "name" : "kontact", + "version" : "4.13.3", + "type" : "email" + } + }, + { + "desc" : "Microsoft Outlook", + "ua" : "Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0.5431; Pro)", + "expect" : + { + "name" : "Microsoft Outlook", + "version" : "16.0.5431", + "type" : "email" + } + }, + { + "desc" : "Microsoft Outlook for Mac", + "ua" : "MacOutlook/14.7.1.161129 (Intel Mac OS X 10.9.6)", + "expect" : + { + "name" : "MacOutlook", + "version" : "14.7.1.161129", + "type" : "email" + } + }, + { + "desc" : "NaverMailApp", + "ua" : "NaverMailApp/2.1.23 (Android 10; SM-N960N)", + "expect" : + { + "name" : "NaverMailApp", + "version" : "2.1.23", + "type" : "email" + } + }, + { + "desc" : "Polymail", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Polymail/2.3.12 Chrome/114.0.5735.134 Electron/25.2.0 Safari/537.36", + "expect" : + { + "name" : "Polymail", + "version" : "2.3.12", + "type" : "email" + } + }, + { + "desc" : "ProtonMail", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) ProtonMail/1.4.0 Chrome/122.0.6261.156 Electron/29.4.6 Safari/537.36", + "expect" : + { + "name" : "ProtonMail", + "version" : "1.4.0", + "type" : "email" + } + }, + { + "desc" : "SparkDesktop", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) SparkDesktop/3.0.30 Chrome/102.0.5005.63 Electron/19.0.4 Safari/537.36", + "expect" : + { + "name" : "SparkDesktop", + "version" : "3.0.30", + "type" : "email" + } + }, + { + "desc" : "Sparrow", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5.6 (KHTML, like Gecko) Sparrow/1043.1", + "expect" : + { + "name" : "Sparrow", + "version" : "1043.1", + "type" : "email" + } + }, + { + "desc" : "Thunderbird", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0", + "expect" : + { + "name" : "Thunderbird", + "version" : "78.13.0", + "type" : "email" + } + }, + { + "desc" : "Yahoo! Mail", + "ua" : "YahooMobile/1.0 (mail; 3.0.5.1311380); (Linux; U; Android 4.0.3; htc_runnymede Build/ICE_CREAM_SANDWICH_MR1);", + "expect" : + { + "name" : "Yahoo", + "version" : "3.0.5.1311380", + "type" : "email" + } + }, + { + "desc" : "Yahoo! Mail", + "ua" : "YahooMobileMail/1.0 (Android Mail; 1.3.10) (supersonic;HTC;PC36100;2.3.5/GRJ90) ", + "expect" : + { + "name" : "Yahoo", + "version" : "1.3.10", + "type" : "email" + } + }, + { + "desc" : "Zimbra", + "ua" : "Mozilla/5.0 (Windows; U; Windows NT 6.2; it; rv:1.9.2.19pre) Gecko/20110902 Prism zdesktop/7.2.8", + "expect" : + { + "name" : "Zimbra", + "version" : "7.2.8", + "type" : "email" + } + }, + { + "desc" : "ZohoMail", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ZohoMail-Desktop/1.6.3 Chrome/124.0.6367.207 Electron/30.0.6 Safari/537.36", + "expect" : + { + "name" : "ZohoMail-Desktop", + "version" : "1.6.3", + "type" : "email" + } + } +] diff --git a/test/data/ua/extension/extra-devices.json b/test/data/ua/extension/extra-devices.json new file mode 100644 index 000000000..174c4adbe --- /dev/null +++ b/test/data/ua/extension/extra-devices.json @@ -0,0 +1,342 @@ +[{ + "desc": "Essential PH-1", + "ua": "Mozilla/5.0 (Linux; Android 9; PH-1 Build/PPR1.180905.036) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.86 Mobile Safari/537.36", + "expect": { + "vendor": "Essential", + "model": "PH-1", + "type": "mobile" + } +}, +{ + "desc": "Gigaset Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Gigaset QV830 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Gigaset", + "model": "QV830", + "type": "tablet" + } +}, +{ + "desc": "RCA Voyager III Tablet", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; RCT6973W43 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "RCA", + "model": "RCT6973W43", + "type": "tablet" + } +}, +{ + "desc": "RCA Voyager II Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.0; RCT6773W22B Build/LRX21M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "RCA", + "model": "RCT6773W22B", + "type": "tablet" + } +}, +{ + "desc": "Verizon Quanta Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; QMV7B Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Verizon", + "model": "QMV7B", + "type": "tablet" + } +}, +{ + "desc": "Verizon Ellipsis 8 Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; QTAQZ3 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Verizon", + "model": "QTAQZ3", + "type": "tablet" + } +}, +{ + "desc": "Verizon Ellipsis 8HD Tablet", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; QTASUN1 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.81 Safari/537.36", + "expect": { + "vendor": "Verizon", + "model": "QTASUN1", + "type": "tablet" + } +}, +{ + "desc": "Dell Venue 8 Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Venue 8 3830 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Dell", + "model": "Venue 8 3830", + "type": "tablet" + } +}, +{ + "desc": "Dell Venue 7 Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Venue 7 3730 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Dell", + "model": "Venue 7 3730", + "type": "tablet" + } +}, +{ + "desc": "Barnes & Noble Nook HD+ Tablet", + "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; en-us; Barnes & Noble Nook HD+ Build/JZO54K; CyanogenMod-10) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Barnes & Noble", + "model": "Nook HD+", + "type": "tablet" + } +}, +{ + "desc": "Barnes & Noble V400 Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.0.4; BNTV400 Build/IMM76L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.111 Safari/537.36", + "expect": { + "vendor": "Barnes & Noble", + "model": "V400", + "type": "tablet" + } +}, +{ + "desc": "NuVision TM101A540N Tablet", + "ua": "Mozilla/5.0 (Linux; Android 5.1; TM101A540N Build/LMY47I; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/50.0.2661.86 Safari/537.36", + "expect": { + "vendor": "NuVision", + "model": "TM101A540N", + "type": "tablet" + } +}, +{ + "desc": "ZTE-Z431", + "ua": "ZTE-Z431/1.4.0 NetFront/4.2 QTV5.1 Profile/MIDP-2.1 Configuration/CLDC-1.1", + "expect": { + "vendor": "ZTE", + "model": "Z431", + "type": "mobile" + } +}, +{ + "desc": "ZTE", + "ua": "Mozilla/5.0 (Linux; U; Android 4.1.2; en-us; ZTE-Z740G Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "ZTE", + "model": "Z740G", + "type": "mobile" + } +}, +{ + "desc": "ZTE K Series Tablet", + "ua": "Mozilla/5.0 (Linux; Android 6.0.1; K88 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "ZTE", + "model": "K88", + "type": "tablet" + } +}, +{ + "desc": "ZTE Nubia Red Magic 3", + "ua": "Mozilla/5.0 (Linux; Android 9; NX629J Build/PKQ1.190321.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/45016 Mobile Safari/537.36 MMWEBID/4064 MicroMessenger/7.0.10.1580(0x27000A34) Process/tools NetType/WIFI Language/zh_CN ABI/arm64", + "expect": { + "vendor": "ZTE", + "model": "NX629J", + "type": "mobile" + } +}, +{ + "desc": "ZTE Blade A5", + "ua": "Mozilla/5.0 (Linux; Android 9; ZTE Blade A5 2019) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "ZTE", + "model": "Blade A5 2019", + "type": "mobile" + } +}, +{ + "desc": "ZTE BLADE V0730", + "ua": "Mozilla/5.0 (Linux; Android 6.0; ZTE BLADE V0730) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.116 Mobile Safari/537.36", + "expect": { + "vendor": "ZTE", + "model": "BLADE V0730", + "type": "mobile" + } +}, +{ + "desc": "ZTE B2017G", + "ua": "Mozilla/5.0 (Linux; Android 7.1.1; ZTE B2017G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.93 Mobile Safari/537.36", + "expect": { + "vendor": "ZTE", + "model": "B2017G", + "type": "mobile" + } +}, +{ + "desc": "Swizz GEN610", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; GEN610 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36", + "expect": { + "vendor": "Swiss", + "model": "GEN610", + "type": "mobile" + } +}, +{ + "desc": "Swizz ZUR700", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; ZUR700 Build/KVT49L) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Safari/537.36", + "expect": { + "vendor": "Swiss", + "model": "ZUR700", + "type": "tablet" + } +}, +{ + "desc": "Zeki TB782b Tablet", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-US; TB782B Build/IMM76D) AppleWebKit/534.31 (KHTML, like Gecko) UCBrowser/9.0.2.299 U3/0.8.0 Mobile Safari/534.31", + "expect": { + "vendor": "Zeki", + "model": "TB782B", + "type": "tablet" + } +}, +{ + "desc": "Dragon Touch Tablet", + "ua": "Mozilla/5.0 (Linux; Android 4.0.4; DT9138B Build/IMM76D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.72 Mobile Safari/537.36", + "expect": { + "vendor": "Dragon Touch", + "model": "9138B", + "type": "tablet" + } +}, +{ + "desc": "Insignia Tablet", + "ua": "Mozilla/5.0 (Linux; U; Android 6.0.1; NS-P08A7100 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Insignia", + "model": "NS-P08A7100", + "type": "tablet" + } +}, +{ + "desc": "Voice Xtreme V75", + "ua": "Mozilla/5.0 (Linux; U; Android 4.2.1; en-us; V75 Build/JOP40D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", + "expect": { + "vendor": "Voice", + "model": "V75", + "type": "mobile" + } +}, +{ + "desc": "LvTel V11", + "ua": "Mozilla/5.0 (Linux; Android 5.1.1; V11 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Safari/537.36", + "expect": { + "vendor": "LvTel", + "model": "V11", + "type": "mobile" + } +}, +{ + "desc": "Envizen Tablet V100MD", + "ua": "Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; V100MD Build/V100MD.20130816) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "Envizen", + "model": "V100MD", + "type": "tablet" + } +}, +{ + "desc": "Rotor Tablet", + "ua": "mozilla/5.0 (linux; android 5.0.1; tu_1491 build/lrx22c) applewebkit/537.36 (khtml, like gecko) chrome/43.0.2357.93 safari/537.36", + "expect": { + "vendor": "Rotor", + "model": "1491", + "type": "tablet" + } +}, +{ + "desc": "MachSpeed Tablets", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; Trio 7.85 vQ Build/Trio_7.85_vQ) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36", + "expect": { + "vendor": "MachSpeed", + "model": "Trio 7.85 vQ", + "type": "tablet" + } +}, +{ + "desc": "Trinity Tablets", + "ua": "Mozilla/5.0 (Linux; Android 5.0.1; Trinity T101 Build/LRX22C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Safari/537.36", + "expect": { + "vendor": "Trinity", + "model": "T101", + "type": "tablet" + } +}, +{ + "desc": "NextBook Next7", + "ua": "Mozilla/5.0 (Linux; U; Android 4.0.4; en-us; Next7P12 Build/IMM76I) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", + "expect": { + "vendor": "NextBook", + "model": "Next7P12", + "type": "tablet" + } +}, +{ + "desc": "NextBook Tablets", + "ua": "Mozilla/5.0 (Linux; Android 5.0; NXA8QC116 Build/LRX21V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "NextBook", + "model": "NXA8QC116", + "type": "tablet" + } +}, +{ + "desc": "Le Pan Tablets", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Le Pan TC802A Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Le Pan", + "model": "TC802A", + "type": "tablet" + } +}, +{ + "desc": "Le Pan Tablets", + "ua": "Mozilla/5.0 (Linux; Android 4.2.2; Le Pan TC802A Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36", + "expect": { + "vendor": "Le Pan", + "model": "TC802A", + "type": "tablet" + } +}, +{ + "desc": "AT&T Radiant Core U304AA", + "ua": "Dalvik/2.1.0 (Linux; U; Android 9; U304AA Build/P00610)", + "expect": { + "vendor": "AT&T", + "model": "U304AA", + "type": "mobile" + } +}, +{ + "desc": "Vodafone Smart Tab 4G", + "ua": "Mozilla/5.0 (Linux; Android 4.4.4; Vodafone Smart Tab 4G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", + "expect": { + "vendor": "Vodafone", + "model": "Smart Tab 4G", + "type": "tablet" + } +}, +{ + "desc": "Vodafone Smart ultra 6", + "ua": "Mozilla/5.0 (Linux; Android 5.0.2; Vodafone Smart ultra 6 Build/LRX22G) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36", + "expect": { + "vendor": "Vodafone", + "model": "Smart ultra 6", + "type": "tablet" + } +}, +{ + "desc": "4ife 4K Smart TV Box", + "ua": "Mozilla/5.0 (Linux; Android 4.4.2; 4ife 4K Smart TV Box Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Safari/537.36 Vinebre", + "expect": { + "vendor": "undefined", + "model": "undefined", + "type": "smarttv" + } +}] \ No newline at end of file diff --git a/test/data/ua/extension/fetcher.json b/test/data/ua/extension/fetcher.json new file mode 100644 index 000000000..13c9f62b3 --- /dev/null +++ b/test/data/ua/extension/fetcher.json @@ -0,0 +1,402 @@ +[ + { + "desc" : "AhrefsSiteAudit", + "ua" : "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.5359.128 Mobile Safari/537.36 (compatible; AhrefsSiteAudit/6.1; +http://ahrefs.com/robot/site-audit)", + "expect" : + { + "name" : "AhrefsSiteAudit", + "version" : "6.1", + "type" : "fetcher" + } + }, + { + "desc" : "Asana", + "ua" : "Asana/1.4.0 WebsiteMetadataRetriever", + "expect" : + { + "name" : "Asana", + "version" : "1.4.0", + "type" : "fetcher" + } + }, + { + "desc" : "Better Uptime Bot", + "ua" : "Better Uptime Bot Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36", + "expect" : + { + "name" : "Better Uptime Bot", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "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" + } + }, + { + "desc" : "Bit.ly", + "ua" : "bitlybot/3.0 (+http://bit.ly/)", + "expect" : + { + "name" : "bitlybot", + "version" : "3.0", + "type" : "fetcher" + } + }, + { + "desc" : "Blueno", + "ua" : "acebookexternalhit/1.1 (compatible; Blueno/1.0; +http://naver.me/scrap)", + "expect" : + { + "name" : "Blueno", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "Bluesky", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Bluesky Cardyb/1.1; +mailto:support@bsky.app) Chrome/100.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Bluesky", + "version" : "1.1", + "type" : "fetcher" + } + }, + { + "desc" : "BufferLinkPreviewBot", + "ua" : "BufferLinkPreviewBot/1.0 (+https://scraper.buffer.com/about/bots/link-preview-bot)", + "expect" : + { + "name" : "BufferLinkPreviewBot", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "ChatGPT-User", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot", + "expect" : + { + "name" : "ChatGPT-User", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "Chrome-Lighthouse", + "ua" : "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4590.2 Mobile Safari/537.36 Chrome-Lighthouse", + "expect" : + { + "name" : "Chrome-Lighthouse", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "DuckAssistBot", + "ua" : "DuckAssistBot/1.2; (+http://duckduckgo.com/duckassistbot.html)", + "expect" : + { + "name" : "DuckAssistBot", + "version" : "1.2", + "type" : "fetcher" + } + }, + { + "desc" : "Gemini-Deep-Research", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Gemini-Deep-Research; +https://gemini.google/overview/deep-research/) Chrome/135.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Gemini-Deep-Research", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Google FeedFetcher", + "ua" : "FeedFetcher-Google; (+http://www.google.com/feedfetcher.html)", + "expect" : + { + "name" : "FeedFetcher-Google", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Google Image Proxy", + "ua" : "Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 (via ggpht.com GoogleImageProxy)", + "expect" : + { + "name" : "GoogleImageProxy", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Google Read Aloud - Mobile agent", + "ua" : "Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.125 Mobile Safari/537.36 (compatible; Google-Read-Aloud; +https://support.google.com/webmasters/answer/1061943)", + "expect" : + { + "name" : "Google-Read-Aloud", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Google Read Aloud - Desktop agent", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36 (compatible; Google-Read-Aloud; +https://support.google.com/webmasters/answer/1061943)", + "expect" : + { + "name" : "Google-Read-Aloud", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Google Page Renderer", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 Google-PageRenderer Google (+https://developers.google.com/+/web/snippet/) ", + "expect" : + { + "name" : "Google-PageRenderer", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Google Publisher Center", + "ua" : "GoogleProducer; (+https://developers.google.com/search/docs/crawling-indexing/google-producer)", + "expect" : + { + "name" : "GoogleProducer", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Google Site Verifier", + "ua" : "Mozilla/5.0 (compatible; Google-Site-Verification/1.0)", + "expect" : + { + "name" : "Google-Site-Verification", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "Iframely", + "ua" : "Iframely/1.3.1 (+https://iframely.com/docs/about)", + "expect" : + { + "name" : "Iframely", + "version" : "1.3.1", + "type" : "fetcher" + } + }, + { + "desc" : "kakaotalk-scrap", + "ua" : "facebookexternalhit/1.1; kakaotalk-scrap/1.0; +https://devtalk.kakao.com/t/scrap/33984", + "expect" : + { + "name" : "kakaotalk-scrap", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "Meta-ExternalFetcher", + "ua" : "meta-externalfetcher/1.1 (+https://developers.facebook.com/docs/sharing/webmasters/crawler)", + "expect" : + { + "name" : "meta-externalfetcher", + "version" : "1.1", + "type" : "fetcher" + } + }, + { + "desc" : "MicrosoftPreview", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; MicrosoftPreview/2.0; +https://aka.ms/MicrosoftPreview) Chrome/W.X.Y.Z Safari/537.36", + "expect" : + { + "name" : "MicrosoftPreview", + "version" : "2.0", + "type" : "fetcher" + } + }, + { + "desc" : "MistralAI-User", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; MistralAI-User/1.0; +https://docs.mistral.ai/robots)", + "expect" : + { + "name" : "MistralAI-User", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "Perplexity-User", + "ua" : "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Perplexity-User/1.0; +https://perplexity.ai/perplexity-user)", + "expect" : + { + "name" : "Perplexity-User", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "Pinterestbot", + "ua" : "Mozilla/5.0 (compatible; Pinterestbot/1.0; +http://www.pinterest.com/bot.html)", + "expect" : + { + "name" : "Pinterestbot", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "Rogerbot", + "ua" : "Mozilla/5.0 (compatible; rogerBot/1.0; UrlCrawler; http://www.seomoz.org/dp/rogerbot)", + "expect" : + { + "name" : "rogerBot", + "version" : "1.0", + "type" : "fetcher" + } + }, + { + "desc" : "SiteAuditBot", + "ua" : "Mozilla/5.0 (compatible; SiteAuditBot/0.97; +http://www.semrush.com/bot.html)", + "expect" : + { + "name" : "SiteAuditBot", + "version" : "0.97", + "type" : "fetcher" + } + }, + { + "desc" : "Snap URL Preview", + "ua" : "Snap URL Preview Service; bot; snapchat; https://developers.snap.com/robots ", + "expect" : + { + "name" : "Snap URL Preview", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "SkypeUriPreview", + "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) SkypeUriPreview Preview/0.5 skype-url-preview@microsoft.com", + "expect" : + { + "name" : "SkypeUriPreview", + "version" : "0.5", + "type" : "fetcher" + } + }, + { + "desc" : "TelegramBot", + "ua" : "TelegramBot (like TwitterBot)", + "expect" : + { + "name" : "TelegramBot", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "TikTokSpider", + "ua" : "Mozilla/5.0 (Linux; Android 5.0) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36 (compatible; TikTokSpider; ttspider-feedback@tiktok.com)", + "expect" : + { + "name" : "TikTokSpider", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "UptimeRobot", + "ua" : "Mozilla/5.0 (compatible; UptimeRobot/2.0; http://www.uptimerobot.com/)", + "expect" : + { + "name" : "UptimeRobot", + "version" : "2.0", + "type" : "fetcher" + } + }, + { + "desc" : "vercel-favicon-bot", + "ua" : "vercel-favicon-bot", + "expect" : + { + "name" : "vercel-favicon-bot", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "vercel-screenshot-bot", + "ua" : "vercel-screenshot-bot", + "expect" : + { + "name" : "vercel-screenshot-bot", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "Vercelbot", + "ua" : "Vercelbot (+https://vercel.com)", + "expect" : + { + "name" : "Vercelbot", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "vercelflags", + "ua" : "vercelflags", + "expect" : + { + "name" : "vercelflags", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "verceltracing", + "ua" : "verceltracing", + "expect" : + { + "name" : "verceltracing", + "version" : "undefined", + "type" : "fetcher" + } + }, + { + "desc" : "WhatsApp", + "ua" : "WhatsApp/2.23.20.0", + "expect" : + { + "name" : "WhatsApp", + "version" : "2.23.20.0", + "type" : "fetcher" + } + }, + { + "desc" : "Zoombot", + "ua" : "Mozilla/5.0 (compatible; Zoombot/1.0; +https://zoom.us; crawler@domain.com)", + "expect" : + { + "name" : "Zoombot", + "version" : "1.0", + "type" : "fetcher" + } + } +] \ No newline at end of file diff --git a/test/data/ua/extension/inapp.json b/test/data/ua/extension/inapp.json new file mode 100644 index 000000000..f1861c401 --- /dev/null +++ b/test/data/ua/extension/inapp.json @@ -0,0 +1,202 @@ +[ + { + "desc" : "Discord on Linux", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) discord/0.0.26 Chrome/108.0.5359.215 Electron/22.3.2 Safari/537.36", + "expect" : + { + "name" : "discord", + "version" : "0.0.26", + "type" : "inapp" + } + }, + { + "desc" : "Discord on iPad", + "ua" : "Discord/52.0 (iPad; iOS 14.4; Scale/2.00)", + "expect" : + { + "name" : "Discord", + "version" : "52.0", + "type" : "inapp" + } + }, + { + "desc" : "Evernote on Windows", + "ua" : "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Evernote Windows/306387 (pt-PT, DDL); Windows/6.1.0 (Win32); Safari/537.36", + "expect" : + { + "name" : "Evernote", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Figma on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_4_0) AppleWebKit/537.36 (KHTML, like Gecko) Figma/99.0.0 Chrome/89.0.4389.128 Electron/12.0.9 Safari/537.36", + "expect" : + { + "name" : "Figma", + "version" : "99.0.0", + "type" : "inapp" + } + }, + { + "desc" : "Flipboard on Android", + "ua" : "Mozilla/5.0 (Linux; Android 8.0.0; SM-A720F Build/R16NW; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/70.0.3538.110 Mobile Safari/537.36 Flipboard/4.1.13/4342,4.1.13.4342", + "expect" : + { + "name" : "Flipboard", + "version" : "4.1.13", + "type" : "inapp" + } + }, + { + "desc" : "Mattermost on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Mattermost/4.4.0 Chrome/76.0.3809.146 Electron/6.1.7 Safari/537.36", + "expect" : + { + "name" : "Mattermost", + "version" : "4.4.0", + "type" : "inapp" + } + }, + { + "desc" : "Mattermost on iPad", + "ua" : "Mattermost/1.49.1 (iPad; iOS 15.3.1; Scale/2.00)", + "expect" : + { + "name" : "Mattermost", + "version" : "1.49.1", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_3_1) AppleWebKit/537.36 (KHTML, like Gecko) Teams/1.8.00.3758 Chrome/126.0.6478.261 Electron/31.7.7 Safari/537.36", + "expect" : + { + "name" : "Teams", + "version" : "1.8.00.3758", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on iPad", + "ua" : "Mozilla/5.0 (iPad; CPU OS 16_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 TeamsMobile-iOS", + "expect" : + { + "name" : "Teams", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on iPhone", + "ua" : "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 TeamsMobile-iOS", + "expect" : + { + "name" : "Teams", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Microsoft Teams on Android", + "ua" : "Mozilla/5.0 (Linux; Android 8.1.0; SM-G610F Build/M1AJQ; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.101 Mobile Safari/537.36 TeamsMobile-Android", + "expect" : + { + "name" : "Teams", + "version" : "undefined", + "type" : "inapp" + } + }, + { + "desc" : "Notion on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Notion/2.0.23 Chrome/91.0.4472.164 Electron/13.6.9 Safari/537.36", + "expect" : + { + "name" : "Notion", + "version" : "2.0.23", + "type" : "inapp" + } + }, + { + "desc" : "Postman on Mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Postman/9.29.0 Chrome/94.0.4606.81 Electron/15.5.7 Safari/537.36", + "expect" : + { + "name" : "Postman", + "version" : "9.29.0", + "type" : "inapp" + } + }, + { + "desc" : "Rambox on mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) Rambox/0.7.7 Chrome/78.0.3904.130 Electron/7.2.4 Safari/537.36", + "expect" : + { + "name" : "Rambox", + "version" : "0.7.7", + "type" : "inapp" + } + }, + { + "desc" : "Rocket.Chat on mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Rocket.Chat/3.8.16 Chrome/106.0.5249.199 Electron/21.3.3 Safari/537.36", + "expect" : + { + "name" : "Rocket.Chat", + "version" : "3.8.16", + "type" : "inapp" + } + }, + { + "desc" : "Slack on mac", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Slack/4.39.90 Chrome/127.0.6533.72 Electron/13.1.9 Safari/537.36", + "expect" : + { + "name" : "Slack", + "version" : "4.39.90", + "type" : "inapp" + } + }, + { + "desc" : "TikTok Lite", + "ua" : "Mozilla/5.0 (Linux; Android 8.0.0; SM-J400F Build/R16NW; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/68.0.3440.91 Mobile Safari/537.36 Channel/release AppName/ultralite app_version/27.2.3 Region/ID ByteLocale/id-ID ByteFullLocale/id-ID", + "expect" : + { + "name" : "TikTok Lite", + "version" : "27.2.3", + "type" : "inapp" + } + }, + { + "desc" : "VS Code on Windows", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Code/1.85.1 Chrome/114.0.5735.289 Electron/25.9.7 Safari/537.36", + "expect" : + { + "name" : "VS Code", + "version" : "1.85.1", + "type" : "inapp" + } + }, + { + "desc" : "Yahoo! Japan on Android", + "ua" : "Mozilla/5.0 (Linux; Android 13; SH-M20 Build/TKQ1.220915.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.163 Mobile Safari/537.36 YJApp-ANDROID jp.co.yahoo.android.yjtop/3.187.0", + "expect" : + { + "name" : "Yahoo! Japan", + "version" : "3.187.0", + "type" : "inapp" + } + }, + { + "desc" : "Yahoo! Japan on iOS", + "ua" : "Mozilla/5.0 (iPad; CPU OS 18_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 YJApp-IOS jp.co.yahoo.ipn.appli/4.131.0", + "expect" : + { + "name" : "Yahoo! Japan", + "version" : "4.131.0", + "type" : "inapp" + } + } +] diff --git a/test/data/ua/extension/library.json b/test/data/ua/extension/library.json new file mode 100644 index 000000000..17435a28f --- /dev/null +++ b/test/data/ua/extension/library.json @@ -0,0 +1,242 @@ +[ + { + "desc" : "AdobeAIR", + "ua" : "Mozilla/5.0 (Windows; U; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) AdobeAIR/3.1", + "expect" : + { + "name" : "AdobeAIR", + "version" : "3.1", + "type" : "library" + } + }, + { + "desc" : "aiohttp", + "ua" : "Python/3.9 aiohttp/3.8.1", + "expect" : + { + "name" : "aiohttp", + "version" : "3.8.1", + "type" : "library" + } + }, + { + "desc" : "Apache-HttpClient", + "ua" : "Apache-HttpClient/4.5.14 (Java/17.0.12)", + "expect" : + { + "name" : "Apache-HttpClient", + "version" : "4.5.14", + "type" : "library" + } + }, + { + "desc" : "Axios", + "ua" : "axios/1.7.2", + "expect" : + { + "name" : "axios", + "version" : "1.7.2", + "type" : "library" + } + }, + { + "desc" : "go-http-client", + "ua" : "go-http-client/1.1", + "expect" : + { + "name" : "go-http-client", + "version" : "1.1", + "type" : "library" + } + }, + { + "desc" : "got", + "ua" : "got/9.6.0 (https://github.com/sindresorhus/got)", + "expect" : + { + "name" : "got", + "version" : "9.6.0", + "type" : "library" + } + }, + { + "desc" : "GuzzleHttp", + "ua" : "GuzzleHttp/6.5.5 curl/7.70.0 PHP/7.4.22", + "expect" : + { + "name" : "GuzzleHttp", + "version" : "6.5.5", + "type" : "library" + } + }, + { + "desc" : "Java", + "ua" : "Java/1.6.0_14", + "expect" : + { + "name" : "Java", + "version" : "1.6.0_14", + "type" : "library" + } + }, + { + "desc" : "Java HTTPClient", + "ua" : "Java-http-client/11.0.6", + "expect" : + { + "name" : "Java-http-client", + "version" : "11.0.6", + "type" : "library" + } + }, + { + "desc" : "jsdom", + "ua" : "Mozilla/5.0 (unknown OS) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/11.12.0", + "expect" : + { + "name" : "jsdom", + "version" : "11.12.0", + "type" : "library" + } + }, + { + "desc" : "libwww-perl", + "ua" : "libwww-perl/6.05", + "expect" : + { + "name" : "libwww-perl", + "version" : "6.05", + "type" : "library" + } + }, + { + "desc" : "lua-resty-http", + "ua" : "lua-resty-http/0.07 (Lua)", + "expect" : + { + "name" : "lua-resty-http", + "version" : "0.07", + "type" : "library" + } + }, + { + "desc" : "Needle", + "ua" : "Needle/3.2.0 (Node.js v18.14.2; win32 x64)", + "expect" : + { + "name" : "Needle", + "version" : "3.2.0", + "type" : "library" + } + }, + { + "desc" : "Nutch", + "ua" : "AliyunSecBot/Nutch-1.21-SNAPSHOT", + "expect" : + { + "name" : "Nutch", + "version" : "1.21-SNAPSHOT", + "type" : "library" + } + }, + { + "desc" : "OkHttp", + "ua" : "okhttp/3.2.0", + "expect" : + { + "name" : "okhttp", + "version" : "3.2.0", + "type" : "library" + } + }, + { + "desc" : "node-fetch", + "ua" : "node-fetch/1.0 (+https://github.com/bitinn/node-fetch)", + "expect" : + { + "name" : "node-fetch", + "version" : "1.0", + "type" : "library" + } + }, + { + "desc" : "PHP-SOAP", + "ua" : "PHP-SOAP/7.4.33", + "expect" : + { + "name" : "PHP-SOAP", + "version" : "7.4.33", + "type" : "library" + } + }, + { + "desc" : "PostmanRuntime", + "ua" : "PostmanRuntime/7.26.5", + "expect" : + { + "name" : "PostmanRuntime", + "version" : "7.26.5", + "type" : "library" + } + }, + { + "desc" : "Python httpx", + "ua" : "python-httpx/0.27.2", + "expect" : + { + "name" : "python-httpx", + "version" : "0.27.2", + "type" : "library" + } + }, + { + "desc" : "Python urllib", + "ua" : "Python-urllib/2.6", + "expect" : + { + "name" : "Python-urllib", + "version" : "2.6", + "type" : "library" + } + }, + { + "desc" : "Python urllib3", + "ua" : "python-urllib3/1.26.18", + "expect" : + { + "name" : "python-urllib3", + "version" : "1.26.18", + "type" : "library" + } + }, + { + "desc" : "Python requests", + "ua" : "python-requests/2.32", + "expect" : + { + "name" : "python-requests", + "version" : "2.32", + "type" : "library" + } + }, + { + "desc" : "Scrapy", + "ua" : "Scrapy/1.5.0 (+https://scrapy.org)", + "expect" : + { + "name" : "Scrapy", + "version" : "1.5.0", + "type" : "library" + } + }, + { + "desc" : "superagent", + "ua" : "node-superagent/5.0.2", + "expect" : + { + "name" : "node-superagent", + "version" : "5.0.2", + "type" : "library" + } + } +] diff --git a/test/mediaplayer-test.json b/test/data/ua/extension/mediaplayer.json similarity index 94% rename from test/mediaplayer-test.json rename to test/data/ua/extension/mediaplayer.json index d40ba04f0..d093048a3 100644 --- a/test/mediaplayer-test.json +++ b/test/data/ua/extension/mediaplayer.json @@ -24,9 +24,9 @@ "ua" : "AppleTV/3.0.2 (Macintosh; Intel Mac OS X 10.4.7) AppleWebKit/528.18", "expect" : { - "name" : "WebKit", - "version" : "528.18", - "major" : "528" + "name" : "AppleTV", + "version" : "3.0.2", + "major" : "3" } }, { @@ -109,16 +109,6 @@ "major" : "1" } }, - { - "desc" : "Dalvik", - "ua" : "Dalvik/1.2.0 (Linux; U; Android 2.2.1; GT-S5830L Build/FROYO)", - "expect" : - { - "name" : "Dalvik", - "version" : "1.2.0", - "major" : "1" - } - }, { "desc" : "NexPlayer", "ua" : "E97510d/ Player/NexPlayer 4.0", @@ -189,16 +179,6 @@ "major" : "3" } }, - { - "desc" : "Java", - "ua" : "Java/1.4.1_04", - "expect" : - { - "name" : "Java", - "version" : "1.4.1_04", - "major" : "1" - } - }, { "desc" : "LG Player", "ua" : "LG Player 1.0; Android", @@ -509,16 +489,6 @@ "major" : "3" } }, - { - "desc" : "Wget", - "ua" : "Wget/1.12 (darwin10.7.0)", - "expect" : - { - "name" : "Wget", - "version" : "1.12", - "major" : "1" - } - }, { "desc" : "Winamp", "ua" : "Winamp 2.81", diff --git a/test/data/ua/extension/vehicle.json b/test/data/ua/extension/vehicle.json new file mode 100644 index 000000000..780005110 --- /dev/null +++ b/test/data/ua/extension/vehicle.json @@ -0,0 +1,62 @@ +[ + { + "desc" : "BMW", + "ua" : "Mozilla/5.0 (Linux; Android 9; AFTLBT962E2) AppleWebKit/537.36 (KHTML, like Gecko) Silk/118.3.1 like Chrome/118.0.5993.155 Safari/537.36", + "expect" : + { + "vendor" : "BMW", + "model" : "undefined", + "type" : "undefined" + } + }, + { + "desc" : "BYD", + "ua" : "Mozilla/5.0 (Linux; Android 10; DiLink3.0 For BYD AUTO Build/QKQ1.200816.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.186 Safari/537.36", + "expect" : + { + "vendor" : "BYD", + "model" : "undefined", + "type" : "undefined" + } + }, + { + "desc" : "Jeep", + "ua" : "Mozilla/5.0 (Linux; Android 9; AFTLFT962X3) AppleWebKit/537.36 (KHTML, like Gecko) Silk/124.5.2 like Chrome/124.0.6367.248 Safari/537.36", + "expect" : + { + "vendor" : "Jeep", + "model" : "Wagooner", + "type" : "undefined" + } + }, + { + "desc" : "Rivian", + "ua" : "Dalvik/2.1.0 (Linux; U; Android 10; Rivian R1T Build/QQ3A.200605.002)", + "expect" : + { + "vendor" : "Rivian", + "model" : "R1T", + "type" : "undefined" + } + }, + { + "desc" : "Tesla", + "ua" : "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/537.36 (KHTML, like Gecko) Chromium/79.0.3945.130 Chrome/79.0.3945.130 Safari/537.36 Tesla/2020.36.16-3e9e4e8dd287", + "expect" : + { + "vendor" : "Tesla", + "model" : "undefined", + "type" : "embedded" + } + }, + { + "desc" : "Volvo", + "ua" : "Mozilla/5.0 (VCC; 1.0; like Gecko) NetFront/4.2", + "expect" : + { + "vendor" : "Volvo", + "model" : "undefined", + "type" : "undefined" + } + } +] diff --git a/test/data/ua/os/aix.json b/test/data/ua/os/aix.json new file mode 100644 index 000000000..d45d1cbe6 --- /dev/null +++ b/test/data/ua/os/aix.json @@ -0,0 +1,38 @@ +[ + { + "desc" : "AIX", + "ua" : "Mozilla/5.0 (X11; U; AIX 000138384C00; en-US; rv:1.0.1) Gecko/20030213 Netscape/7.0", + "expect" : + { + "name" : "AIX", + "version" : "undefined" + } + }, + { + "desc" : "AIX", + "ua" : "Mozilla/5.0 (AIX; SPARC64; smart-thumbtack; Mosaic/2.7) Gecko/20100101 Mosaic/2.7", + "expect" : + { + "name" : "AIX", + "version" : "undefined" + } + }, + { + "desc" : "AIX", + "ua" : "Mozilla/5.0 (X11; N; Linux i686; en-US; rv:1.8.1.12) Gecko/20080219 Firefox/2.0.0.12 Mozilla/3.0 (X11; I; AIX 2) Navigator/9.0.0.6", + "expect" : + { + "name" : "AIX", + "version" : "2" + } + }, + { + "desc" : "AIX", + "ua" : "Mozilla/5.0 (X11; U; AIX 7.2; en-US; rv:1.7.12) Gecko/20100101 Firefox/126.0", + "expect" : + { + "name" : "AIX", + "version" : "7.2" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/amigaos.json b/test/data/ua/os/amigaos.json new file mode 100644 index 000000000..398d9b21b --- /dev/null +++ b/test/data/ua/os/amigaos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "AmigaOS", + "ua" : "Mozilla/4.0 (compatible; AWEB 3.4 SE; AmigaOS)", + "expect" : + { + "name" : "AmigaOS", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/android-x86.json b/test/data/ua/os/android-x86.json new file mode 100644 index 000000000..e83d3ca45 --- /dev/null +++ b/test/data/ua/os/android-x86.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Android-x86", + "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Generic Android-x86) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 OPR/61.2.3076.56749", + "expect" : + { + "name" : "Android-x86", + "version" : "7.1.2" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/android.json b/test/data/ua/os/android.json new file mode 100644 index 000000000..7304e1e7f --- /dev/null +++ b/test/data/ua/os/android.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "Android", + "ua" : "Mozilla/5.0 (Linux; U; Android 2.2.2; en-us; VM670 Build/FRG83G) AppleWebKit/533.1 (KHTML, like Gecko)", + "expect" : + { + "name" : "Android", + "version" : "2.2.2" + } + }, + { + "desc" : "MIUI", + "ua" : "Dalvik/2.1.0 (Linux; U; Android 9; Mi MIX 3 5G MIUI/V10.3.2.0.PEMEUVF)", + "expect" : + { + "name" : "Android", + "version" : "9" + } + }, + { + "desc" : "KTB-Nexus 5", + "ua" : "APP-My App/1.0 (Linux; Android 4.2.1; Nexus 5 Build/JOP40D)", + "expect" : + { + "name" : "Android", + "version" : "4.2.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/arcaos.json b/test/data/ua/os/arcaos.json new file mode 100644 index 000000000..5097545b7 --- /dev/null +++ b/test/data/ua/os/arcaos.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "ArcaOS", + "ua" : "Mozilla/5.0 (OS/2; ArcaOS 5.0.6; x86_64; rv:89.0) Gecko/20100101 Firefox/89.0", + "expect" : + { + "name" : "ArcaOS", + "version" : "5.0.6" + } + }, + { + "desc" : "ArcaOS", + "ua" : "Mozilla/5.0 (OS/2; ArcaOS; x64; rv:89.0) Gecko/20100101 Firefox/89.0", + "expect" : + { + "name" : "ArcaOS", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/arch.json b/test/data/ua/os/arch.json new file mode 100644 index 000000000..a186af0f2 --- /dev/null +++ b/test/data/ua/os/arch.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "Arch", + "ua" : "Mozilla/5.0 (X11; Arch Linux i686; rv:2.0) Gecko/20100101 Firefox/126.1", + "expect" : + { + "name" : "Arch", + "version" : "undefined" + } + }, + { + "desc" : "Arch", + "ua" : "Mozilla/5.0 ArchLinux (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41 Safari/535.1", + "expect" : + { + "name" : "Arch", + "version" : "undefined" + } + }, + { + "desc" : "Arch", + "ua" : "Uzbl (Webkit 1.1.10) (Arch Linux)", + "expect" : + { + "name" : "Arch", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/bada.json b/test/data/ua/os/bada.json new file mode 100644 index 000000000..1125641a2 --- /dev/null +++ b/test/data/ua/os/bada.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Bada", + "ua" : "Mozilla/5.0 (SAMSUNG; SAMSUNG-GT-S5253/S5253DDKC1; U; Bada/1.0; en-us) AppleWebKit/533.1 (KHTML, like Gecko) Dolfin/2.0 Mobile WQVGA SMM-MMS/1.2.0 OPN-B", + "expect" : + { + "name" : "Bada", + "version" : "1.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/beos.json b/test/data/ua/os/beos.json new file mode 100644 index 000000000..045e54f93 --- /dev/null +++ b/test/data/ua/os/beos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "BeOS", + "ua" : "Mozilla/5.0 (BeOS; U; BeOS BePC; en-US; rv:1.8.1.8pre) Gecko/20070926 SeaMonkey/1.1.5pre", + "expect" : + { + "name" : "BeOS", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/blackberry.json b/test/data/ua/os/blackberry.json new file mode 100644 index 000000000..1865ab6fb --- /dev/null +++ b/test/data/ua/os/blackberry.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "BlackBerry", + "ua" : "BlackBerry9300/5.0.0.912 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/378", + "expect" : + { + "name" : "BlackBerry", + "version" : "5.0.0.912" + } + }, + { + "desc" : "BlackBerry 10", + "ua" : "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.3+ (KHTML, like Gecko) Version/10.0.9.386 Mobile Safari/537.3+", + "expect" : + { + "name" : "BlackBerry", + "version" : "10" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/centos.json b/test/data/ua/os/centos.json new file mode 100644 index 000000000..4fe25b09a --- /dev/null +++ b/test/data/ua/os/centos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "CentOS", + "ua" : "Konqueror/15.13 (CentOS Linux 7.4; cs-CZ;)", + "expect" : + { + "name" : "CentOS", + "version" : "7.4" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/chrome-os.json b/test/data/ua/os/chrome-os.json new file mode 100644 index 000000000..69bc8786f --- /dev/null +++ b/test/data/ua/os/chrome-os.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Chrome OS", + "ua" : "Mozilla/5.0 (X11; CrOS x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Chrome OS", + "version" : "undefined" + } + }, + { + "desc" : "Chromium OS", + "ua" : "Mozilla/5.0 (X11; CrOS x86_64 10575.58.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", + "expect" : + { + "name" : "Chrome OS", + "version" : "10575.58.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/chromecast-android.json b/test/data/ua/os/chromecast-android.json new file mode 100644 index 000000000..c0969dd07 --- /dev/null +++ b/test/data/ua/os/chromecast-android.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Google Chromecast with Google TV", + "ua" : "Mozilla/5.0 (Linux; Android 12.0; Build/STTL.240206.002) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.0 Safari/537.36 CrKey/1.56.500000 DeviceType/AndroidTV", + "expect" : + { + "name" : "Chromecast Android", + "version" : "12.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/chromecast-fuchsia.json b/test/data/ua/os/chromecast-fuchsia.json new file mode 100644 index 000000000..5bfecf832 --- /dev/null +++ b/test/data/ua/os/chromecast-fuchsia.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Google Chromecast Nest Hub", + "ua" : "Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 CrKey/1.56.500000", + "expect" : + { + "name" : "Chromecast Fuchsia", + "version" : "1.56.500000" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/chromecast-linux.json b/test/data/ua/os/chromecast-linux.json new file mode 100644 index 000000000..608234665 --- /dev/null +++ b/test/data/ua/os/chromecast-linux.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Google Chromecast Legacy Linux-Based", + "ua" : "Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.81 Safari/537.36 CrKey/1.42.183786", + "expect" : + { + "name" : "Chromecast Linux", + "version" : "1.42.183786" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/chromecast-smartspeaker.json b/test/data/ua/os/chromecast-smartspeaker.json new file mode 100644 index 000000000..eb6f15b46 --- /dev/null +++ b/test/data/ua/os/chromecast-smartspeaker.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Google Chromecast Mini Smart Speaker", + "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.225 Safari/537.36 CrKey/1.56.500000 DeviceType/SmartSpeaker", + "expect" : + { + "name" : "Chromecast SmartSpeaker", + "version" : "1.56.500000" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/contiki.json b/test/data/ua/os/contiki.json new file mode 100644 index 000000000..b8b0503c3 --- /dev/null +++ b/test/data/ua/os/contiki.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Contiki", + "ua" : "Contiki/1.0 (Commodore 64; http://dunkels.com/adam/contiki/)", + "expect" : + { + "name" : "Contiki", + "version" : "1.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/debian.json b/test/data/ua/os/debian.json new file mode 100644 index 000000000..a6a777b2f --- /dev/null +++ b/test/data/ua/os/debian.json @@ -0,0 +1,65 @@ +[ + { + "desc" : "Debian", + "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.7 (like Gecko) (Debian)", + "expect" : + { + "name" : "Debian", + "version" : "undefined" + } + }, + { + "desc" : "Debian", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; Debian GNU/Linux 8.1 (jessie)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.0 Maxthon/1.0.5.3 Safari/537.36", + "expect" : + { + "name" : "Debian", + "version" : "8.1" + } + }, + { + "desc" : "Debian", + "ua" : "ELinks/0.12~pre5-4 (textmode; Debian; Linux 3.2.0-4-amd64 x86_64 192x47-2)", + "expect" : + { + "name" : "Debian", + "version" : "undefined" + } + }, + { + "desc" : "Debian", + "ua" : "w3m/0.5.3+debian-19", + "expect" : + { + "name" : "debian", + "version" : "19" + } + }, + { + "desc" : "Debian", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.3) Gecko/2008092814 (Debian-3.0.1-1)", + "expect" : + { + "name" : "Debian", + "version" : "3.0.1-1" + } + }, + { + "desc" : "Debian", + "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux 2.6.24.4; X11) KHTML/3.5.9 (like Gecko) (Debian package 4:3.5.9.dfsg.1-2+b1)", + "expect" : + { + "name" : "Debian", + "version" : "undefined" + } + }, + { + "desc" : "Debian", + "ua" : "Mozilla/5.0 (X11; U; Linux sparc64; es-PY; rv:5.0) Gecko/20100101 IceCat/5.0 (like Firefox/5.0; Debian-6.0.1)", + "expect" : + { + "name" : "Debian", + "version" : "6.0.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/deepin.json b/test/data/ua/os/deepin.json new file mode 100644 index 000000000..b41e2a3f7 --- /dev/null +++ b/test/data/ua/os/deepin.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Deepin", + "ua" : "Mozilla/5.0 (X11; Linux x86_64; Deepin 15.5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36 NFSBrowser/5.0.0.1886", + "expect" : + { + "name" : "Deepin", + "version" : "15.5" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/dragonfly.json b/test/data/ua/os/dragonfly.json new file mode 100644 index 000000000..f8f3c5833 --- /dev/null +++ b/test/data/ua/os/dragonfly.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "DragonFly", + "ua" : "Mozilla/5.0 (X11; U; DragonFly i386; de; rv:1.9.1) Gecko/20090720 Firefox/3.5.1", + "expect" : + { + "name" : "DragonFly", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/elementary-os.json b/test/data/ua/os/elementary-os.json new file mode 100644 index 000000000..1fd5db7b7 --- /dev/null +++ b/test/data/ua/os/elementary-os.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "elementary OS", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/604.1 (KHTML, like Gecko) Version/11.0 Safari/604.1 elementary OS/0.4 (Loki) Epiphany/3.18.11", + "expect" : + { + "name" : "elementary OS", + "version" : "0.4" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/fedora.json b/test/data/ua/os/fedora.json new file mode 100644 index 000000000..a7abe1fa4 --- /dev/null +++ b/test/data/ua/os/fedora.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "Fedora", + "ua" : "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0", + "expect" : + { + "name" : "Fedora", + "version" : "undefined" + } + }, + { + "desc" : "Fedora", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:2.0) Gecko/20110404 Fedora/16-dev Firefox/4.0", + "expect" : + { + "name" : "Fedora", + "version" : "16-dev" + } + }, + { + "desc" : "Fedora", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; sk; rv:1.9.0.4) Gecko/2008111217 Fedora/3.0.4-1.fc10 Firefox/3.0.4", + "expect" : + { + "name" : "Fedora", + "version" : "3.0.4-1.fc10" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/firefox-os.json b/test/data/ua/os/firefox-os.json new file mode 100644 index 000000000..faf081a15 --- /dev/null +++ b/test/data/ua/os/firefox-os.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "Firefox OS", + "ua" : "Mozilla/5.0 (Mobile; rv:14.0) Gecko/14.0 Firefox/14.0", + "expect" : + { + "name" : "Firefox OS", + "version" : "14.0" + } + }, + { + "desc" : "Firefox OS on Tablet", + "ua" : "Mozilla/5.0 (Tablet; rv:26.0) Gecko/26.0 Firefox/26.0", + "expect" : + { + "name" : "Firefox OS", + "version" : "26.0" + } + }, + { + "desc" : "Firefox OS on TV", + "ua" : "Mozilla/5.0 (TV; rv:44.0) Gecko/44.0 Firefox/44.0", + "expect" : + { + "name" : "Firefox OS", + "version" : "44.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/freebsd.json b/test/data/ua/os/freebsd.json new file mode 100644 index 000000000..74544a0a7 --- /dev/null +++ b/test/data/ua/os/freebsd.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "FreeBSD", + "ua" : "Mozilla/5.0 (X11; U; FreeBSD x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16", + "expect" : + { + "name" : "FreeBSD", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/fuchsia.json b/test/data/ua/os/fuchsia.json new file mode 100644 index 000000000..1f2e0501d --- /dev/null +++ b/test/data/ua/os/fuchsia.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Fuchsia", + "ua" : "Mozilla/5.0 (X11; Fuchsia x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3557.0 Safari/537.36", + "expect" : + { + "name" : "Fuchsia", + "version" : "undefined" + } + }, + { + "desc" : "Fuchsia", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Mozilla/5.0 (X11; Linux; Fuchsia; GoogleTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Large Screen Safari/537.36 GoogleTV", + "expect" : + { + "name" : "Fuchsia", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/gentoo.json b/test/data/ua/os/gentoo.json new file mode 100644 index 000000000..8fa207ba6 --- /dev/null +++ b/test/data/ua/os/gentoo.json @@ -0,0 +1,39 @@ +[ + + { + "desc" : "Gentoo", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.16) Gecko/20080716 (Gentoo) Galeon/2.0.6", + "expect" : + { + "name" : "Gentoo", + "version" : "undefined" + } + }, + { + "desc" : "Gentoo", + "ua" : "Xombrero (X11; U; Gentoo Linux amd64; en-US) Webkit/2.8.5", + "expect" : + { + "name" : "Gentoo", + "version" : "amd64" + } + }, + { + "desc" : "Gentoo", + "ua" : "Xombrero/1.6.4 (Linux amd64; en; Gentoo)", + "expect" : + { + "name" : "Gentoo", + "version" : "undefined" + } + }, + { + "desc" : "Gentoo", + "ua" : "Links (2.8; Linux 3.17.2-gentoo-x86 i686; GNU C 4.8.2; x)", + "expect" : + { + "name" : "gentoo", + "version" : "x86" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/ghostbsd.json b/test/data/ua/os/ghostbsd.json new file mode 100644 index 000000000..5e36d81f8 --- /dev/null +++ b/test/data/ua/os/ghostbsd.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "GhostBSD", + "ua" : "Mozilla/5.0 (X11; GhostBSD/10.3; x86_64; rv:50.0.1) Gecko/20100101 Firefox/50.0.1", + "expect" : + { + "name" : "GhostBSD", + "version" : "10.3" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/haiku.json b/test/data/ua/os/haiku.json new file mode 100644 index 000000000..f2537395b --- /dev/null +++ b/test/data/ua/os/haiku.json @@ -0,0 +1,38 @@ +[ + { + "desc" : "Haiku", + "ua" : "Mozilla/5.0 (Macintosh; Intel Haiku R1 x86) AppleWebKit/602.1.1 (KHTML, like Gecko) WebPositive/1.2 Version/8.0 Safari/602.1.1", + "expect" : + { + "name" : "Haiku", + "version" : "R1" + } + }, + { + "desc" : "Haiku", + "ua" : "Mozilla/5.0 (X11; Haiku x86_64; rv:128.0) Gecko/20100101 Firefox/128.0", + "expect" : + { + "name" : "Haiku", + "version" : "undefined" + } + }, + { + "desc" : "Haiku", + "ua" : "Mozilla/5.0 (Haiku; BeOS; rv:99.0) Gecko/20100101 Firefox/99.0", + "expect" : + { + "name" : "Haiku", + "version" : "undefined" + } + }, + { + "desc" : "Haiku", + "ua" : "Mozilla/5.0 (X11; Haiku BePC) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.15.17 Chrome/87.0.4280.144 Safari/537.36 Dooble/2023.12.25 Dooble/2023.12.25", + "expect" : + { + "name" : "Haiku", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/harmonyos.json b/test/data/ua/os/harmonyos.json new file mode 100644 index 000000000..c2016541d --- /dev/null +++ b/test/data/ua/os/harmonyos.json @@ -0,0 +1,56 @@ +[ + { + "desc" : "HarmonyOS", + "ua" : "Mozilla/5.0 (Linux; Android 10; HarmonyOS; YAL-AL10; HMSCore 6.3.0.327; GMSCore 21.48.15) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 HuaweiBrowser/12.0.3.310 Mobile Safari/537.36", + "expect" : + { + "name" : "HarmonyOS", + "version" : "undefined" + } + }, + { + "desc" : "HarmonyOS 2", + "ua" : "Mozilla/5.0 (Linux; Android 10; STK-AL00 Build/HUAWEISTK-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/88.0.4324.93 Mobile Safari/537.36 BDOS/1.0 (HarmonyOS 2.2.0) SP-engine/2.72.0 baiduboxapp/13.34.5.10 (Baidu; P1 10) NABar/1.0", + "expect" : + { + "name" : "HarmonyOS", + "version" : "2.2.0" + } + }, + { + "desc" : "HarmonyOS 3", + "ua" : "Mozilla/5.0 (Linux; Android 12; Huawei P60 Pro Build/HarmonyOS 3.1.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.1.6136.139 Mobile Safari/537.36 OPR/111.11", + "expect" : + { + "name" : "HarmonyOS", + "version" : "3.1.0" + } + }, + { + "desc" : "HarmonyOS 4", + "ua" : "Mozilla/5.0 (Android 12; HarmonyOS 4.0.0.118; OXF-AN00 Build/HUAWEIOXF-AN00; HMSCore 6.13.0.302; wv) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36 EdgA/125.0.0.0 Language/zh_TW ABI/arm64-v8a", + "expect" : + { + "name" : "HarmonyOS", + "version" : "4.0.0.118" + } + }, + { + "desc" : "HarmonyOS 4", + "ua" : "Mozilla/5.0 (Linux; Android 12; HarmonyOS/4.0.3.601; JAD-AL50; HMSCore/6.13.0.320 Build/AP31.240223.016.A3; x64; ARM64) AppleWebKit/537.36 (KHTML, like Gecko) HuaweiBrowser/14.0.7.302 Mobile Safari/537.36", + "expect" : + { + "name" : "HarmonyOS", + "version" : "4.0.3.601" + } + }, + { + "desc" : "HarmonyOS 5", + "ua" : "Mozilla/5.0 (PC; OpenHarmony 5.0; HarmonyOS 5.0) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Browser/harmony360Browser/1.0.0", + "expect" : + { + "name" : "HarmonyOS", + "version" : "5.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/hp-ux.json b/test/data/ua/os/hp-ux.json new file mode 100644 index 000000000..841474030 --- /dev/null +++ b/test/data/ua/os/hp-ux.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "HP-UX", + "ua" : "Mozilla/5.0 (X11; U; HP-UX 9000/785; es-ES; rv:1.0.1) Gecko/20020827 Netscape/7.0", + "expect" : + { + "name" : "HP-UX", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/hurd.json b/test/data/ua/os/hurd.json new file mode 100644 index 000000000..01707284c --- /dev/null +++ b/test/data/ua/os/hurd.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Hurd", + "ua" : "Mozilla/5.0 (X11; Hurd 0.9 i386; en-US) libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.0 Safari/696.96", + "expect" : + { + "name" : "Hurd", + "version" : "0.9" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/ios.json b/test/data/ua/os/ios.json new file mode 100644 index 000000000..86ba8cb47 --- /dev/null +++ b/test/data/ua/os/ios.json @@ -0,0 +1,65 @@ +[ + { + "desc" : "iOS in App", + "ua" : "AppName/version CFNetwork/version Darwin/version", + "expect" : + { + "name" : "iOS", + "version" : "undefined" + } + }, + { + "desc" : "iOS with Chrome", + "ua" : "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3", + "expect" : + { + "name" : "iOS", + "version" : "5.1.1" + } + }, + { + "desc" : "iOS with Opera Mini", + "ua" : "Opera/9.80 (iPhone; Opera Mini/7.1.32694/27.1407; U; en) Presto/2.8.119 Version/11.10", + "expect" : + { + "name" : "iOS", + "version" : "undefined" + } + }, + { + "desc": "iOS with FaceBook Mobile App", + "ua": "[FBAN/FBIOS;FBAV/283.0.0.44.117;FBBV/238386386;FBDV/iPhone12,1;FBMD/iPhone;FBSN/iOS;FBSV/13.6.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/240127608]", + "expect": + { + "name" : "iOS", + "version" : "13.6.1" + } + }, + { + "desc": "iOS with Slack App", + "ua": "com.tinyspeck.chatlyio/23.04.10 (iPhone; iOS 16.4.1; Scale/3.00)", + "expect": + { + "name" : "iOS", + "version" : "16.4.1" + } + }, + { + "desc" : "iOS BE App", + "ua" : "APP-BE Test/1.0 (iPad; Apple; CPU iPhone OS 7_0_2 like Mac OS X)", + "expect" : + { + "name" : "iOS", + "version" : "7.0.2" + } + }, + { + "desc": "Apple HomePod", + "ua": "AppleCoreMedia/1.0.0.15D61 (HomePod; U; CPU OS 11_2_5 like Mac OS X; en_us)", + "expect" : + { + "name" : "iOS", + "version" : "11.2.5" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/joli.json b/test/data/ua/os/joli.json new file mode 100644 index 000000000..e9815fd8d --- /dev/null +++ b/test/data/ua/os/joli.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Joli", + "ua" : "Mozilla/5.0 (X11; Jolicloud Linux i686) AppleWebKit/537.6 (KHTML, like Gecko) Joli OS/1.2 Chromium/23.0.1240.0 Chrome/23.0.1240.0 Safari/537.6", + "expect" : + { + "name" : "Joli", + "version" : "1.2" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/kaios.json b/test/data/ua/os/kaios.json new file mode 100644 index 000000000..f59572d59 --- /dev/null +++ b/test/data/ua/os/kaios.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "KaiOS", + "ua" : "Mozilla/5.0 (Mobile; Nokia_8110_4G; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5", + "expect" : + { + "name" : "KAIOS", + "version" : "2.5" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/knoppix.json b/test/data/ua/os/knoppix.json new file mode 100644 index 000000000..4ca6cdba1 --- /dev/null +++ b/test/data/ua/os/knoppix.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Knoppix", + "ua" : "Mozilla/5.0 (Knoppix; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.3", + "expect" : + { + "name" : "Knoppix", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/kubuntu.json b/test/data/ua/os/kubuntu.json new file mode 100644 index 000000000..d24214236 --- /dev/null +++ b/test/data/ua/os/kubuntu.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Kubuntu", + "ua" : "Mozilla/5.0 (compatible; Konqueror/4.4; Linux 2.6.32-22-generic; X11; en_US) KHTML/4.4.3 (like Gecko) Kubuntu", + "expect" : + { + "name" : "Kubuntu", + "version" : "undefined" + } + }, + { + "desc" : "Kubuntu", + "ua" : "Mozilla/5.0 (Kubuntu; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0", + "expect" : + { + "name" : "Kubuntu", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/linpus.json b/test/data/ua/os/linpus.json new file mode 100644 index 000000000..40c81691c --- /dev/null +++ b/test/data/ua/os/linpus.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Linpus", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b5pre) Gecko/2008032619 Linpus/3.0-0.49", + "expect" : + { + "name" : "Linpus", + "version" : "3.0-0.49" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/linspire.json b/test/data/ua/os/linspire.json new file mode 100644 index 000000000..4d2497b24 --- /dev/null +++ b/test/data/ua/os/linspire.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Linspire", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060803 Firefox/1.5.0.4 Linspire/1.5.0.4", + "expect" : + { + "name" : "Linspire", + "version" : "1.5.0.4" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/linux.json b/test/data/ua/os/linux.json new file mode 100644 index 000000000..af7859363 --- /dev/null +++ b/test/data/ua/os/linux.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Linux", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36", + "expect" : + { + "name" : "Linux", + "version" : "undefined" + } + }, + { + "desc" : "Linux", + "ua" : "Mozilla/5.0 (X11; U; Linux armv61; en-US; rv:1.9.1b2pre) Gecko/20081015 Fennec/1.0a1", + "expect" : + { + "name" : "Linux", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/macos.json b/test/data/ua/os/macos.json new file mode 100644 index 000000000..43f90dba9 --- /dev/null +++ b/test/data/ua/os/macos.json @@ -0,0 +1,38 @@ +[ + { + "desc" : "Mac OS on PowerPC", + "ua" : "Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC)", + "expect" : + { + "name" : "macOS", + "version" : "undefined" + } + }, + { + "desc" : "Mac OS X on x86, x86_64, or aarch64 using Firefox", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0", + "expect" : + { + "name" : "macOS", + "version" : "x.y" + } + }, + { + "desc" : "Mac OS X on PowerPC using Firefox", + "ua" : "Mozilla/5.0 (Macintosh; PPC Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0", + "expect" : + { + "name" : "macOS", + "version" : "x.y" + } + }, + { + "desc" : "Mac OS", + "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36", + "expect" : + { + "name" : "macOS", + "version" : "10.6.8" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/maemo.json b/test/data/ua/os/maemo.json new file mode 100644 index 000000000..9d898491f --- /dev/null +++ b/test/data/ua/os/maemo.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "Nokia N900 Linux mobile, on the Fennec browser", + "ua" : "Mozilla/5.0 (Maemo; Linux armv7l; rv:10.0) Gecko/20100101 Firefox/10.0 Fennec/10.0", + "expect" : + { + "name" : "Maemo", + "version" : "undefined" + } + }, + { + "desc" : "Nokia N900 Linux mobile, on the Maemo browser", + "ua" : "Mozilla/5.0(X11; U; Linux armv7l; fr-FR; rv:1.9.2a1pre) Gecko/20091127 Firefox/3.5 Maemo Browser 1.5.6 RX-51 N900", + "expect" : + { + "name" : "Maemo", + "version" : "undefined" + } + }, + { + "desc" : "Nokia N900 Linux mobile, on the Maemo browser", + "ua" : "Mozilla/5.0 (Linux; Maemo 5.0; Nokia N900; Build/4.0.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0 Mobile Safari/537.36", + "expect" : + { + "name" : "Maemo", + "version" : "5.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/mandriva.json b/test/data/ua/os/mandriva.json new file mode 100644 index 000000000..3dbccf782 --- /dev/null +++ b/test/data/ua/os/mandriva.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Mandriva", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.22) Gecko/20110907 Mandriva Linux/1.9.2.22-0.1mdv2010.2 (2010.2) Firefox/3.6.22", + "expect" : + { + "name" : "Mandriva", + "version" : "1.9.2.22-0.1mdv2010.2" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/manjaro.json b/test/data/ua/os/manjaro.json new file mode 100644 index 000000000..ede015f22 --- /dev/null +++ b/test/data/ua/os/manjaro.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Manjaro", + "ua" : "Mozilla/5.0 (X11; Manjaro 19.0.2; Arch; x64; rv:84.0) Gecko/20100101 Firefox/84.0", + "expect" : + { + "name" : "Manjaro", + "version" : "19.0.2" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/meego.json b/test/data/ua/os/meego.json new file mode 100644 index 000000000..a506cde7a --- /dev/null +++ b/test/data/ua/os/meego.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "MeeGo", + "ua" : "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", + "expect" : + { + "name" : "MeeGo", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/minix.json b/test/data/ua/os/minix.json new file mode 100644 index 000000000..d39a5148a --- /dev/null +++ b/test/data/ua/os/minix.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Minix", + "ua" : "Mozilla/5.0 (X11; Original ; Minix 3.3 ; rv:3.0)", + "expect" : + { + "name" : "Minix", + "version" : "3.3" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/mint.json b/test/data/ua/os/mint.json new file mode 100644 index 000000000..8493d1ac2 --- /dev/null +++ b/test/data/ua/os/mint.json @@ -0,0 +1,128 @@ +[ + { + "desc" : "Mint", + "ua" : "Opera/9.80 (X11; Linux x86_64; Edition Linux Mint) Presto/2.12.388 Version/12.16", + "expect" : + { + "name" : "Mint", + "version" : "undefined" + } + }, + { + "desc" : "Mint", + "ua" : "Opera/9.64 (X11; Linux i686; U; Linux Mint; nb) Presto/2.1.1", + "expect" : + { + "name" : "Mint", + "version" : "undefined" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121622 Linux Mint/6 (Felicia) Firefox/3.0.4", + "expect" : + { + "name" : "Mint", + "version" : "6" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121622 Linux Mint/6 (Felicia) Firefox/3.0.4", + "expect" : + { + "name" : "Mint", + "version" : "6" + } + }, + { + "desc" : "Mint", + "ua" : "Opera/9.80 (X11; Linux i686; Edition Linux Mint) Presto/2.12.388 Version/12.15", + "expect" : + { + "name" : "Mint", + "version" : "undefined" + } + }, + { + "desc" : "Mint", + "ua" : "Opera/9.51 (X11; Linux i686; U; Linux Mint; en)", + "expect" : + { + "name" : "Mint", + "version" : "undefined" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; Linux Mint/19.3 x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Brave Chrome/83.0.4103.97 Safari/537.36", + "expect" : + { + "name" : "Mint", + "version" : "19.3" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; Linux Mint/20 x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Kiwi Chrome/100.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Mint", + "version" : "20" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; Linux Mint/20.2; x86_64) Gecko/20100101 Firefox/92.0.1", + "expect" : + { + "name" : "Mint", + "version" : "20.2" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.9.0.10) Gecko/2009042513 Linux Mint/5 (Elyssa) Firefox/3.0.10", + "expect" : + { + "name" : "Mint", + "version" : "5" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.11) Gecko/2009060308 Linux Mint/7 (Gloria) Firefox/54.0", + "expect" : + { + "name" : "Mint", + "version" : "7" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.3) Gecko/20091020 Linux Mint/8 (Helena) Firefox/3.5.3", + "expect" : + { + "name" : "Mint", + "version" : "8" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:2.0) Gecko/20100101 Linux Mint 16/Petra Firefox/25.0.1.", + "expect" : + { + "name" : "Mint", + "version" : "16" + } + }, + { + "desc" : "Mint", + "ua" : "Mozilla/5.0 (Linux x86_64; Mint 21.3) AppleWebKit/537.36 (KHTML, like Gecko) Brave/128.0.0.0 Safari/537.36", + "expect" : + { + "name" : "Mint", + "version" : "21.3" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/morphos.json b/test/data/ua/os/morphos.json new file mode 100644 index 000000000..e540858d9 --- /dev/null +++ b/test/data/ua/os/morphos.json @@ -0,0 +1,38 @@ +[ + { + "desc" : "MorphOS", + "ua" : "AmigaVoyager/3.4.4 (MorphOS/PPC native)", + "expect" : + { + "name" : "MorphOS", + "version" : "undefined" + } + }, + { + "desc" : "MorphOS", + "ua" : "Mozilla/5.0 (Macintosh; PowerPC MorphOS 3.7; Odyssey Web Browser; rv:1.23) AppleWebKit/538.1 (KHTML, like Gecko) OWB/1.23 Safari/538.1", + "expect" : + { + "name" : "MorphOS", + "version" : "3.7" + } + }, + { + "desc" : "MorphOS", + "ua" : "Mozilla/5.0 (X11; MorphOS ppc64; rv:88.0) Gecko/20100101 Firefox/88.0", + "expect" : + { + "name" : "MorphOS", + "version" : "undefined" + } + }, + { + "desc" : "MorphOS", + "ua" : "Mozilla/5.0 (compatible; Origyn Web Browser; MorphOS; PPC; U) AppleWebKit/528.5+ (KHTML, like Gecko, Safari/528.5+)", + "expect" : + { + "name" : "MorphOS", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/netbsd.json b/test/data/ua/os/netbsd.json new file mode 100644 index 000000000..3a7657a27 --- /dev/null +++ b/test/data/ua/os/netbsd.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "NetBSD", + "ua" : "ELinks (0.4.3; NetBSD 3.0.2PATCH sparc64; 141x19)", + "expect" : + { + "name" : "NetBSD", + "version" : "3.0.2PATCH" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/netrange.json b/test/data/ua/os/netrange.json new file mode 100644 index 000000000..a7ff0a988 --- /dev/null +++ b/test/data/ua/os/netrange.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Netrange Smart TV", + "ua" : "Mozilla/5.0 (Linux; U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36 OPR/46.0.2207.0 LOEWE-SL410/5.2.0.0 HbbTV/1.4.1 (; LOEWE; SL410; LOH/5.2.0.0;;) FVC/3.0 (LOEWE; SL410;) CE-HTML/1.0 Config (L:deu,CC:DEU) NETRANGEMMH", + "expect" : + { + "name" : "NETRANGE", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/nettv.json b/test/data/ua/os/nettv.json new file mode 100644 index 000000000..2584e4096 --- /dev/null +++ b/test/data/ua/os/nettv.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "NetTV 3.2.1", + "ua" : "Opera/9.80 (Linux mips ; U; HbbTV/1.1.1 (; Philips; ; ; ; ) CE-HTML/1.0 NETTV/3.2.1; en) Presto/2.6.33 Version/10.70", + "expect" : + { + "name" : "NETTV", + "version" : "3.2.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/nintendo.json b/test/data/ua/os/nintendo.json new file mode 100644 index 000000000..b8ac8a398 --- /dev/null +++ b/test/data/ua/os/nintendo.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Nintendo Switch", + "ua" : "Mozilla/5.0 (Nintendo Switch; WifiWebAuthApplet) AppleWebKit/606.4 (KHTML, like Gecko) NF/6.0.1.15.4 NintendoBrowser/5.1.0.20393", + "expect" : + { + "name" : "Nintendo", + "version" : "Switch" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/openbsd.json b/test/data/ua/os/openbsd.json new file mode 100644 index 000000000..25164de54 --- /dev/null +++ b/test/data/ua/os/openbsd.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "OpenBSD", + "ua" : "Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.9.1) Gecko/20090702 Firefox/3.5", + "expect" : + { + "name" : "OpenBSD", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/openharmony.json b/test/data/ua/os/openharmony.json new file mode 100644 index 000000000..b0606a1c4 --- /dev/null +++ b/test/data/ua/os/openharmony.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "OpenHarmony", + "ua" : "Mozilla/5.0 (Phone; OpenHarmony 4.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ArkWeb/4.1.6.1 Mobile", + "expect" : + { + "name" : "OpenHarmony", + "version" : "4.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/os2.json b/test/data/ua/os/os2.json new file mode 100644 index 000000000..56eac7c3f --- /dev/null +++ b/test/data/ua/os/os2.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "OS/2", + "ua" : "Links (2.1pre14; OS/2 1 i386; 80x33)", + "expect" : + { + "name" : "OS/2", + "version" : "undefined" + } + }, + { + "desc" : "OS/2", + "ua" : "Mozilla/5.0 (OS/2; OS/2 i386) AppleWebKit/538.36 (KHTML, like Gecko) QtWebEngine/5.15.2 Chrome/127.0.6533.72", + "expect" : + { + "name" : "OS/2", + "version" : "undefined" + } + }, + { + "desc" : "OS/2", + "ua" : "Mozilla/5.0 (OS/2; Warp 4.5; rv:45.0) Gecko/20100101 Firefox/45.0", + "expect" : + { + "name" : "OS/2", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/palm.json b/test/data/ua/os/palm.json new file mode 100644 index 000000000..5fc831ebc --- /dev/null +++ b/test/data/ua/os/palm.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Palm OS", + "ua" : "Mozilla/4.76 [en] (PalmOS; U; WebPro3.0; Palm-Arz1)", + "expect" : + { + "name" : "Palm", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/pclinuxos.json b/test/data/ua/os/pclinuxos.json new file mode 100644 index 000000000..521b4be9b --- /dev/null +++ b/test/data/ua/os/pclinuxos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "PCLinuxOS", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.13) Gecko/20101209 PCLinuxOS/1.9.2.13-1pclos2010 (2010) Firefox/3.6.13", + "expect" : + { + "name" : "PCLinuxOS", + "version" : "1.9.2.13-1pclos2010" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/pico.json b/test/data/ua/os/pico.json new file mode 100644 index 000000000..456a5fa47 --- /dev/null +++ b/test/data/ua/os/pico.json @@ -0,0 +1,26 @@ +[ + { + "desc": "Pico 4", + "ua": "Mozilla/5.0 (X11; Linux x86_64; PICO 4 OS5.8.2 like Quest) AppleWebKit/537.36 (KHTML, like Gecko) PicoBrowser/3.3.38 Chrome/105.0.5195.68 VR Safari/537.36", + "expect": { + "name" : "PICO", + "version" : "5.8.2" + } + }, + { + "desc": "Pico 4", + "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", + "version" : "5.4.0" + } + }, + { + "desc": "Pico Neo3 Link", + "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", + "version" : "5.8.4.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/plan9.json b/test/data/ua/os/plan9.json new file mode 100644 index 000000000..e3726fb9a --- /dev/null +++ b/test/data/ua/os/plan9.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Plan9", + "ua" : "NCSA_Mosaic/5.0 (X11;Plan 9 4.0)", + "expect" : + { + "name" : "Plan 9", + "version" : "4.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/playstation.json b/test/data/ua/os/playstation.json new file mode 100644 index 000000000..38cf97fe4 --- /dev/null +++ b/test/data/ua/os/playstation.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "PlayStation 4", + "ua" : "Mozilla/5.0 (PlayStation 4 3.00) AppleWebKit/537.73 (KHTML, like Gecko)", + "expect" : + { + "name" : "PlayStation", + "version" : "4" + } + }, + { + "desc" : "PlayStation 5", + "ua" : "Mozilla/5.0 (PlayStation 5/SmartTV) AppleWebKit/605.1.15 (KHTML, like Gecko)", + "expect" : + { + "name" : "PlayStation", + "version" : "5" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/qnx.json b/test/data/ua/os/qnx.json new file mode 100644 index 000000000..2817e488f --- /dev/null +++ b/test/data/ua/os/qnx.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "QNX", + "ua" : "Mozilla/5.0 (Photon; U; QNX x86pc; en-US; rv:1.8.1.20) Gecko/20090127 BonEcho/2.0.0.20", + "expect" : + { + "name" : "QNX", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/raspbian.json b/test/data/ua/os/raspbian.json new file mode 100644 index 000000000..10fd9fb11 --- /dev/null +++ b/test/data/ua/os/raspbian.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Raspbian", + "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Raspbian Chromium/72.0.3626.121 HeadlessChrome/72.0.3626.121 Safari/537.36", + "expect" : + { + "name" : "Raspbian", + "version" : "undefined" + } + }, + { + "desc" : "Raspbian", + "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/538.15 (KHTML, like Gecko) Version/8.0 Safari/538.15 Raspbian/9.0 (1:3.8.2.0-0rpi28) Epiphany/3.8.2", + "expect" : + { + "name" : "Raspbian", + "version" : "9.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/redhat.json b/test/data/ua/os/redhat.json new file mode 100644 index 000000000..89f3e25bd --- /dev/null +++ b/test/data/ua/os/redhat.json @@ -0,0 +1,38 @@ +[ + { + "desc" : "RedHat", + "ua" : "Mozilla/5.0 (compatible; Konqueror/4.3; Linux) KHTML/4.3.4 (like Gecko) Red Hat Enterprise Linux/4.3.4-11.el6_1.4", + "expect" : + { + "name" : "Red Hat", + "version" : "4.3.4-11.el6_1.4" + } + }, + { + "desc" : "RedHat", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.13pre) Gecko/20070717 Red Hat/1.0.9-4.el4 SeaMonkey/1.0.9", + "expect" : + { + "name" : "Red Hat", + "version" : "1.0.9-4.el4" + } + }, + { + "desc" : "RedHat", + "ua" : "iTunes/4.7.1 (Linux; N; Red Hat; x86_64-linux; EN; utf8) SqueezeCenter, Squeezebox Server, Logitech Media Server/7.9.1/1522157629", + "expect" : + { + "name" : "Red Hat", + "version" : "undefined" + } + }, + { + "desc" : "RedHat", + "ua" : "curl/7.20.0 (x86_64-redhat-linux-gnu) libcurl/7.20.0 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5", + "expect" : + { + "name" : "redhat", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/rim-tablet-os.json b/test/data/ua/os/rim-tablet-os.json new file mode 100644 index 000000000..4451c3cda --- /dev/null +++ b/test/data/ua/os/rim-tablet-os.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "RIM Tablet OS", + "ua" : "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+", + "expect" : + { + "name" : "RIM Tablet OS", + "version" : "2.1.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/risc-os.json b/test/data/ua/os/risc-os.json new file mode 100644 index 000000000..5442784e3 --- /dev/null +++ b/test/data/ua/os/risc-os.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "RISC OS", + "ua" : "Mozilla/1.10 [en] (Compatible; RISC OS 3.70; Oregano 1.10)", + "expect" : + { + "name" : "RISC OS", + "version" : "3.70" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/sabayon.json b/test/data/ua/os/sabayon.json new file mode 100644 index 000000000..d44f9e2d3 --- /dev/null +++ b/test/data/ua/os/sabayon.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Sabayon", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Sabayon Chrome/19.0.1084.46 Safari/536.5", + "expect" : + { + "name" : "Sabayon", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/sailfish.json b/test/data/ua/os/sailfish.json new file mode 100644 index 000000000..f96376be4 --- /dev/null +++ b/test/data/ua/os/sailfish.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Sailfish", + "ua" : "Mozilla/5.0 (Linux; U; Sailfish 3.0; Mobile; rv:45.0) Gecko/45.0 Firefox/45.0 SailfishBrowser/1.0", + "expect" : + { + "name" : "Sailfish", + "version" : "3.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/serenityos.json b/test/data/ua/os/serenityos.json new file mode 100644 index 000000000..748546e39 --- /dev/null +++ b/test/data/ua/os/serenityos.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "SerenityOS", + "ua" : "Mozilla/4.0 (SerenityOS; x86) LibWeb+LibJS (Not KHTML, nor Gecko) LibWeb", + "expect" : + { + "name" : "SerenityOS", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/series40.json b/test/data/ua/os/series40.json new file mode 100644 index 000000000..1b02c1538 --- /dev/null +++ b/test/data/ua/os/series40.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Series40", + "ua" : "Mozilla/5.0 (Series40; Nokia2055/03.20; Profile/MIDP-2.1 Configuration/CLDC-1.1) Gecko/20100401 S40OviBrowser/2.2.0.0.34", + "expect" : + { + "name" : "Series40", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/slackware.json b/test/data/ua/os/slackware.json new file mode 100644 index 000000000..35f446e69 --- /dev/null +++ b/test/data/ua/os/slackware.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Slackware", + "ua" : "Mozilla/5.0 Slackware/13.37 (X11; U; Linux x86_64; en-US) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41", + "expect" : + { + "name" : "Slackware", + "version" : "13.37" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/solaris.json b/test/data/ua/os/solaris.json new file mode 100644 index 000000000..4c8f6739e --- /dev/null +++ b/test/data/ua/os/solaris.json @@ -0,0 +1,47 @@ +[ + { + "desc" : "Solaris", + "ua" : "Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.7) Gecko/20070606", + "expect" : + { + "name" : "Solaris", + "version" : "undefined" + } + }, + { + "desc" : "Solaris", + "ua" : "Mozilla/5.0 (Solaris; SPARC; w3m/0.5.3; rv:1.0) Gecko/20100101 w3m/0.5.3", + "expect" : + { + "name" : "Solaris", + "version" : "undefined" + } + }, + { + "desc" : "Solaris", + "ua" : "NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)", + "expect" : + { + "name" : "Solaris", + "version" : "4.1.4" + } + }, + { + "desc" : "Solaris", + "ua" : "Opera/5.0 (SunOS 5.8 sun4m; U) [en]", + "expect" : + { + "name" : "Solaris", + "version" : "5.8" + } + }, + { + "desc" : "Solaris", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.8) Gecko/20100215 Solaris/10.1 (GNU) Superswan/3.5.8 (Byte/me)", + "expect" : + { + "name" : "Solaris", + "version" : "10.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/suse.json b/test/data/ua/os/suse.json new file mode 100644 index 000000000..57f5f6989 --- /dev/null +++ b/test/data/ua/os/suse.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "OpenSUSE", + "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.17) Gecko/20110420 SUSE/3.6.17-0.2.1 Firefox/3.6.17", + "expect" : + { + "name" : "SUSE", + "version" : "3.6.17-0.2.1" + } + }, + { + "desc" : "SUSE", + "ua" : "Mozilla/5.0 (X11; SUSE; Linux amd64; rv:86.0) Gecko/20100101 Firefox/86.0", + "expect" : + { + "name" : "SUSE", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/symbian.json b/test/data/ua/os/symbian.json new file mode 100644 index 000000000..0e61d0281 --- /dev/null +++ b/test/data/ua/os/symbian.json @@ -0,0 +1,65 @@ +[ + { + "desc" : "Nokia 5250", + "ua" : "Nokia5250/10.0.011 (SymbianOS/9.4; U; Series60/5.0 Mozilla/5.0; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/525 (KHTML, like Gecko) Safari/525 3gpp-gba", + "expect" : + { + "name" : "Symbian", + "version" : "9.4" + } + }, + { + "desc" : "Nokia N79", + "ua" : "Mozilla/5.0 (SymbianOS/9.3; U; Series60/3.2 NokiaN79-1/32.001; Profile/MIDP-2.1 Configuration/CLDC-1", + "expect" : + { + "name" : "Symbian", + "version" : "9.3" + } + }, + { + "desc" : "Nokia E71", + "ua" : "Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 NokiaE71-1/110.07.127; Profile/MIDP-2.0 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413", + "expect" : + { + "name" : "Symbian", + "version" : "9.2" + } + }, + { + "desc" : "Opera Mini on S60", + "ua" : "Opera/9.80 (Series 60; Opera Mini/7.1.32444/191.361; U; de) Presto/2.12.423 Version/12.16", + "expect" : + { + "name" : "Symbian", + "version" : "undefined" + } + }, + { + "desc" : "NokiaBrowser on Nokia C7", + "ua" : "Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaC7-00/024.001; 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", + "expect" : + { + "name" : "Symbian", + "version" : "3" + } + }, + { + "desc" : "Nokia 808 PureView", + "ua" : "Mozilla/5.0 (Symbian/3; Series60/5.5 Nokia808PureView/113.010.1508; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/535.1 (KHTML, like Gecko) NokiaBrowser/8.3.2.21 Mobile Safari/535.1 3gpp-gba", + "expect" : + { + "name" : "Symbian", + "version" : "3" + } + }, + { + "desc" : "Nokia 808 PureView", + "ua" : "Mozilla/5.0 (Symbian; U; Nokia808 PureView; en-GB) AppleWebKit/534.3 (KHTML, like Gecko) Version/3.0 Mobile/1A543a Mobile Safari/534.3", + "expect" : + { + "name" : "Symbian", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/tizen.json b/test/data/ua/os/tizen.json new file mode 100644 index 000000000..4f9320d7c --- /dev/null +++ b/test/data/ua/os/tizen.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "Tizen", + "ua" : "Mozilla/5.0 (SMART-TV; Linux; Tizen 2.3) AppleWebkit/538.1 (KHTML, like Gecko) SamsungBrowser/1.0 TV Safari/538.1", + "expect" : + { + "name" : "Tizen", + "version" : "2.3" + } + }, + { + "desc" : "Tizen", + "ua" : "Mozilla/5.0 (Linux; Tizen 2.3; SAMSUNG SM-Z130H) AppleWebKit/537.3 (KHTML, like Gecko) Version/2.3 Mobile Safari/537.3", + "expect" : + { + "name" : "Tizen", + "version" : "2.3" + } + }, + { + "desc" : "Tizen 6.0", + "ua" : "HbbTV/1.5.1 (+DRM;Samsung;SmartTV2021:UAU7000;T-KSU2EDEUC-1506.0;KantSU2e;urn:samsungtv:familyname:21_KANTSU2E_UHD_BASIC:2021;) Tizen/6.0 (+TVPLUS+SmartHubLink) Chrome/76 LaTivu_1.0.1_2021 RVID/17", + "expect" : + { + "name" : "Tizen", + "version" : "6.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/ubuntu-touch.json b/test/data/ua/os/ubuntu-touch.json new file mode 100644 index 000000000..e0aea5ff2 --- /dev/null +++ b/test/data/ua/os/ubuntu-touch.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Ubuntu Touch", + "ua" : "Mozilla/5.0 (Linux; Ubuntu 16.04 like Android 4.4) AppleWebKit/537.36 Chromium/65.0.3325.151 Mobile Safari/537.36", + "expect" : + { + "name" : "Ubuntu Touch", + "version" : "16.04" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/ubuntu.json b/test/data/ua/os/ubuntu.json new file mode 100644 index 000000000..5438e8934 --- /dev/null +++ b/test/data/ua/os/ubuntu.json @@ -0,0 +1,47 @@ +[ + { + "desc" : "Ubuntu", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.22+ (KHTML, like Gecko) Chromium/17.0.963.56 Chrome/17.0.963.56 Safari/535.22+ Ubuntu/12.04 (3.4.1-0ubuntu1) Epiphany/3.4.1", + "expect" : + { + "name" : "Ubuntu", + "version" : "12.04" + } + }, + { + "desc" : "Ubuntu", + "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/31.0.1650.63 Chrome/31.0.1650.63 Safari/537.36", + "expect" : + { + "name" : "Ubuntu", + "version" : "undefined" + } + }, + { + "desc" : "Ubuntu", + "ua" : "Mozilla/5.0 (Wayland; Linux x86_64; Huawei) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Ubuntu/23.04 Edg/110.0.1587.41", + "expect" : + { + "name" : "Ubuntu", + "version" : "23.04" + } + }, + { + "desc" : "Ubuntu", + "ua" : "Mozilla/5.0 (X11; Ubuntu 20.04; Linux arm; rv:99.0) Gecko/20100101 Firefox/99.0", + "expect" : + { + "name" : "Ubuntu", + "version" : "20.04" + } + }, + { + "desc" : "Ubuntu", + "ua" : "Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16", + "expect" : + { + "name" : "Ubuntu", + "version" : "14.10" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/unix.json b/test/data/ua/os/unix.json new file mode 100644 index 000000000..18e733208 --- /dev/null +++ b/test/data/ua/os/unix.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "UNIX", + "ua" : "Surf/0.4.1 (X11; U; Unix; en-US) AppleWebKit/531.2+ Compatible (Safari)", + "expect" : + { + "name" : "Unix", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/watchos.json b/test/data/ua/os/watchos.json new file mode 100644 index 000000000..ec2c30b55 --- /dev/null +++ b/test/data/ua/os/watchos.json @@ -0,0 +1,29 @@ +[ + { + "desc" : "watchOS", + "ua" : "server-bag [Watch OS,8.4,19S546,Watch3,4]", + "expect" : + { + "name" : "watchOS", + "version" : "8.4" + } + }, + { + "desc" : "watchOS", + "ua" : "atc/1.0 watchOS/7.4.1 model/Watch3,3 hwp/t8004 build/18T201 (6; dt:155)", + "expect" : + { + "name" : "watchOS", + "version" : "7.4.1" + } + }, + { + "desc" : "watchOS", + "ua" : "Watch4,3/5.3.8 (16U680)", + "expect" : + { + "name" : "watchOS", + "version" : "5.3.8" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/webos.json b/test/data/ua/os/webos.json new file mode 100644 index 000000000..7f3d04ad4 --- /dev/null +++ b/test/data/ua/os/webos.json @@ -0,0 +1,119 @@ +[ + { + "desc" : "HP WebOS", + "ua" : "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.5; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/234.83 Safari/534.6 TouchPad/1.0", + "expect" : + { + "name" : "webOS", + "version" : "3.0.5" + } + }, + { + "desc" : "Palm WebOS", + "ua" : "Mozilla/5.0 (webOS/1.4.5; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.0", + "expect" : + { + "name" : "webOS", + "version" : "1.4.5" + } + }, + { + "desc" : "WebOS TV 22", + "ua" : " Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/87.0.4280.88 Safari/537.36 LG Browser/8.00.00(LGE; ST50K-LG2200WEB; 04.42.26; 0x00000001; DTV_C22P); webOS.TV-2022; LG NetCast.TV-2013 Compatible (LGE, ST50K-LG2200WEB, wireless)", + "expect" : + { + "name" : "webOS", + "version" : "22" + } + }, + { + "desc" : "WebOS TV 22", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 HbbTV/1.6.1 (+DRM; LGE/SILICONPLAYER/Hyundai; 50HYN3205; WEBOS22 04.42.26; W22_K8LP; DTV_C22P;)", + "expect" : + { + "name" : "webOS", + "version" : "22" + } + }, + { + "desc" : "WebOS TV 5.0", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/68.0.3440.106 Safari/537.36 LG Browser/8.00.00(LGE; SWU-6522; 04.50.63; 0x00000001; DTV_C20P); webOS.TV-2020; LG NetCast.TV-2013 Compatible (LGE, SWU-6522, wireless)", + "expect" : + { + "name" : "webOS", + "version" : "5" + } + }, + { + "desc" : "WebOS TV 5.0", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 HbbTV/1.5.1 (+DRM; LGE/WALTON/Hyundai; 50HYN3205; WEBOS5.0 04.50.63; W50_K6LP; DTV_C20P;)", + "expect" : + { + "name" : "webOS", + "version" : "5.0" + } + }, + { + "desc" : "WebOS TV 5.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 WebAppManager", + "expect" : + { + "name" : "webOS", + "version" : "5" + } + }, + { + "desc" : "WebOS TV 5.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chr0me/68.0.3440.106 Safari/537.36 LG Browser/8.00.00(LGE; E65A71B-S; 04.50.63; 0x00000001; DTV_C20P); webOS.TV-2020; LG NetCast.TV-2013 Compatible (LGE, E65A71B-S, wired)", + "expect" : + { + "name" : "webOS", + "version" : "5" + } + }, + { + "desc" : "WebOS TV 4.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.34 Safari/537.36 WebAppManager", + "expect" : + { + "name" : "webOS", + "version" : "4" + } + }, + { + "desc" : "WebOS TV 3.5", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 HbbTV/1.3.1 ( DRM; LGE; OLED55B7V-T; WEBOS3.5 06.10.60; W3_M16P; ) FVC/2.0 (LGE; WEBOS3.5 ;)", + "expect" : + { + "name" : "webOS", + "version" : "3.5" + } + }, + { + "desc" : "WebOS TV 3.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.2.1 Chrome/38.0.2125.122 Safari/537.36 WebAppManager", + "expect" : + { + "name" : "webOS", + "version" : "3" + } + }, + { + "desc" : "WebOS TV 2.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/538.2 (KHTML, like Gecko) Large Screen WebAppManager Safari/538.2", + "expect" : + { + "name" : "webOS", + "version" : "2" + } + }, + { + "desc" : "WebOS TV 1.x", + "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.41 (KHTML, like Gecko) Large Screen WebAppManager Safari/537.41", + "expect" : + { + "name" : "webOS", + "version" : "1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/windows-ce.json b/test/data/ua/os/windows-ce.json new file mode 100644 index 000000000..a28eb17fd --- /dev/null +++ b/test/data/ua/os/windows-ce.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Windows CE", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11)", + "expect" : + { + "name" : "Windows CE", + "version" : "undefined" + } + }, + { + "desc" : "Windows CE", + "ua" : "Mozilla/4.0 (PDA; Windows CE/1.0.1) NetFront/3.0", + "expect" : + { + "name" : "Windows CE", + "version" : "1.0.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/windows-iot.json b/test/data/ua/os/windows-iot.json new file mode 100644 index 000000000..ff1d02c05 --- /dev/null +++ b/test/data/ua/os/windows-iot.json @@ -0,0 +1,11 @@ +[ + { + "desc" : "Windows IoT", + "ua" : "Mozilla/5.0 (Windows IoT 10.0; Android 6.0.1; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Mobile Safari/537.36 Edge/18.17763", + "expect" : + { + "name" : "Windows IoT", + "version" : "10.0" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/windows-mobile.json b/test/data/ua/os/windows-mobile.json new file mode 100644 index 000000000..78df88b1a --- /dev/null +++ b/test/data/ua/os/windows-mobile.json @@ -0,0 +1,38 @@ +[ + { + "desc" : "Windows Mobile", + "ua" : "Mozilla/5.0 (ZTE-E_N72/N72V1.0.0B02;U;Windows Mobile/6.1;Profile/MIDP-2.0 Configuration/CLDC-1.1;320*240;CTC/2.0) IE/6.0 (compatible; MSIE 4.01; Windows CE; PPC)/UC Browser7.7.1.88", + "expect" : + { + "name" : "Windows Mobile", + "version" : "6.1" + } + }, + { + "desc" : "Windows Mobile", + "ua" : "Opera/9.80 (Windows Mobile; WCE; Opera Mobi/WMD-50433; U; en) Presto/2.4.13 Version/10.00", + "expect" : + { + "name" : "Windows Mobile", + "version" : "undefined" + } + }, + { + "desc" : "Windows Mobile", + "ua" : "Opera/9.7 (Windows Mobile; PPC; Opera Mobi/35166; U; en) Presto/2.2.1", + "expect" : + { + "name" : "Windows Mobile", + "version" : "undefined" + } + }, + { + "desc" : "Windows Mobile 6.1", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11) 320x240; VZW; Motorola-Q9c; Windows Mobile 6.1 Standard", + "expect" : + { + "name" : "Windows Mobile", + "version" : "6.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/windows-phone.json b/test/data/ua/os/windows-phone.json new file mode 100644 index 000000000..bf4bd0a26 --- /dev/null +++ b/test/data/ua/os/windows-phone.json @@ -0,0 +1,56 @@ +[ + { + "desc" : "Windows Phone", + "ua" : "Opera/9.80 (Windows Phone; Opera Mini/7.6.8/35.7518; U; ru) Presto/2.8.119 Version/11.10", + "expect" : + { + "name" : "Windows Phone", + "version" : "undefined" + } + }, + { + "desc" : "Windows Phone 6.5", + "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; HTC_Touch2_T3333; Windows Phone 6.5)", + "expect" : + { + "name" : "Windows Phone", + "version" : "6.5" + } + }, + { + "desc" : "Windows Phone 7.0", + "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; DELL; Venue Pro)", + "expect" : + { + "name" : "Windows Phone", + "version" : "7.0" + } + }, + { + "desc" : "Windows Phone 7.5", + "ua" : "Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; NOKIA; Lumia 800)", + "expect" : + { + "name" : "Windows Phone", + "version" : "7.5" + } + }, + { + "desc" : "Windows Phone 8", + "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; HTC; Windows Phone 8X by HTC)", + "expect" : + { + "name" : "Windows Phone", + "version" : "8.0" + } + }, + { + "desc" : "Windows Phone 8.1", + "ua" : "Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537", + "expect" : + { + "name" : "Windows Phone", + "version" : "8.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/windows-rt.json b/test/data/ua/os/windows-rt.json new file mode 100644 index 000000000..8bee15174 --- /dev/null +++ b/test/data/ua/os/windows-rt.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Windows RT 8", + "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)", + "expect" : + { + "name" : "Windows RT", + "version" : "8" + } + }, + { + "desc" : "Windows RT 8.1", + "ua" : "Mozilla/5.0 (Windows NT 6.3; ARM; Trident/7.0; Touch; rv:11.0) like Gecko", + "expect" : + { + "name" : "Windows RT", + "version" : "8.1" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/windows.json b/test/data/ua/os/windows.json new file mode 100644 index 000000000..faa965289 --- /dev/null +++ b/test/data/ua/os/windows.json @@ -0,0 +1,389 @@ +[ + { + "desc" : "Windows 3.1", + "ua" : "NCSA_Mosaic/2.0 (Windows 3.1)", + "expect" : + { + "name" : "Windows", + "version" : "3.1" + } + }, + { + "desc" : "Windows 3.1", + "ua" : "Mozilla/1.22 (compatible; MSIE 2.0; Windows 3.1)", + "expect" : + { + "name" : "Windows", + "version" : "3.1" + } + }, + { + "desc" : "Windows NT", + "ua" : "Mozilla/4.51 [de] (WinNT; I)", + "expect" : + { + "name" : "Windows", + "version" : "NT" + } + }, + { + "desc" : "Windows NT 3.51", + "ua" : "Mozilla/4.0 (compatible; MSIE 4.0; Windows NT)", + "expect" : + { + "name" : "Windows", + "version" : "NT" + } + }, + { + "desc" : "Windows NT 3.51", + "ua" : "Mozilla/4.0 (compatible; MSIE 5.05; Windows NT 3.51)", + "expect" : + { + "name" : "Windows", + "version" : "NT 3.51" + } + }, + { + "desc" : "Windows NT 4.0", + "ua" : "Opera/8.41.(Windows NT 4.0; ts-ZA) Presto/2.9.178 Version/11.00", + "expect" : + { + "name" : "Windows", + "version" : "NT 4.0" + } + }, + { + "desc" : "Windows NT 4.0", + "ua" : "Mozilla/5.0 (Windows; U; WinNT4.0; de-DE; rv:1.7.5) Gecko/20041108 Firefox/52.7.4", + "expect" : + { + "name" : "Windows", + "version" : "NT 4.0" + } + }, + { + "desc" : "Netscape on Windows 95", + "ua" : "Mozilla/5.0 (Windows; U; Win95; de-DE; rv:0.9.2) Gecko/20010726 Netscape6/6.1", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 95", + "ua" : "Mozilla/3.0 (Win95)", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 95", + "ua" : "Mozilla/3.0 (compatible; Opera/3.0; Windows 95/NT4) 3.2", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 95", + "ua" : "Mozilla/4.0 (compatible; MSIE 5.0; Windows 95) Opera 6.02 [en]", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 95", + "ua" : "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)", + "expect" : + { + "name" : "Windows", + "version" : "95" + } + }, + { + "desc" : "Windows 98", + "ua" : "Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)", + "expect" : + { + "name" : "Windows", + "version" : "98" + } + }, + { + "desc" : "Firebird on Windows 98", + "ua" : "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031007 Firebird/0.7", + "expect" : + { + "name" : "Windows", + "version" : "98" + } + }, + { + "desc" : "K-Meleon on Windows 98", + "ua" : "Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.5) Gecko/20031016 K-Meleon/0.8.2", + "expect" : + { + "name" : "Windows", + "version" : "98" + } + }, + { + "desc" : "Windows ME", + "ua" : "Mozilla/5.0 (Windows; U; Win 9x 4.90) Gecko/20020502 CS 2000 7.0/7.0", + "expect" : + { + "name" : "Windows", + "version" : "ME" + } + }, + { + "desc" : "Opera on Windows ME", + "ua" : "Mozilla/4.0 (compatible; MSIE 5.0; Windows ME) Opera 5.12 [de]", + "expect" : + { + "name" : "Windows", + "version" : "ME" + } + }, + { + "desc" : "Netscape on Windows ME", + "ua" : "Mozilla/5.0 (Windows; U; Win 9x 4.90; en-US; rv:1.8.1.8pre) Gecko/20071015 Firefox/2.0.0.7 Navigator/9.0", + "expect" : + { + "name" : "Windows", + "version" : "ME" + } + }, + { + "desc" : "Netscape on Windows 2000", + "ua" : "Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.5) Gecko/20050519 Netscape/8.0.1", + "expect" : + { + "name" : "Windows", + "version" : "2000" + } + }, + { + "desc" : "Opera on Windows 2000", + "ua" : "Opera/6.05 (Windows 2000; U)", + "expect" : + { + "name" : "Windows", + "version" : "2000" + } + }, + { + "desc" : "Opera on Windows 2000", + "ua" : "Opera/9.69 (Windows NT 5.01; en-US) Presto/2.8.160 Version/10.00", + "expect" : + { + "name" : "Windows", + "version" : "2000" + } + }, + { + "desc" : "Windows 2000", + "ua" : "Mozilla/3.0 (compatible; MSIE 3.0; Windows NT 5.0)", + "expect" : + { + "name" : "Windows", + "version" : "2000" + } + }, + { + "desc" : "Windows XP", + "ua" : "Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 5.2)", + "expect" : + { + "name" : "Windows", + "version" : "XP" + } + }, + { + "desc" : "Windows XP", + "ua" : "Mozilla/5.0 (Windows XP; U) Opera 6.05 [de]", + "expect" : + { + "name" : "Windows", + "version" : "XP" + } + }, + { + "desc" : "Windows Vista", + "ua" : "Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; fr-FR)", + "expect" : + { + "name" : "Windows", + "version" : "Vista" + } + }, + { + "desc" : "Windows 7", + "ua" : "Microsoft Windows 7", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "Windows 7", + "ua" : "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Evernote Windows/306387 (pt-PT, DDL); Windows/6.1.0 (Win32); Safari/537.36", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "Windows 7", + "ua" : "Mozilla/5.0 (Windows 7 Enterprise; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6099.71 Safari/537.36", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "Windows 7", + "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "Windows 8", + "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Win64; x64; Trident/6.0; .NET4.0E; .NET4.0C)", + "expect" : + { + "name" : "Windows", + "version" : "8" + } + }, + { + "desc" : "Windows 8.1", + "ua" : "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:66.0.5) Gecko/20100101 Firefox/66.0.5", + "expect" : + { + "name" : "Windows", + "version" : "8.1" + } + }, + { + "desc" : "Windows 10", + "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0", + "expect" : + { + "name" : "Windows", + "version" : "10" + } + }, + { + "desc" : "Windows Server 2012 R2", + "ua" : "Mozilla/5.0 (Windows Server 2012 R2 Standard; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5975.80 Safari/537.36", + "expect" : + { + "name" : "Windows", + "version" : "Server 2012 R2" + } + }, + { + "desc" : "WeChat Desktop for Windows Built-in Browser", + "ua" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 MicroMessenger/6.5.2.501 NetType/WIFI WindowsWechat QBCore/3.43.901.400 QQBrowser/9.0.2524.400", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "WeChat Desktop for Windows Built-in Browser major version in 4", + "ua" : "mozilla/5.0 (windows nt 6.1; wow64) applewebkit/537.36 (khtml, like gecko) chrome/81.0.4044.138 safari/537.36 nettype/wifi micromessenger/7.0.20.1781(0x6700143b) windowswechat", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "iTunes on Windows Vista", + "ua" : "iTunes/10.7 (Windows; Microsoft Windows Vista Home Premium Edition Service Pack 1 (Build 6001)) AppleWebKit/536.26.9", + "expect" : + { + "name" : "Windows", + "version" : "Vista" + } + }, + { + "desc" : "iTunes on Windows 7", + "ua" : "iTunes/10.6.3 (Windows; Microsoft Windows 7 x64 Business Edition Service Pack 1 (Build 7601)) AppleWebKit/534.57.2", + "expect" : + { + "name" : "Windows", + "version" : "7" + } + }, + { + "desc" : "iTunes on Windows 8", + "ua" : "iTunes/12.1.1 (Windows; Microsoft Windows 8 x64 Business Edition (Build 9200)) AppleWebKit/7600.1017.9000.2", + "expect" : + { + "name" : "Windows", + "version" : "8" + } + }, + { + "desc" : "iTunes on Windows 8.1", + "ua" : "iTunes/12.4 (Windows; Microsoft Windows 8.1 x64 Business Edition (Build 9200); x64) AppleWebKit/7601.6016.1000.1", + "expect" : + { + "name" : "Windows", + "version" : "8.1" + } + }, + { + "desc" : "iTunes on Windows 10", + "ua" : "iTunes/12.9.1 (Windows; Microsoft Windows 10 x64 Professional Edition (Build 18362); x64) AppleWebKit/7606.2104.0.21", + "expect" : + { + "name" : "Windows", + "version" : "10" + } + }, + { + "desc" : "iTunes on Windows 10", + "ua" : "iTunes/12.6.3 (Windows; Microsoft Windows 10.0 x64 (Build 17763); x64) AppleWebKit/7604.1038.1006.6", + "expect" : + { + "name" : "Windows", + "version" : "10" + } + }, + { + "desc" : "iTunes on Windows 10 S", + "ua" : "iTunes/12.12 (Windows; Microsoft Windows 10 S x64; x64) AppleWebKit/7613.2007", + "expect" : + { + "name" : "Windows", + "version" : "10" + } + }, + { + "desc" : "iTunes on Windows 11", + "ua" : "iTunes/12.13 (Windows; Microsoft Windows 11 x64; x64) AppleWebKit/7613.2007", + "expect" : + { + "name" : "Windows", + "version" : "11" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/xbox.json b/test/data/ua/os/xbox.json new file mode 100644 index 000000000..f8f9f6784 --- /dev/null +++ b/test/data/ua/os/xbox.json @@ -0,0 +1,47 @@ +[ + { + "desc" : "Xbox 360", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox 360) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36", + "expect" : + { + "name" : "Xbox", + "version" : "360" + } + }, + { + "desc" : "Xbox One", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041", + "expect" : + { + "name" : "Xbox", + "version" : "One" + } + }, + { + "desc" : "Xbox X", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 Edge/20.02", + "expect" : + { + "name" : "Xbox", + "version" : "X" + } + }, + { + "desc" : "Xbox Series X", + "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox Series X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 Edge/20.02 ", + "expect" : + { + "name" : "Xbox", + "version" : "Series X" + } + }, + { + "desc" : "Xbox Series S", + "ua" : "Mozilla/5.0 (Compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Xbox; Xbox Series S)", + "expect" : + { + "name" : "Xbox", + "version" : "Series S" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/xubuntu.json b/test/data/ua/os/xubuntu.json new file mode 100644 index 000000000..1f38a96ca --- /dev/null +++ b/test/data/ua/os/xubuntu.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Xubuntu", + "ua" : "Mozilla/5.0 (X11; Xubuntu Linux 21.10 x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.81 Safari/537.57 Vivaldi/5.1.2 (Blink)", + "expect" : + { + "name" : "Xubuntu", + "version" : "21.10" + } + }, + { + "desc" : "Xubuntu", + "ua" : "Mozilla/5.0 (X11; Xubuntu 14.04.5 LTS) AppleWebKit/537.36 (KHTML, like Gecko) Xubuntu Chrome/66.0.2623.87 Safari/537.36", + "expect" : + { + "name" : "Xubuntu", + "version" : "14.04.5" + } + } +] \ No newline at end of file diff --git a/test/data/ua/os/zenwalk.json b/test/data/ua/os/zenwalk.json new file mode 100644 index 000000000..170db50ea --- /dev/null +++ b/test/data/ua/os/zenwalk.json @@ -0,0 +1,20 @@ +[ + { + "desc" : "Zenwalk", + "ua" : "Flock/2.16 (Zenwalk 7.3; es_PR;)", + "expect" : + { + "name" : "Zenwalk", + "version" : "7.3" + } + }, + { + "desc" : "Zenwalk", + "ua" : "Mozilla/5.0 (X11; U; Linux i686; de; rv:1.9.2.13) Gecko/20101221 IceCat/3.6.13 (like Firefox/3.6.13) (Zenwalk GNU Linux)", + "expect" : + { + "name" : "Zenwalk", + "version" : "undefined" + } + } +] \ No newline at end of file diff --git a/test/e2e/browser.spec.mjs b/test/e2e/browser.spec.mjs new file mode 100644 index 000000000..848694c5c --- /dev/null +++ b/test/e2e/browser.spec.mjs @@ -0,0 +1,154 @@ +import { test, expect } from '@playwright/test'; +import path from 'path'; +import url from 'url'; + +const localHtml = `file://${path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '../../')}/dist/ua-parser.html`; +const browserMap = { + chromium: 'Chrome Headless', + firefox: 'Firefox', + webkit: 'Safari' +} + +test.describe('Custom navigator.userAgent tests', () => { + + test('Undefined navigator.userAgent regarded as an empty user-agent string', async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, 'userAgent', { + value: undefined + }); + }); + await page.goto(localHtml); + // @ts-ignore + const uap = await page.evaluate(async () => await UAParser()); + expect(uap).toHaveProperty('ua', ''); + expect(uap).toHaveProperty('browser.name', undefined); + }); +}); + +test.describe('User-defined user-agent tests', () => { + + test.beforeEach(async ({ page }) => { + await page.goto(localHtml); + }); + + test('Accept empty user-agent', async ({ page }) => { + // @ts-ignore + const uap = await page.evaluate(async () => await UAParser('')); + expect(uap).toHaveProperty('ua', ''); + }); + + test('Truncate very long user-agent', async ({ page }) => { + // @ts-ignore + const uap = await page.evaluate(async () => await UAParser('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; (R1 1.6); SLCC1; .NET CLR 2.0.50727; InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0; .NET CLR 3.5.30729; .NET CLR 3.0.30618; 66760635803; runtime 11.00294; 876906799603; 97880703; 669602703; 9778063903; 877905603; 89670803; 96690803; 8878091903; 7879040603; 999608065603; 799808803; 6666059903; 669602102803; 888809342903; 696901603; 788907703; 887806555703; 97690214703; 66760903; 968909903; 796802422703; 8868026703; 889803611803; 898706903; 977806408603; 976900799903; 9897086903; 88780803; 798802301603; 9966008603; 66760703; 97890452603; 9789064803; 96990759803; 99960107703; 8868087903; 889801155603; 78890703; 8898070603; 89970603; 89970539603; 89970488703; 8789007603; 87890903; 877904603; 9887077703; 798804903; 97890264603; 967901703; 87890703; 97690420803; 79980706603; 9867086703; 996602846703; 87690803; 6989010903; 977809603; 666601903; 876905337803; 89670603; 89970200903; 786903603; 696901911703; 788905703; 896709803; 96890703; 998601903; 88980703; 666604769703; 978806603; 7988020803; 996608803; 788903297903; 98770043603; 899708803; 66960371603; 9669088903; 69990703; 99660519903; 97780603; 888801803; 9867071703; 79780803; 9779087603; 899708603; 66960456803; 898706824603; 78890299903; 99660703; 9768079803; 977901591603; 89670605603; 787903608603; 998607934903; 799808573903; 878909603; 979808146703; 9996088603; 797803154903; 69790603; 99660565603; 7869028603; 896707703; 97980965603; 976907191703; 88680703; 888809803; 69690903; 889805523703; 899707703; 997605035603; 89970029803; 9699094903; 877906803; 899707002703; 786905857603; 69890803; 97980051903; 997603978803; 9897097903; 66960141703; 7968077603; 977804603; 88980603; 989700803; 999607887803; 78690772803; 96990560903; 98970961603; 9996032903; 9699098703; 69890655603; 978903803; 698905066803; 977806903; 9789061703; 967903747703; 976900550903; 88980934703; 8878075803; 8977028703; 97980903; 9769006603; 786900803; 98770682703; 78790903; 878906967903; 87690399603; 99860976703; 796805703; 87990603; 968906803; 967904724603; 999606603; 988705903; 989702842603; 96790603; 99760703; 88980166703; 9799038903; 98670903; 697905248603; 7968043603; 66860703; 66860127903; 9779048903; 89670123903; 78890397703; 97890603; 87890803; 8789030603; 69990603; 88880763703; 9769000603; 96990203903; 978900405903; 7869022803; 699905422903; 97890703; 87990903; 878908703; 7998093903; 898702507603; 97780637603; 966907903; 896702603; 9769004803; 7869007903; 99660158803; 7899099603; 8977055803; 99660603; 7889080903; 66660981603; 997604603; 6969089803; 899701903; 9769072703; 666603903; 99860803; 997608803; 69790903; 88680756703; 979805677903; 9986047703; 89970803; 66660603; 96690903; 8997051603; 789901209803; 8977098903; 968900326803; 87790703; 98770024803; 697901794603; 69990803; 887805925803; 968908903; 97880603; 897709148703; 877909476903; 66760197703; 977908603; 698902703; 988706504803; 977802026603; 88680964703; 8878068703; 987705107903; 978902878703; 8898069803; 9768031703; 79680803; 79980803; 669609328703; 89870238703; 99960593903; 969904218703; 78890603; 9788000703; 69690630903; 889800982903; 988709748803; 7968052803; 99960007803; 969900800803; 668604817603; 66960903; 78790734603; 8868007703; 79780034903; 8878085903; 976907603; 89670830803; 877900903; 969904889703; 7978033903; 8987043903; 99860703; 979805903; 667603803; 976805348603; 999604127603; 97790701603; 78990342903; 98770672903; 87990253903; 9877027703; 97790803; 877901895603; 8789076903; 896708595603; 997601903; 799806903; 97690603; 87790371703; 667605603; 99760303703; 97680283803; 788902750803; 787909803; 79780603; 79880866903; 9986050903; 87890543903; 979800803; 97690179703; 876901603; 699909903; 96990192603; 878904903; 877904734903; 796801446903; 977904803; 9887044803; 797805565603; 98870789703; 7869093903; 87790727703; 797801232803; 666604803; 9778071903; 9799086703; 6969000903; 89670903; 8799075903; 897708903; 88680903; 97980362603; 97980503903; 889803256703; 88980388703; 789909376803; 69690703; 6969025903; 89970309903; 96690703; 877901847803; 968901903; 96690603; 88680607603; 7889001703; 789904761803; 976807703; 976902903; 878907889703; 9897014903; 896707046603; 696909903; 666603998903; 969902703; 79680421803; 9769075603; 798800192703; 97990903; 9689024903; 668604803; 969908671903; 9996094703; 69990642703; 97890895903; 977805619903; 79980859903; 88980443803; 98970649603; 997602703; 888802169903; 699907803; 667602028803; 786903283903; 997607703; 969909803; 798809925903; 9976045603; 97790903; 9789001903; 966903603; 9789069603; 968906603; 6989091803; 896701603; 6979059803; 978803903; 997606362603; 88980803; 98970803; 88880921703; 8997065703; 899700703; 698908703; 797801027903; 7889050903; 87890603; 78690703; 99660069703; 97980309903; 976800603; 666606803; 898707703; 79880019803; 66960250803; 7978049803; 88780602603; 79680903; 88880792703; 96990903; 667608603; 87790730903; 98970903; 9699032903; 8987004803; 88880703; 89770046603; 978800803; 969908903; 9798022603; 696901903; 799803703; 989703703; 668605903; 79780903; 998601371703; 796803339703; 87890922603; 898708903; 9966061903; 66960891903; 96790903; 8779050803; 98870858803; 976909298603; 9887029903; 669608703; 979806903; 878903803; 99960703; 9789086703; 979801803; 66960008703; 979806830803; 99760212703; 786906603; 797807603; 789907297703; 96990703; 786901603; 796807766603; 896702651603; 789902585603; 66660925903; 9986085703; 66960302703; 69890703; 789900703; 89970903; 9679060703; 9789002903; 979908821603; 986708140803; 976809828703; 7988082803; 79680997903; 99960803; 9788081903; 979805703; 787908603; 66960602803; 9887098703; 978803237703; 888806804603; 999604703; 977904703; 966904635703; 97680291703; 977809345603; 8878046703; 988709803; 976900773603; 989703903; 88780198603; 87790603; 986708703; 78890604703; 87790544803; 976809850903; 887806703; 987707527603; 79880803; 9897059603; 897709820603; 97880804803; 66960026703; 9789062803; 9867090803; 669600603; 8967087703; 78890903; 89770903; 97980703; 976802687603; 66860400803; 979901288603; 96990160903; 99860228903; 966900703; 66760603; 9689035703; 9779064703; 7968023603; 87890791903; 98770870603; 9798005803; 6969087903; 9779097903; 6979065703; 699903252603; 79780989703; 87690901803; 978905763903; 977809703; 97790369703; 899703269603; 8878012703; 78790803; 87690395603; 8888042803; 667607689903; 8977041803; 6666085603; 6999080703; 69990797803; 88680721603; 99660519803; 889807603; 87890146703; 699906325903; 89770603; 669608615903; 9779028803; 88880603; 97790703; 79780703; 97680355603; 6696024803; 78790784703; 97880329903; 9699077703; 89870803; 79680227903; 976905852703; 8997098903; 896704796703; 66860598803; 9897036703; 66960703; 9699094703; 9699008703; 97780485903; 999603179903; 89770834803; 96790445603; 79680460903; 9867009603; 89870328703; 799801035803; 989702903; 66960758903; 66860150803; 6686088603; 9877092803; 96990603; 99860603; 987703663603; 98870903; 699903325603; 87790803; 97680703; 8868030703; 9799030803; 89870703; 97680803; 9669054803; 6979097603; 987708046603; 999608603; 878904803; 998607408903; 968903903; 696900703; 977907491703; 6686033803; 669601803; 99960290603; 887809169903; 979803703; 69890903; 699901447903; 8987064903; 799800603; 98770903; 8997068703; 967903603; 66760146803; 978805087903; 697908138603; 799801603; 88780964903; 989708339903; 8967048603; 88880981603; 789909703; 796806603; 977905977603; 989700603; 97780703; 9669062603; 88980714603; 897709545903; 988701916703; 667604694903; 786905664603; 877900803; 886805490903; 89970559903; 99960531803; 7998033903; 98770803; 78890418703; 669600872803; 996605216603; 78690962703; 667604903; 996600903; 999608903; 9699083803; 787901803; 97780707603; 787905312703; 977805803; 8977033703; 97890708703; 989705521903; 978800703; 698905703; 78890376903; 878907703; 999602903; 986705903; 668602719603; 979901803; 997606903; 66760393903; 987703603; 78790338903; 96890803; 97680596803; 666601603; 977902178803; 877902803; 78790038603; 8868075703; 99960060603)')); + + expect(uap).toHaveProperty('ua', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; (R1 1.6); SLCC1; .NET CLR 2.0.50727; InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0; .NET CLR 3.5.30729; .NET CLR 3.0.30618; 66760635803; runtime 11.00294; 876906799603; 97880703; 669602703; 9778063903; 877905603; 89670803; 96690803; 8878091903; 7879040603; 999608065603; 799808803; 6666059903; 669602102803; 888809342903; 696901603; 788907703; 887806555703; 97690214703; 66760903; 968909903; 796802422703; 8868026703; 8898036'); + }); +}); + +test.describe('withClientHints() tests', () => { + + test('Detect custom Client Hints data', async ({ page, browserName }) => { + await page.addInitScript((browserName) => { + if (browserName == 'chromium') { + Object.defineProperty(navigator, 'userAgentData', { + value: { + brands: [], + platform: '', + mobile: false, + getHighEntropyValues: () => { + return Promise.resolve({ + brands: [ + { + brand: 'Chromium', + version: '110' + }, + { + brand: 'Not(A:Brand', + version: '110' + }, + { + brand: 'New Browser', + version: '110' + } + ], + platform: 'New OS', + formFactors: 'New Form Factor' + }); + } + } + }); + } + }, browserName); + await page.goto(localHtml); + // @ts-ignore + const uap = await page.evaluate(async () => await UAParser().withClientHints()); + if (browserName == 'chromium') { + expect(uap).toHaveProperty('browser.name', 'New Browser'); + expect(uap).toHaveProperty('os.name', 'New OS'); + expect(uap).toHaveProperty('device.type', undefined); + } else { + expect(uap).toHaveProperty('browser.name', browserMap[browserName]); + } + }); +}); + +test.describe('withFeatureCheck() tests', () => { + + test('Detect Brave', async ({ page, browserName }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, 'brave', { + value: { + isBrave: () => true + } + }); + }); + await page.goto(localHtml); + // @ts-ignore + let uap = await page.evaluate(() => UAParser()); + expect(uap).toHaveProperty('browser.name', browserMap[browserName]); + // @ts-ignore + uap = await page.evaluate(() => UAParser().withFeatureCheck()); + expect(uap).toHaveProperty('browser.name', 'Brave'); + }); + + + test('Detect iPad', async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(navigator, 'userAgent', { + value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15' + }); + Object.defineProperty(navigator, 'standalone', { + value: true + }); + Object.defineProperty(navigator, 'maxTouchPoints', { + value: 3 + }); + }); + await page.goto(localHtml); + // @ts-ignore + let uap = await page.evaluate(() => UAParser()); + expect(uap).toHaveProperty('device.model', 'Macintosh'); + expect(uap).toHaveProperty('device.type', undefined); + // @ts-ignore + uap = await page.evaluate(() => UAParser().withFeatureCheck()); + expect(uap).toHaveProperty('device.model', 'iPad'); + expect(uap).toHaveProperty('device.type', 'tablet'); + }); +}); + +test.describe('request.headers can be passed in form of a Headers object', () => { + + test('Headers automatically converted into a plain key-value object', async ({ page }) => { + await page.addInitScript(() => { + Object.defineProperty(window, 'req', { + value : { + headers: new Headers([["User-Agent", "myBrowser/1.0"]]) + } + }); + }); + await page.goto(localHtml); + // @ts-ignore + const uap = await page.evaluate(() => UAParser(req.headers)); + expect(uap.ua).toBe('myBrowser/1.0'); + }); +}); diff --git a/test/fuzz/redos.js b/test/fuzz/redos.js new file mode 100644 index 000000000..b04686218 --- /dev/null +++ b/test/fuzz/redos.js @@ -0,0 +1,20 @@ +const { FuzzedDataProvider } = require('@jazzer.js/core'); +const { UAParser } = require('../../dist/main'); +const UA_MAX_LENGTH = 500; + +module.exports.fuzz = function (buffer) { + const data = new FuzzedDataProvider(buffer); + const userAgent = data.consumeString(UA_MAX_LENGTH, 'utf-8', true); + const start = process.hrtime(); + + UAParser(userAgent); + + const elapsed = process.hrtime(start); + const milisec = (elapsed[0]*1e3+elapsed[1]*1e-6).toFixed(3); + if (milisec > 1000) { + throw new Error( + `Potential ReDoS\n` + + `Time taken: ${milisec} ms.\n` + + `User agent: ${userAgent}`); + } +}; \ No newline at end of file diff --git a/test/os-test.json b/test/os-test.json deleted file mode 100644 index 57a4d8a95..000000000 --- a/test/os-test.json +++ /dev/null @@ -1,1127 +0,0 @@ -[ - { - "desc" : "Windows 95", - "ua" : "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)", - "expect" : - { - "name" : "Windows", - "version" : "95" - } - }, - { - "desc" : "Windows 98", - "ua" : "Mozilla/4.0 (compatible; MSIE 4.01; Windows 98)", - "expect" : - { - "name" : "Windows", - "version" : "98" - } - }, - { - "desc" : "Windows ME", - "ua" : "Mozilla/5.0 (Windows; U; Win 9x 4.90) Gecko/20020502 CS 2000 7.0/7.0", - "expect" : - { - "name" : "Windows", - "version" : "ME" - } - }, - { - "desc" : "Windows 2000", - "ua" : "Mozilla/3.0 (compatible; MSIE 3.0; Windows NT 5.0)", - "expect" : - { - "name" : "Windows", - "version" : "2000" - } - }, - { - "desc" : "Windows XP", - "ua" : "Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 5.2)", - "expect" : - { - "name" : "Windows", - "version" : "XP" - } - }, - { - "desc" : "Windows Vista", - "ua" : "Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; fr-FR)", - "expect" : - { - "name" : "Windows", - "version" : "Vista" - } - }, - { - "desc" : "Windows 7", - "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", - "expect" : - { - "name" : "Windows", - "version" : "7" - } - }, - { - "desc" : "Windows 8", - "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Win64; x64; Trident/6.0; .NET4.0E; .NET4.0C)", - "expect" : - { - "name" : "Windows", - "version" : "8" - } - }, - { - "desc" : "Windows 10", - "ua" : "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0", - "expect" : - { - "name" : "Windows", - "version" : "10" - } - }, - { - "desc" : "Windows RT", - "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; ARM; Trident/6.0)", - "expect" : - { - "name" : "Windows", - "version" : "RT" - } - }, - { - "desc" : "Windows CE", - "ua" : "Mozilla/4.0 (compatible; MSIE 6.0; Windows CE; IEMobile 7.11)", - "expect" : - { - "name" : "Windows", - "version" : "CE" - } - }, - { - "desc" : "Windows Mobile", - "ua" : "Mozilla/5.0 (ZTE-E_N72/N72V1.0.0B02;U;Windows Mobile/6.1;Profile/MIDP-2.0 Configuration/CLDC-1.1;320*240;CTC/2.0) IE/6.0 (compatible; MSIE 4.01; Windows CE; PPC)/UC Browser7.7.1.88", - "expect" : - { - "name" : "Windows Mobile", - "version" : "6.1" - } - }, - { - "desc" : "Windows Mobile", - "ua" : "Opera/9.80 (Windows Mobile; WCE; Opera Mobi/WMD-50433; U; en) Presto/2.4.13 Version/10.00", - "expect" : - { - "name" : "Windows Mobile", - "version" : "undefined" - } - }, - { - "desc" : "Windows Phone", - "ua" : "Opera/9.80 (Windows Phone; Opera Mini/7.6.8/35.7518; U; ru) Presto/2.8.119 Version/11.10", - "expect" : - { - "name" : "Windows Phone", - "version" : "undefined" - } - }, - { - "desc" : "Windows Phone OS", - "ua" : "Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; DELL; Venue Pro)", - "expect" : - { - "name" : "Windows Phone OS", - "version" : "7.0" - } - }, - { - "desc" : "Windows Phone 8", - "ua" : "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; HTC; Windows Phone 8X by HTC)", - "expect" : - { - "name" : "Windows Phone", - "version" : "8.0" - } - }, - { - "desc" : "Windows NT on x86 or aarch64 CPU using Firefox", - "ua" : "Mozilla/5.0 (Windows NT x.y; rv:10.0) Gecko/20100101 Firefox/10.0", - "expect" : - { - "name" : "Windows", - "version" : "NT x" - } - }, - { - "desc" : "Windows NT on x64 CPU using Firefox", - "ua" : "Mozilla/5.0 (Windows NT x.y; Win64; x64; rv:10.0) Gecko/20100101 Firefox/10.0", - "expect" : - { - "name" : "Windows", - "version" : "NT x" - } - }, - { - "desc" : "BlackBerry", - "ua" : "BlackBerry9300/5.0.0.912 Profile/MIDP-2.1 Configuration/CLDC-1.1 VendorID/378", - "expect" : - { - "name" : "BlackBerry", - "version" : "5.0.0.912" - } - }, - { - "desc" : "BlackBerry 10", - "ua" : "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.3+ (KHTML, like Gecko) Version/10.0.9.386 Mobile Safari/537.3+", - "expect" : - { - "name" : "BlackBerry", - "version" : "10" - } - }, - { - "desc" : "Tizen", - "ua" : "Mozilla/5.0 (SMART-TV; Linux; Tizen 2.3) AppleWebkit/538.1 (KHTML, like Gecko) SamsungBrowser/1.0 TV Safari/538.1", - "expect" : - { - "name" : "Tizen", - "version" : "2.3" - } - }, - { - "desc" : "Tizen", - "ua" : "Mozilla/5.0 (Linux; Tizen 2.3; SAMSUNG SM-Z130H) AppleWebKit/537.3 (KHTML, like Gecko) Version/2.3 Mobile Safari/537.3", - "expect" : - { - "name" : "Tizen", - "version" : "2.3" - } - }, - { - "desc" : "Android", - "ua" : "Mozilla/5.0 (Linux; U; Android 2.2.2; en-us; VM670 Build/FRG83G) AppleWebKit/533.1 (KHTML, like Gecko)", - "expect" : - { - "name" : "Android", - "version" : "2.2.2" - } - }, - { - "desc" : "HarmonyOS", - "ua" : "Mozilla/5.0 (Linux; Android 10; HarmonyOS; YAL-AL10; HMSCore 6.3.0.327; GMSCore 21.48.15) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.105 HuaweiBrowser/12.0.3.310 Mobile Safari/537.36", - "expect" : - { - "name" : "HarmonyOS", - "version" : "10" - } - }, - { - "desc" : "Sailfish", - "ua" : "Mozilla/5.0 (Linux; U; Sailfish 3.0; Mobile; rv:45.0) Gecko/45.0 Firefox/45.0 SailfishBrowser/1.0", - "expect" : - { - "name" : "Sailfish", - "version" : "3.0" - } - }, - { - "desc" : "WebOS", - "ua" : "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.5; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/234.83 Safari/534.6 TouchPad/1.0", - "expect" : - { - "name" : "webOS", - "version" : "3.0.5" - } - }, - { - "desc" : "WebOS", - "ua" : "Mozilla/5.0 (webOS/1.4.5; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.0", - "expect" : - { - "name" : "webOS", - "version" : "1.4.5" - } - }, - { - "desc" : "WebOS TV 5.x", - "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 WebAppManager", - "expect" : - { - "name" : "webOS", - "version" : "TV" - } - }, - { - "desc" : "WebOS TV 4.x", - "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.34 Safari/537.36 WebAppManager", - "expect" : - { - "name" : "webOS", - "version" : "TV" - } - }, - { - "desc" : "WebOS TV 3.x", - "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.2.1 Chrome/38.0.2125.122 Safari/537.36 WebAppManager", - "expect" : - { - "name" : "webOS", - "version" : "TV" - } - }, - { - "desc" : "WebOS TV 2.x", - "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/538.2 (KHTML, like Gecko) Large Screen WebAppManager Safari/538.2", - "expect" : - { - "name" : "webOS", - "version" : "TV" - } - }, - { - "desc" : "WebOS TV 1.x", - "ua" : "Mozilla/5.0 (Web0S; Linux/SmartTV) AppleWebKit/537.41 (KHTML, like Gecko) Large Screen WebAppManager Safari/537.41", - "expect" : - { - "name" : "webOS", - "version" : "TV" - } - }, - { - "desc" : "QNX", - "ua" : "Mozilla/5.0 (Photon; U; QNX x86pc; en-US; rv:1.8.1.20) Gecko/20090127 BonEcho/2.0.0.20", - "expect" : - { - "name" : "QNX", - "version" : "x86pc" - } - }, - { - "desc" : "Bada", - "ua" : "Mozilla/5.0 (SAMSUNG; SAMSUNG-GT-S5253/S5253DDKC1; U; Bada/1.0; en-us) AppleWebKit/533.1 (KHTML, like Gecko) Dolfin/2.0 Mobile WQVGA SMM-MMS/1.2.0 OPN-B", - "expect" : - { - "name" : "Bada", - "version" : "1.0" - } - }, - { - "desc" : "RIM Tablet OS", - "ua" : "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/7.2.1.0 Safari/536.2+", - "expect" : - { - "name" : "RIM Tablet OS", - "version" : "2.1.0" - } - }, - { - "desc" : "Nokia N900 Linux mobile, on the Fennec browser", - "ua" : "Mozilla/5.0 (Maemo; Linux armv7l; rv:10.0) Gecko/20100101 Firefox/10.0 Fennec/10.0", - "expect" : - { - "name" : "Maemo", - "version" : "undefined" - } - }, - { - "desc" : "MeeGo", - "ua" : "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13", - "expect" : - { - "name" : "MeeGo", - "version" : "undefined" - } - }, - { - "desc" : "Symbian", - "ua" : "Nokia5250/10.0.011 (SymbianOS/9.4; U; Series60/5.0 Mozilla/5.0; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/525 (KHTML, like Gecko) Safari/525 3gpp-gba", - "expect" : - { - "name" : "Symbian", - "version" : "9.4" - } - }, - { - "desc" : "Symbian", - "ua" : "Mozilla/5.0 (Symbian/3; Series60/5.2 NokiaC7-00/024.001; 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", - "expect" : - { - "name" : "Symbian", - "version" : "5.2" - } - }, - { - "desc" : "Series40", - "ua" : "Mozilla/5.0 (Series40; Nokia2055/03.20; Profile/MIDP-2.1 Configuration/CLDC-1.1) Gecko/20100401 S40OviBrowser/2.2.0.0.34", - "expect" : - { - "name" : "Series40", - "version" : "undefined" - } - }, - { - "desc" : "Firefox OS", - "ua" : "Mozilla/5.0 (Mobile; rv:14.0) Gecko/14.0 Firefox/14.0", - "expect" : - { - "name" : "Firefox OS", - "version" : "14.0" - } - }, - { - "desc" : "Firefox OS on Tablet", - "ua" : "Mozilla/5.0 (Tablet; rv:26.0) Gecko/26.0 Firefox/26.0", - "expect" : - { - "name" : "Firefox OS", - "version" : "26.0" - } - }, - { - "desc" : "Firefox OS on TV", - "ua" : "Mozilla/5.0 (TV; rv:44.0) Gecko/44.0 Firefox/44.0", - "expect" : - { - "name" : "Firefox OS", - "version" : "44.0" - } - }, - { - "desc" : "Google Chromecast", - "ua" : "Mozilla/5.0 (X11; Linux aarch64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.81 Safari/537.36 CrKey/1.42.183786", - "expect" : - { - "name" : "Chromecast", - "version" : "1.42.183786" - } - }, - { - "desc" : "Nintendo Switch", - "ua" : "Mozilla/5.0 (Nintendo Switch; WifiWebAuthApplet) AppleWebKit/606.4 (KHTML, like Gecko) NF/6.0.1.15.4 NintendoBrowser/5.1.0.20393", - "expect" : - { - "name" : "Nintendo", - "version" : "Switch" - } - }, - { - "desc" : "PlayStation 4", - "ua" : "Mozilla/5.0 (PlayStation 4 3.00) AppleWebKit/537.73 (KHTML, like Gecko)", - "expect" : - { - "name" : "PlayStation", - "version" : "4" - } - }, - { - "desc" : "Xbox 360", - "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox 360) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36", - "expect" : - { - "name" : "Xbox", - "version" : "360" - } - }, - { - "desc" : "Xbox One", - "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox One; WebView/3.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041", - "expect" : - { - "name" : "Xbox", - "version" : "One" - } - }, - { - "desc" : "Xbox X", - "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 Edge/20.02", - "expect" : - { - "name" : "Xbox", - "version" : "X" - } - }, - { - "desc" : "Xbox Series X", - "ua" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; Xbox; Xbox Series X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.82 Safari/537.36 Edge/20.02 ", - "expect" : - { - "name" : "Xbox", - "version" : "Series X" - } - }, - { - "desc" : "Mint", - "ua" : "Opera/9.80 (X11; Linux x86_64; Edition Linux Mint) Presto/2.12.388 Version/12.16", - "expect" : - { - "name" : "Mint", - "version" : "undefined" - } - }, - { - "desc" : "Mint", - "ua" : "Opera/9.64 (X11; Linux i686; U; Linux Mint; nb) Presto/2.1.1", - "expect" : - { - "name" : "Mint", - "version" : "undefined" - } - }, - { - "desc" : "Mint", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.5) Gecko/2008121622 Linux Mint/6 (Felicia) Firefox/3.0.4", - "expect" : - { - "name" : "Mint", - "version" : "6" - } - }, - { - "desc" : "Ubuntu", - "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.22+ (KHTML, like Gecko) Chromium/17.0.963.56 Chrome/17.0.963.56 Safari/535.22+ Ubuntu/12.04 (3.4.1-0ubuntu1) Epiphany/3.4.1", - "expect" : - { - "name" : "Ubuntu", - "version" : "12.04" - } - }, - { - "desc" : "Ubuntu", - "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/31.0.1650.63 Chrome/31.0.1650.63 Safari/537.36", - "expect" : - { - "name" : "Ubuntu", - "version" : "undefined" - } - }, - { - "desc" : "Kubuntu", - "ua" : "Mozilla/5.0 (compatible; Konqueror/4.4; Linux 2.6.32-22-generic; X11; en_US) KHTML/4.4.3 (like Gecko) Kubuntu", - "expect" : - { - "name" : "Kubuntu", - "version" : "undefined" - } - }, - { - "desc" : "Debian", - "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.7 (like Gecko) (Debian)", - "expect" : - { - "name" : "Debian", - "version" : "undefined" - } - }, - { - "desc" : "Debian", - "ua" : "Mozilla/5.0 (X11; Linux x86_64; Debian GNU/Linux 8.1 (jessie)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.0 Maxthon/1.0.5.3 Safari/537.36", - "expect" : - { - "name" : "Debian", - "version" : "8.1" - } - }, - { - "desc" : "Debian", - "ua" : "ELinks/0.12~pre5-4 (textmode; Debian; Linux 3.2.0-4-amd64 x86_64 192x47-2)", - "expect" : - { - "name" : "Debian", - "version" : "undefined" - } - }, - { - "desc" : "Debian", - "ua" : "w3m/0.5.3+debian-19", - "expect" : - { - "name" : "debian", - "version" : "19" - } - }, - { - "desc" : "Debian", - "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.3) Gecko/2008092814 (Debian-3.0.1-1)", - "expect" : - { - "name" : "Debian", - "version" : "3.0.1-1" - } - }, - { - "desc" : "Debian", - "ua" : "Mozilla/5.0 (compatible; Konqueror/3.5; Linux 2.6.24.4; X11) KHTML/3.5.9 (like Gecko) (Debian package 4:3.5.9.dfsg.1-2+b1)", - "expect" : - { - "name" : "Debian", - "version" : "undefined" - } - }, - { - "desc" : "OpenSUSE", - "ua" : "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.17) Gecko/20110420 SUSE/3.6.17-0.2.1 Firefox/3.6.17", - "expect" : - { - "name" : "SUSE", - "version" : "3.6.17-0.2.1" - } - }, - { - "desc" : "Gentoo", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.16) Gecko/20080716 (Gentoo) Galeon/2.0.6", - "expect" : - { - "name" : "Gentoo", - "version" : "undefined" - } - }, - { - "desc" : "Gentoo", - "ua" : "Xombrero (X11; U; Gentoo Linux amd64; en-US) Webkit/2.8.5", - "expect" : - { - "name" : "Gentoo", - "version" : "amd64" - } - }, - { - "desc" : "Gentoo", - "ua" : "Xombrero/1.6.4 (Linux amd64; en; Gentoo)", - "expect" : - { - "name" : "Gentoo", - "version" : "undefined" - } - }, - { - "desc" : "Gentoo", - "ua" : "Links (2.8; Linux 3.17.2-gentoo-x86 i686; GNU C 4.8.2; x)", - "expect" : - { - "name" : "gentoo", - "version" : "x86" - } - }, - { - "desc" : "Arch", - "ua" : "Uzbl (Webkit 1.1.10) (Arch Linux)", - "expect" : - { - "name" : "Arch", - "version" : "undefined" - } - }, - { - "desc" : "Slackware", - "ua" : "Mozilla/5.0 Slackware/13.37 (X11; U; Linux x86_64; en-US) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.41", - "expect" : - { - "name" : "Slackware", - "version" : "13.37" - } - }, - { - "desc" : "Fedora", - "ua" : "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0", - "expect" : - { - "name" : "Fedora", - "version" : "undefined" - } - }, - { - "desc" : "Fedora", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:2.0) Gecko/20110404 Fedora/16-dev Firefox/4.0", - "expect" : - { - "name" : "Fedora", - "version" : "16-dev" - } - }, - { - "desc" : "Fedora", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; sk; rv:1.9.0.4) Gecko/2008111217 Fedora/3.0.4-1.fc10 Firefox/3.0.4", - "expect" : - { - "name" : "Fedora", - "version" : "3.0.4-1.fc10" - } - }, - { - "desc" : "Mandriva", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.22) Gecko/20110907 Mandriva Linux/1.9.2.22-0.1mdv2010.2 (2010.2) Firefox/3.6.22", - "expect" : - { - "name" : "Mandriva", - "version" : "1.9.2.22-0.1mdv2010.2" - } - }, - { - "desc" : "Chromium OS", - "ua" : "Mozilla/5.0 (X11; CrOS x86_64 10575.58.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36", - "expect" : - { - "name" : "Chromium OS", - "version" : "10575.58.0" - } - }, - { - "desc" : "Fuchsia", - "ua" : "Mozilla/5.0 (X11; Fuchsia x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3557.0 Safari/537.36", - "expect" : - { - "name" : "Fuchsia", - "version" : "undefined" - } - }, - { - "desc" : "Solaris", - "ua" : "Mozilla/5.0 (X11; U; SunOS sun4u; en-US; rv:1.7) Gecko/20070606", - "expect" : - { - "name" : "Solaris", - "version" : "sun4u" - } - }, - { - "desc" : "FreeBSD", - "ua" : "Mozilla/5.0 (X11; U; FreeBSD x86_64; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16", - "expect" : - { - "name" : "FreeBSD", - "version" : "undefined" - } - }, - { - "desc" : "OpenBSD", - "ua" : "Mozilla/5.0 (X11; U; OpenBSD i386; en-US; rv:1.9.1) Gecko/20090702 Firefox/3.5", - "expect" : - { - "name" : "OpenBSD", - "version" : "undefined" - } - }, - { - "desc" : "NetBSD", - "ua" : "ELinks (0.4.3; NetBSD 3.0.2PATCH sparc64; 141x19)", - "expect" : - { - "name" : "NetBSD", - "version" : "3.0.2PATCH" - } - }, - { - "desc" : "DragonFly", - "ua" : "Mozilla/5.0 (X11; U; DragonFly i386; de; rv:1.9.1) Gecko/20090720 Firefox/3.5.1", - "expect" : - { - "name" : "DragonFly", - "version" : "undefined" - } - }, - { - "desc" : "iOS in App", - "ua" : "AppName/version CFNetwork/version Darwin/version", - "expect" : - { - "name" : "iOS", - "version" : "undefined" - } - }, - { - "desc" : "iOS with Chrome", - "ua" : "Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3", - "expect" : - { - "name" : "iOS", - "version" : "5.1.1" - } - }, - { - "desc" : "iOS with Opera Mini", - "ua" : "Opera/9.80 (iPhone; Opera Mini/7.1.32694/27.1407; U; en) Presto/2.8.119 Version/11.10", - "expect" : - { - "name" : "iOS", - "version" : "undefined" - } - }, - { - "desc" : "Mac OS on PowerPC", - "ua" : "Mozilla/4.0 (compatible; MSIE 5.0b1; Mac_PowerPC)", - "expect" : - { - "name" : "Mac OS", - "version" : "undefined" - } - }, - { - "desc" : "Mac OS X on x86, x86_64, or aarch64 using Firefox", - "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0", - "expect" : - { - "name" : "Mac OS", - "version" : "x.y" - } - }, - { - "desc" : "Mac OS X on PowerPC using Firefox", - "ua" : "Mozilla/5.0 (Macintosh; PPC Mac OS X x.y; rv:10.0) Gecko/20100101 Firefox/10.0", - "expect" : - { - "name" : "Mac OS", - "version" : "x.y" - } - }, - { - "desc" : "Mac OS", - "ua" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36", - "expect" : - { - "name" : "Mac OS", - "version" : "10.6.8" - } - }, - { - "desc" : "Haiku", - "ua" : "Mozilla/5.0 (Macintosh; Intel Haiku R1 x86) AppleWebKit/602.1.1 (KHTML, like Gecko) WebPositive/1.2 Version/8.0 Safari/602.1.1", - "expect" : - { - "name" : "Haiku", - "version" : "R1" - } - }, - { - "desc" : "KaiOS", - "ua" : "Mozilla/5.0 (Mobile; Nokia_8110_4G; rv:48.0) Gecko/48.0 Firefox/48.0 KAIOS/2.5", - "expect" : - { - "name" : "KAIOS", - "version" : "2.5" - } - }, - { - "desc" : "iTunes Windows Vista", - "ua" : "iTunes/10.7 (Windows; Microsoft Windows Vista Home Premium Edition Service Pack 1 (Build 6001)) AppleWebKit/536.26.9", - "expect" : - { - "name" : "Windows", - "version" : "Vista" - } - }, - { - "desc" : "iOS BE App", - "ua" : "APP-BE Test/1.0 (iPad; Apple; CPU iPhone OS 7_0_2 like Mac OS X)", - "expect" : - { - "name" : "iOS", - "version" : "7.0.2" - } - }, - { - "desc" : "KTB-Nexus 5", - "ua" : "APP-My App/1.0 (Linux; Android 4.2.1; Nexus 5 Build/JOP40D)", - "expect" : - { - "name" : "Android", - "version" : "4.2.1" - } - }, - { - "desc" : "Solaris", - "ua" : "NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)", - "expect" : - { - "name" : "Solaris", - "version" : "4.1.4" - } - }, - { - "desc" : "Raspbian", - "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/537.36 (KHTML, like Gecko) Raspbian Chromium/72.0.3626.121 HeadlessChrome/72.0.3626.121 Safari/537.36", - "expect" : - { - "name" : "Raspbian", - "version" : "undefined" - } - }, - { - "desc" : "Raspbian", - "ua" : "Mozilla/5.0 (X11; Linux armv7l) AppleWebKit/538.15 (KHTML, like Gecko) Version/8.0 Safari/538.15 Raspbian/9.0 (1:3.8.2.0-0rpi28) Epiphany/3.8.2", - "expect" : - { - "name" : "Raspbian", - "version" : "9.0" - } - }, - { - "desc" : "AIX", - "ua" : "Mozilla/5.0 (X11; U; AIX 000138384C00; en-US; rv:1.0.1) Gecko/20030213 Netscape/7.0", - "expect" : - { - "name" : "AIX", - "version" : "undefined" - } - }, - { - "desc" : "Plan9", - "ua" : "NCSA_Mosaic/5.0 (X11;Plan 9 4.0)", - "expect" : - { - "name" : "Plan 9", - "version" : "4.0" - } - }, - { - "desc" : "Minix", - "ua" : "Mozilla/5.0 (X11; Original ; Minix 3.3 ; rv:3.0)", - "expect" : - { - "name" : "Minix", - "version" : "3.3" - } - }, - { - "desc" : "BeOS", - "ua" : "Mozilla/5.0 (BeOS; U; BeOS BePC; en-US; rv:1.8.1.8pre) Gecko/20070926 SeaMonkey/1.1.5pre", - "expect" : - { - "name" : "BeOS", - "version" : "undefined" - } - }, - { - "desc" : "OS/2", - "ua" : "Links (2.1pre14; OS/2 1 i386; 80x33)", - "expect" : - { - "name" : "OS/2", - "version" : "undefined" - } - }, - { - "desc" : "AmigaOS", - "ua" : "Mozilla/4.0 (compatible; AWEB 3.4 SE; AmigaOS)", - "expect" : - { - "name" : "AmigaOS", - "version" : "undefined" - } - }, - { - "desc" : "MorphOS", - "ua" : "AmigaVoyager/3.4.4 (MorphOS/PPC native)", - "expect" : - { - "name" : "MorphOS", - "version" : "undefined" - } - }, - { - "desc" : "UNIX", - "ua" : "Surf/0.4.1 (X11; U; Unix; en-US) AppleWebKit/531.2+ Compatible (Safari)", - "expect" : - { - "name" : "Unix", - "version" : "undefined" - } - }, - { - "desc" : "Joli", - "ua" : "Mozilla/5.0 (X11; Jolicloud Linux i686) AppleWebKit/537.6 (KHTML, like Gecko) Joli OS/1.2 Chromium/23.0.1240.0 Chrome/23.0.1240.0 Safari/537.6", - "expect" : - { - "name" : "Joli", - "version" : "1.2" - } - }, - { - "desc" : "CentOS", - "ua" : "Konqueror/15.13 (CentOS Linux 7.4; cs-CZ;)", - "expect" : - { - "name" : "CentOS", - "version" : "7.4" - } - }, - { - "desc" : "PCLinuxOS", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.13) Gecko/20101209 PCLinuxOS/1.9.2.13-1pclos2010 (2010) Firefox/3.6.13", - "expect" : - { - "name" : "PCLinuxOS", - "version" : "1.9.2.13-1pclos2010" - } - }, - { - "desc" : "RedHat", - "ua" : "Mozilla/5.0 (compatible; Konqueror/4.3; Linux) KHTML/4.3.4 (like Gecko) Red Hat Enterprise Linux/4.3.4-11.el6_1.4", - "expect" : - { - "name" : "Red Hat", - "version" : "4.3.4-11.el6_1.4" - } - }, - { - "desc" : "RedHat", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.13pre) Gecko/20070717 Red Hat/1.0.9-4.el4 SeaMonkey/1.0.9", - "expect" : - { - "name" : "Red Hat", - "version" : "1.0.9-4.el4" - } - }, - { - "desc" : "RedHat", - "ua" : "iTunes/4.7.1 (Linux; N; Red Hat; x86_64-linux; EN; utf8) SqueezeCenter, Squeezebox Server, Logitech Media Server/7.9.1/1522157629", - "expect" : - { - "name" : "Red Hat", - "version" : "undefined" - } - }, - { - "desc" : "RedHat", - "ua" : "curl/7.20.0 (x86_64-redhat-linux-gnu) libcurl/7.20.0 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5", - "expect" : - { - "name" : "redhat", - "version" : "undefined" - } - }, - { - "desc" : "RISC OS", - "ua" : "Mozilla/1.10 [en] (Compatible; RISC OS 3.70; Oregano 1.10)", - "expect" : - { - "name" : "RISC OS", - "version" : "3.70" - } - }, - { - "desc" : "Zenwalk", - "ua" : "Flock/2.16 (Zenwalk 7.3; es_PR;)", - "expect" : - { - "name" : "Zenwalk", - "version" : "7.3" - } - }, - { - "desc" : "Hurd", - "ua" : "Mozilla/5.0 (X11; Hurd 0.9 i386; en-US) libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/3.7.0 Safari/696.96", - "expect" : - { - "name" : "Hurd", - "version" : "0.9" - } - }, - { - "desc" : "Linux", - "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36", - "expect" : - { - "name" : "Linux", - "version" : "x86_64" - } - }, - { - "desc" : "Deepin", - "ua" : "Mozilla/5.0 (X11; Linux x86_64; Deepin 15.5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36 NFSBrowser/5.0.0.1886", - "expect" : - { - "name" : "Deepin", - "version" : "15.5" - } - }, - { - "desc" : "Palm OS", - "ua" : "Mozilla/4.76 [en] (PalmOS; U; WebPro3.0; Palm-Arz1)", - "expect" : - { - "name" : "Palm", - "version" : "undefined" - } - }, - { - "desc" : "HP-UX", - "ua" : "Mozilla/5.0 (X11; U; HP-UX 9000/785; es-ES; rv:1.0.1) Gecko/20020827 Netscape/7.0", - "expect" : - { - "name" : "HP-UX", - "version" : "undefined" - } - }, - { - "desc" : "Contiki", - "ua" : "Contiki/1.0 (Commodore 64; http://dunkels.com/adam/contiki/)", - "expect" : - { - "name" : "Contiki", - "version" : "1.0" - } - }, - { - "desc" : "Linpus", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9b5pre) Gecko/2008032619 Linpus/3.0-0.49", - "expect" : - { - "name" : "Linpus", - "version" : "3.0-0.49" - } - }, - { - "desc" : "Manjaro", - "ua" : "Mozilla/5.0 (X11; Manjaro 19.0.2; Arch; x64; rv:84.0) Gecko/20100101 Firefox/84.0", - "expect" : - { - "name" : "Manjaro", - "version" : "19.0.2" - } - }, - { - "desc" : "elementary OS", - "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/604.1 (KHTML, like Gecko) Version/11.0 Safari/604.1 elementary OS/0.4 (Loki) Epiphany/3.18.11", - "expect" : - { - "name" : "elementary OS", - "version" : "0.4" - } - }, - { - "desc" : "GhostBSD", - "ua" : "Mozilla/5.0 (X11; GhostBSD/10.3; x86_64; rv:50.0.1) Gecko/20100101 Firefox/50.0.1", - "expect" : - { - "name" : "GhostBSD", - "version" : "10.3" - } - }, - { - "desc" : "Android-x86", - "ua" : "Mozilla/5.0 (Linux; Android 7.1.2; Generic Android-x86) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 OPR/61.2.3076.56749", - "expect" : - { - "name" : "Android-x86", - "version" : "7.1.2" - } - }, - { - "desc" : "Sabayon", - "ua" : "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Sabayon Chrome/19.0.1084.46 Safari/536.5", - "expect" : - { - "name" : "Sabayon", - "version" : "undefined" - } - }, - { - "desc" : "Linspire", - "ua" : "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.4) Gecko/20060803 Firefox/1.5.0.4 Linspire/1.5.0.4", - "expect" : - { - "name" : "Linspire", - "version" : "1.5.0.4" - } - } -] diff --git a/test/static/dts-lint.ts b/test/static/dts-lint.ts new file mode 100644 index 000000000..bcfc26287 --- /dev/null +++ b/test/static/dts-lint.ts @@ -0,0 +1,49 @@ +import { expectType } from 'tsd'; +import { UAParser, IResult, IBrowser, ICPU, IEngine, IDevice, IOS } from "../../src/main/ua-parser"; +import { isAppleSilicon, isChromeFamily } from "../../src/helpers/ua-parser-helpers"; + +const uastring = 'Mozilla/5.0 (X11; MyCustomOS; Linux i686; rv:19.0) Gecko/20100101 Firefox/19.0'; +const extensions = { + os : [ + [/(mycustomos)/], [UAParser.OS.NAME, [UAParser.OS.VERSION, '10']] + ] +}; +const headers = { + 'sec-ch-ua-mobile' : '?1' +}; + +expectType(UAParser()); +expectType(UAParser(uastring)); +expectType(UAParser(uastring, extensions)); +expectType(UAParser(uastring, headers)); +expectType(UAParser(extensions, headers)); +expectType(UAParser(extensions)); +expectType(UAParser(headers)); +expectType(new UAParser()); + +const parser = new UAParser(uastring); +const browser = parser.getBrowser(); + +expectType(browser); +expectType(browser.name); +expectType(browser.version); +expectType(browser.major); +expectType<'crawler' | 'cli' | 'email' | 'fetcher' | 'inapp' | 'mediaplayer' | 'library' | undefined>(browser.type); +expectType(browser.is('')); +expectType(browser.toString()); +expectType>(browser.withClientHints()); +expectType>((browser.withClientHints()).withFeatureCheck()); +expectType(((browser.withClientHints()).withFeatureCheck()).is('')); + +expectType(parser.getCPU()); +expectType(parser.getDevice()); +expectType(parser.getEngine()); +expectType(parser.getOS()); +expectType(parser.getResult()); +expectType(parser.getUA()); +expectType(parser.setUA(uastring)); + +const result = parser.getResult(); + +expectType(isAppleSilicon(result)); +expectType(isChromeFamily(result)); \ No newline at end of file diff --git a/test/test.js b/test/test.js deleted file mode 100644 index e1fd175d2..000000000 --- a/test/test.js +++ /dev/null @@ -1,168 +0,0 @@ -var fs = require('fs'); -var safe = require('safe-regex'); -var assert = require('assert'); -var requirejs = require('requirejs'); -var parseJS = require('@babel/parser').parse; -var traverse = require('@babel/traverse').default; -var UAParser = require('./../src/ua-parser'); -var browsers = require('./browser-test.json'); -var cpus = require('./cpu-test.json'); -var devices = require('./device-test.json'); -var engines = require('./engine-test.json'); -var os = require('./os-test.json'); -var parser = new UAParser(); -var methods = [ - { - title : 'getBrowser', - label : 'browser', - list : browsers, - properties : ['name', 'major', 'version'] - }, - { - title : 'getCPU', - label : 'cpu', - list : cpus, - properties : ['architecture'] - }, - { - title : 'getDevice', - label : 'device', - list : devices, - properties : ['model', 'type', 'vendor'] - }, - { - title : 'getEngine', - label : 'engine', - list : engines, - properties : ['name', 'version'] - }, - { - title : 'getOS', - label : 'os', - list : os, - properties : ['name', 'version'] -}]; - -describe('UAParser()', function () { - var ua = 'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6'; - assert.deepStrictEqual(UAParser(ua), new UAParser().setUA(ua).getResult()); -}); - -describe('UAParser() constructor does not throw with undefined ua argument', function () { - assert.doesNotThrow(() => new UAParser(undefined).getResult()); -}); - -describe('UAParser.setUA method does not throw with undefined ua argument', function () { - assert.doesNotThrow(() => new UAParser().setUA(undefined).getResult()); -}); - -for (var i in methods) { - describe(methods[i]['title'], function () { - for (var j in methods[i]['list']) { - if (!!methods[i]['list'][j].ua) { - describe('[' + methods[i]['list'][j].desc + ']', function () { - describe('"' + methods[i]['list'][j].ua + '"', function () { - var expect = methods[i]['list'][j].expect; - var result = parser.setUA(methods[i]['list'][j].ua).getResult()[methods[i]['label']]; - - methods[i]['properties'].forEach(function(m) { - it('should return ' + methods[i]['label'] + ' ' + m + ': ' + expect[m], function () { - assert.strictEqual(result[m], expect[m] != 'undefined' ? expect[m] : undefined); - }); - }); - }); - }); - } - } - }); -} - -describe('Returns', function () { - it('getResult() should returns JSON', function(done) { - assert.deepStrictEqual(new UAParser('').getResult(), - { - ua : '', - browser: { name: undefined, version: undefined, major: undefined }, - cpu: { architecture: undefined }, - device: { vendor: undefined, model: undefined, type: undefined }, - engine: { name: undefined, version: undefined}, - os: { name: undefined, version: undefined } - }); - done(); - }); -}); - -describe('Extending Regex', function () { - var uaString = 'Mozilla/5.0 MyOwnBrowser/1.3'; - var myOwnBrowser = [[/(myownbrowser)\/((\d+)?[\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, UAParser.BROWSER.MAJOR]]; - - var parser1 = new UAParser(uaString, {browser: myOwnBrowser}); - assert.strictEqual(parser1.getBrowser().name, 'MyOwnBrowser'); - assert.strictEqual(parser1.getBrowser().version, '1.3'); - assert.strictEqual(parser1.getBrowser().major, '1'); - - var parser2 = new UAParser({browser: myOwnBrowser}); - assert.strictEqual(parser2.getBrowser().name, undefined); - parser2.setUA(uaString); - assert.strictEqual(parser2.getBrowser().name, 'MyOwnBrowser'); - assert.strictEqual(parser1.getBrowser().version, '1.3'); -}); - -describe('User-agent length', function () { - var UA_MAX_LENGTH = 350; - - // Real data from https://stackoverflow.com/questions/654921/how-big-can-a-user-agent-string-get#answer-6595973 - var uaString = 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; (R1 1.6); SLCC1; .NET CLR 2.0.50727; InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0; .NET CLR 3.5.30729; .NET CLR 3.0.30618; 66760635803; runtime 11.00294; 876906799603; 97880703; 669602703; 9778063903; 877905603; 89670803; 96690803; 8878091903; 7879040603; 999608065603; 799808803; 6666059903; 669602102803; 888809342903; 696901603; 788907703; 887806555703; 97690214703; 66760903; 968909903; 796802422703; 8868026703; 889803611803; 898706903; 977806408603; 976900799903; 9897086903; 88780803; 798802301603; 9966008603; 66760703; 97890452603; 9789064803; 96990759803; 99960107703; 8868087903; 889801155603; 78890703; 8898070603; 89970603; 89970539603; 89970488703; 8789007603; 87890903; 877904603; 9887077703; 798804903; 97890264603; 967901703; 87890703; 97690420803; 79980706603; 9867086703; 996602846703; 87690803; 6989010903; 977809603; 666601903; 876905337803; 89670603; 89970200903; 786903603; 696901911703; 788905703; 896709803; 96890703; 998601903; 88980703; 666604769703; 978806603; 7988020803; 996608803; 788903297903; 98770043603; 899708803; 66960371603; 9669088903; 69990703; 99660519903; 97780603; 888801803; 9867071703; 79780803; 9779087603; 899708603; 66960456803; 898706824603; 78890299903; 99660703; 9768079803; 977901591603; 89670605603; 787903608603; 998607934903; 799808573903; 878909603; 979808146703; 9996088603; 797803154903; 69790603; 99660565603; 7869028603; 896707703; 97980965603; 976907191703; 88680703; 888809803; 69690903; 889805523703; 899707703; 997605035603; 89970029803; 9699094903; 877906803; 899707002703; 786905857603; 69890803; 97980051903; 997603978803; 9897097903; 66960141703; 7968077603; 977804603; 88980603; 989700803; 999607887803; 78690772803; 96990560903; 98970961603; 9996032903; 9699098703; 69890655603; 978903803; 698905066803; 977806903; 9789061703; 967903747703; 976900550903; 88980934703; 8878075803; 8977028703; 97980903; 9769006603; 786900803; 98770682703; 78790903; 878906967903; 87690399603; 99860976703; 796805703; 87990603; 968906803; 967904724603; 999606603; 988705903; 989702842603; 96790603; 99760703; 88980166703; 9799038903; 98670903; 697905248603; 7968043603; 66860703; 66860127903; 9779048903; 89670123903; 78890397703; 97890603; 87890803; 8789030603; 69990603; 88880763703; 9769000603; 96990203903; 978900405903; 7869022803; 699905422903; 97890703; 87990903; 878908703; 7998093903; 898702507603; 97780637603; 966907903; 896702603; 9769004803; 7869007903; 99660158803; 7899099603; 8977055803; 99660603; 7889080903; 66660981603; 997604603; 6969089803; 899701903; 9769072703; 666603903; 99860803; 997608803; 69790903; 88680756703; 979805677903; 9986047703; 89970803; 66660603; 96690903; 8997051603; 789901209803; 8977098903; 968900326803; 87790703; 98770024803; 697901794603; 69990803; 887805925803; 968908903; 97880603; 897709148703; 877909476903; 66760197703; 977908603; 698902703; 988706504803; 977802026603; 88680964703; 8878068703; 987705107903; 978902878703; 8898069803; 9768031703; 79680803; 79980803; 669609328703; 89870238703; 99960593903; 969904218703; 78890603; 9788000703; 69690630903; 889800982903; 988709748803; 7968052803; 99960007803; 969900800803; 668604817603; 66960903; 78790734603; 8868007703; 79780034903; 8878085903; 976907603; 89670830803; 877900903; 969904889703; 7978033903; 8987043903; 99860703; 979805903; 667603803; 976805348603; 999604127603; 97790701603; 78990342903; 98770672903; 87990253903; 9877027703; 97790803; 877901895603; 8789076903; 896708595603; 997601903; 799806903; 97690603; 87790371703; 667605603; 99760303703; 97680283803; 788902750803; 787909803; 79780603; 79880866903; 9986050903; 87890543903; 979800803; 97690179703; 876901603; 699909903; 96990192603; 878904903; 877904734903; 796801446903; 977904803; 9887044803; 797805565603; 98870789703; 7869093903; 87790727703; 797801232803; 666604803; 9778071903; 9799086703; 6969000903; 89670903; 8799075903; 897708903; 88680903; 97980362603; 97980503903; 889803256703; 88980388703; 789909376803; 69690703; 6969025903; 89970309903; 96690703; 877901847803; 968901903; 96690603; 88680607603; 7889001703; 789904761803; 976807703; 976902903; 878907889703; 9897014903; 896707046603; 696909903; 666603998903; 969902703; 79680421803; 9769075603; 798800192703; 97990903; 9689024903; 668604803; 969908671903; 9996094703; 69990642703; 97890895903; 977805619903; 79980859903; 88980443803; 98970649603; 997602703; 888802169903; 699907803; 667602028803; 786903283903; 997607703; 969909803; 798809925903; 9976045603; 97790903; 9789001903; 966903603; 9789069603; 968906603; 6989091803; 896701603; 6979059803; 978803903; 997606362603; 88980803; 98970803; 88880921703; 8997065703; 899700703; 698908703; 797801027903; 7889050903; 87890603; 78690703; 99660069703; 97980309903; 976800603; 666606803; 898707703; 79880019803; 66960250803; 7978049803; 88780602603; 79680903; 88880792703; 96990903; 667608603; 87790730903; 98970903; 9699032903; 8987004803; 88880703; 89770046603; 978800803; 969908903; 9798022603; 696901903; 799803703; 989703703; 668605903; 79780903; 998601371703; 796803339703; 87890922603; 898708903; 9966061903; 66960891903; 96790903; 8779050803; 98870858803; 976909298603; 9887029903; 669608703; 979806903; 878903803; 99960703; 9789086703; 979801803; 66960008703; 979806830803; 99760212703; 786906603; 797807603; 789907297703; 96990703; 786901603; 796807766603; 896702651603; 789902585603; 66660925903; 9986085703; 66960302703; 69890703; 789900703; 89970903; 9679060703; 9789002903; 979908821603; 986708140803; 976809828703; 7988082803; 79680997903; 99960803; 9788081903; 979805703; 787908603; 66960602803; 9887098703; 978803237703; 888806804603; 999604703; 977904703; 966904635703; 97680291703; 977809345603; 8878046703; 988709803; 976900773603; 989703903; 88780198603; 87790603; 986708703; 78890604703; 87790544803; 976809850903; 887806703; 987707527603; 79880803; 9897059603; 897709820603; 97880804803; 66960026703; 9789062803; 9867090803; 669600603; 8967087703; 78890903; 89770903; 97980703; 976802687603; 66860400803; 979901288603; 96990160903; 99860228903; 966900703; 66760603; 9689035703; 9779064703; 7968023603; 87890791903; 98770870603; 9798005803; 6969087903; 9779097903; 6979065703; 699903252603; 79780989703; 87690901803; 978905763903; 977809703; 97790369703; 899703269603; 8878012703; 78790803; 87690395603; 8888042803; 667607689903; 8977041803; 6666085603; 6999080703; 69990797803; 88680721603; 99660519803; 889807603; 87890146703; 699906325903; 89770603; 669608615903; 9779028803; 88880603; 97790703; 79780703; 97680355603; 6696024803; 78790784703; 97880329903; 9699077703; 89870803; 79680227903; 976905852703; 8997098903; 896704796703; 66860598803; 9897036703; 66960703; 9699094703; 9699008703; 97780485903; 999603179903; 89770834803; 96790445603; 79680460903; 9867009603; 89870328703; 799801035803; 989702903; 66960758903; 66860150803; 6686088603; 9877092803; 96990603; 99860603; 987703663603; 98870903; 699903325603; 87790803; 97680703; 8868030703; 9799030803; 89870703; 97680803; 9669054803; 6979097603; 987708046603; 999608603; 878904803; 998607408903; 968903903; 696900703; 977907491703; 6686033803; 669601803; 99960290603; 887809169903; 979803703; 69890903; 699901447903; 8987064903; 799800603; 98770903; 8997068703; 967903603; 66760146803; 978805087903; 697908138603; 799801603; 88780964903; 989708339903; 8967048603; 88880981603; 789909703; 796806603; 977905977603; 989700603; 97780703; 9669062603; 88980714603; 897709545903; 988701916703; 667604694903; 786905664603; 877900803; 886805490903; 89970559903; 99960531803; 7998033903; 98770803; 78890418703; 669600872803; 996605216603; 78690962703; 667604903; 996600903; 999608903; 9699083803; 787901803; 97780707603; 787905312703; 977805803; 8977033703; 97890708703; 989705521903; 978800703; 698905703; 78890376903; 878907703; 999602903; 986705903; 668602719603; 979901803; 997606903; 66760393903; 987703603; 78790338903; 96890803; 97680596803; 666601603; 977902178803; 877902803; 78790038603; 8868075703; 99960060603)'; - - it('greater than ' + UA_MAX_LENGTH + ' should be trimmed down', function () { - assert.strictEqual(UAParser(uaString).ua.length, UA_MAX_LENGTH); - }); -}); - -describe('Using Require.js', function () { - it('should loaded automatically', function(done) { - requirejs.config({ - baseUrl : 'dist', - paths : { - 'ua-parser-js' : 'ua-parser.min' - } - }); - requirejs(['ua-parser-js'], function(ua) { - var parser = new ua('Dillo/1.0'); - assert.deepStrictEqual(parser.getBrowser().name, 'Dillo'); - done(); - }); - }); -}); - -describe('Testing regexes', function () { - - var regexes; - - before('Read main js file', function () { - var code = fs.readFileSync('src/ua-parser.js', 'utf8').toString(); - var ast = parseJS(code, { sourceType: "script" }); - regexes = []; - traverse(ast, { - RegExpLiteral: (path) => { - regexes.push(path.node.pattern); - } - }); - - if (regexes.length === 0) { - throw new Error("Regexes cannot be empty!"); - } - }); - - describe('Begin testing', function () { - it('all regexes in main file', function () { - regexes.forEach(function (regex) { - describe('Test against `safe-regex` : ' + regex, function () { - it('should be safe from potentially vulnerable regex', function () { - assert.strictEqual(safe(regex), true); - }); - }); - }); - }); - }); -}); diff --git a/test/unit/es6.mjs b/test/unit/es6.mjs new file mode 100644 index 000000000..8e26908d5 --- /dev/null +++ b/test/unit/es6.mjs @@ -0,0 +1,26 @@ +import { UAParser } from '../../src/main/ua-parser.mjs'; +import { CPU, Device, Engine } from '../../src/enums/ua-parser-enums.mjs'; +import * as assert from 'assert'; + +describe('Returns', () => { + it('getResult() should returns object', () => { + assert.deepEqual(new UAParser('').getResult(), + { + ua : '', + browser: { name: undefined, version: undefined, major: undefined, type: undefined }, + cpu: { architecture: undefined }, + device: { vendor: undefined, model: undefined, type: undefined }, + engine: { name: undefined, version: undefined}, + os: { name: undefined, version: undefined } + }); + }); +}); + +describe('Enums', () => { + it('Can use enum', () => { + const { cpu, device, engine } = UAParser('Mozilla/5.0 (X11; U; Linux armv7l; en-GB; rv:1.9.2a1pre) Gecko/20090928 Firefox/3.5 Maemo Browser 1.4.1.22 RX-51 N900'); + assert.strictEqual(cpu.is(CPU.ARM), true); + assert.strictEqual(device.is(Device.MOBILE), true); + assert.strictEqual(engine.is(Engine.GECKO), true); + }); +}); \ No newline at end of file diff --git a/test/unit/extensions.js b/test/unit/extensions.js new file mode 100644 index 000000000..9f1fa9630 --- /dev/null +++ b/test/unit/extensions.js @@ -0,0 +1,114 @@ +const fs = require('fs'); +const assert = require('assert'); +const parseJS = require('@babel/parser').parse; +const traverse = require('@babel/traverse').default; +const safe = require('safe-regex'); +const { UAParser } = require('../../src/main/ua-parser'); +const { Bots, CLIs, Crawlers, Emails, Fetchers, InApps, Libraries, Vehicles } = require('../../src/extensions/ua-parser-extensions'); + +describe('Extensions', () => { + [ + ['CLIs', 'cli', CLIs], + ['Crawlers', 'crawler', Crawlers], + ['Emails', 'email', Emails], + ['Fetchers', 'fetcher', Fetchers], + ['InApps', 'inapp', InApps], + ['Libraries', 'library', Libraries], + ['Vehicles', 'vehicle', Vehicles] + ] + .forEach(([desc, path, ext]) => { + const tests = require(`../data/ua/extension/${path}.json`); + describe(desc, () => { + tests.forEach((test) => { + it(`Can detect ${test.desc}: "${test.ua}"`, () => { + const { browser, device } = UAParser(test.ua, ext); + if ('browser' in ext) { + assert.strictEqual(String(browser.name), test.expect.name); + assert.strictEqual(String(browser.version), test.expect.version); + assert.strictEqual(String(browser.type), test.expect.type); + } else if ('device' in ext) { + assert.strictEqual(String(device.vendor), test.expect.vendor); + assert.strictEqual(String(device.model), test.expect.model); + assert.strictEqual(String(device.type), test.expect.type); + } + }); + }); + }); + }); + + const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)'; + const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; + const axios = 'axios/1.3.5'; + const jsdom = 'Mozilla/5.0 (darwin) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3'; + const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)'; + + assert.equal(UAParser(scrapy, Bots).browser.name, 'Scrapy'); + + 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(thunderbird).getBrowser(), {name: "Thunderbird", version: "78.13.0", major: "78", type: "email"}); + + const libraryParser = new UAParser(Libraries); + assert.deepEqual(libraryParser.setUA(axios).getBrowser(), {name: "axios", version: "1.3.5", major: "1", type: "library"}); + assert.deepEqual(libraryParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "library"}); + assert.deepEqual(libraryParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "library"}); + + // Bluesky + const bluesky = 'Mozilla/5.0 (compatible; Bluesky Cardyb/1.1; +mailto:support@bsky.app)'; + assert.deepEqual(new UAParser(bluesky, Bots).getBrowser(), { + name: 'Bluesky', + version: '1.1', + major: '1', + type: 'fetcher' + }); + + const whatsapp = "WhatsApp/2.0 A"; + assert.deepEqual(new UAParser(whatsapp, Fetchers).getOS(), { + name : 'Android', + version : undefined + }); +}); + +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 the safety of regexes', () => { + + let regexes; + let code = fs.readFileSync('src/extensions/ua-parser-extensions.js', 'utf8').toString(); + let ast = parseJS(code, { sourceType: 'script' }); + regexes = []; + traverse(ast, { + RegExpLiteral: (path) => { + regexes.push(path.node.pattern); + } + }); + + if (regexes.length === 0) { + throw new Error('Regexes cannot be empty!'); + } + + describe('Checking for potentially vulnerable regex', () => { + for (let regex of regexes) { + it('Test against `safe-regex` : ' + regex, () => { + assert.strictEqual(safe(regex), true); + }); + } + }); +}); \ No newline at end of file diff --git a/test/unit/helpers.js b/test/unit/helpers.js new file mode 100644 index 000000000..127133bec --- /dev/null +++ b/test/unit/helpers.js @@ -0,0 +1,84 @@ +const assert = require('assert'); +const { UAParser } = require('../../src/main/ua-parser'); +const { getDeviceVendor, isAppleSilicon, isAIBot, isBot, isChromeFamily } = require('../../src/helpers/ua-parser-helpers'); +const { Bots, Emails } = require('../../src/extensions/ua-parser-extensions'); + +describe('getDeviceVendor', () => { + it('Can guess the device vendor from a model name', () => { + + const modelSM = 'SM-A605G'; + const modelRedmi = 'Redmi Note 8'; + const modelNexus = 'Nexus 6P'; + const modelAquos = 'AQUOS-TVX19B'; + + assert.equal(getDeviceVendor(modelSM), 'Samsung'); + assert.equal(getDeviceVendor(modelRedmi), 'Xiaomi'); + assert.equal(getDeviceVendor(modelNexus), 'Huawei'); + assert.equal(getDeviceVendor(modelAquos), 'Sharp'); + }); +}); + +describe('isAppleSilicon', () => { + it('Can detect Apple Silicon device', () => { + + // non-real ua + const macARM = 'Mozilla/5.0 (Macintosh; ARM; Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0'; + const macIntel = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0'; + + assert.equal(isAppleSilicon(UAParser(macIntel)), false); + assert.equal(isAppleSilicon(macIntel), false); + assert.equal(isAppleSilicon(UAParser(macARM)), true); + assert.equal(isAppleSilicon(macARM), true); + }); +}); + +describe('isAIBot', () => { + it('Can detect AI Bots', () => { + + const claudeBot = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)'; + const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0'; + const searchGPT = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; OAI-SearchBot/1.0; +https://openai.com/searchbot'; + const semrushAI = 'Mozilla/5.0 (compatible; SemrushBot-OCOB/1; +https://www.semrush.com/bot/)'; + + assert.equal(isAIBot(UAParser(claudeBot, Bots)), true); + assert.equal(isAIBot(claudeBot), true); + assert.equal(isAIBot(firefox), false); + assert.equal(isAIBot(searchGPT), true); + assert.equal(isAIBot(semrushAI), true); + }); +}); + +describe('isBot', () => { + it('Can detect Bots', () => { + + // non-real ua + const ahrefsBot = 'Mozilla/5.0 (compatible; AhrefsBot/7.0; +http://ahrefs.com/robot/)'; + const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0'; + const scrapy = 'Scrapy/1.5.0 (+https://scrapy.org)'; + const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0'; + + const botParser = new UAParser(firefox, { Bots, Emails }); + assert.equal(isBot(botParser.getResult()), false); + assert.equal(isBot(botParser.setUA(ahrefsBot).getResult()), true); + assert.equal(isBot(botParser.setUA(scrapy).getResult()), true); + assert.equal(isBot(botParser.setUA(thunderbird).getResult()), false); + + assert.equal(isBot(ahrefsBot), true); + assert.equal(isBot(firefox), false); + assert.equal(isBot(scrapy), true); + assert.equal(isBot(thunderbird), false); + }); +}); + +describe('isChromeFamily', () => { + it('Can detect Chromium-based browser', () => { + + const edge = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.2151.58'; + const firefox = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0'; + + assert.equal(isChromeFamily(UAParser(edge)), true); + assert.equal(isChromeFamily(UAParser(firefox)), false); + assert.equal(isChromeFamily(edge), true); + assert.equal(isChromeFamily(firefox), false); + }); +}); \ No newline at end of file diff --git a/test/unit/main.js b/test/unit/main.js new file mode 100644 index 000000000..f9b2ef961 --- /dev/null +++ b/test/unit/main.js @@ -0,0 +1,384 @@ +var fs = require('fs'); +var safe = require('safe-regex'); +var assert = require('assert'); +var requirejs = require('requirejs'); +var parseJS = require('@babel/parser').parse; +var traverse = require('@babel/traverse').default; +var {UAParser} = require('../../src/main/ua-parser'); +var browsers = require('../data/ua/browser/browser-all.json'); +var cpus = require('../data/ua/cpu/cpu-all.json'); +var devices = readJsonFiles('test/data/ua/device'); +var engines = require('../data/ua/engine/engine-all.json'); +var os = readJsonFiles('test/data/ua/os'); +var { Headers } = require('undici'); + +function readJsonFiles(dir) { + var list = []; + fs.readdirSync(dir).forEach(function (file) { + list.push(...JSON.parse(fs.readFileSync(`${dir}/${file}`, 'utf-8'))); + }); + return list; +}; + +describe('UAParser()', function () { + var ua = 'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6'; + assert.deepEqual(UAParser(ua), new UAParser().setUA(ua).getResult()); +}); + +describe('UAParser() constructor does not throw with undefined ua argument', function () { + assert.doesNotThrow(() => new UAParser(undefined).getResult()); +}); + +describe('UAParser.setUA method does not throw with undefined ua argument', function () { + assert.doesNotThrow(() => new UAParser().setUA(undefined).getResult()); +}); + +describe('UAParser get*() methods', () => { + [ + { + title : 'getBrowser()', + label : 'browser', + list : browsers + }, + { + title : 'getCPU()', + label : 'cpu', + list : cpus + }, + { + title : 'getDevice()', + label : 'device', + list : devices + }, + { + title : 'getEngine()', + label : 'engine', + list : engines + }, + { + title : 'getOS()', + label : 'os', + list : os + } + ] + .forEach(method => { + describe(`[${method.title}]`, () => { + method.list.forEach(unit => { + describe(`[${unit.desc}]: "${unit.ua}"`, () => { + const actual = UAParser(unit.ua)[method.label]; + Object.entries(unit.expect).forEach(entry => { + const [key, val] = entry; + it(`Should return ${key}: ${val}`, () => { + assert.strictEqual(String(val), String(actual[key])); + }); + }); + }); + }); + }); + }); +}); + +describe('Returns', function () { + it('getResult() should returns JSON', function(done) { + assert.deepEqual(new UAParser('').getResult(), + { + ua : '', + browser: { name: undefined, version: undefined, major: undefined, type: undefined }, + cpu: { architecture: undefined }, + device: { vendor: undefined, model: undefined, type: undefined }, + engine: { name: undefined, version: undefined}, + os: { name: undefined, version: undefined } + }); + done(); + }); + + it('works even when Array.prototype has been mangled', function(done) { + const result = withMangledArrayProto(() => new UAParser('').getResult()); + + function withMangledArrayProto(fn, key = 'isEmpty', value = function() { return this.length === 0; }) { + const originalValue = Array.prototype[key]; + const restore = Object.hasOwnProperty.call(Array.prototype, key) + ? () => Array.prototype[key] = originalValue + : () => delete Array.prototype[key]; + + Array.prototype[key] = value; + const result = fn(); + restore(); + + return result; + } + + assert.deepEqual(result, + { + ua : '', + browser: { name: undefined, version: undefined, major: undefined, type: undefined }, + cpu: { architecture: undefined }, + device: { vendor: undefined, model: undefined, type: undefined }, + engine: { name: undefined, version: undefined}, + os: { name: undefined, version: undefined } + }); + done(); + }); +}); + +describe('Extending Regex', function () { + var uaString = 'Mozilla/5.0 MyOwnBrowser/1.3'; + var myOwnBrowser = [[/(myownbrowser)\/((\d+)?[\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, UAParser.BROWSER.MAJOR]]; + + var parser1 = new UAParser(uaString, {browser: myOwnBrowser}); + assert.strictEqual(parser1.getBrowser().name, 'MyOwnBrowser'); + assert.strictEqual(parser1.getBrowser().version, '1.3'); + assert.strictEqual(parser1.getBrowser().major, '1'); + + var parser2 = new UAParser({browser: myOwnBrowser}); + assert.strictEqual(parser2.getBrowser().name, undefined); + parser2.setUA(uaString); + assert.strictEqual(parser2.getBrowser().name, 'MyOwnBrowser'); + assert.strictEqual(parser1.getBrowser().version, '1.3'); + + let myOwnListOfBrowsers = [ + [/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']] + ]; + let myParser = new UAParser({ browser: myOwnListOfBrowsers }); + let myUA = 'Mozilla/5.0 MyBrowser/1.3'; + assert.deepEqual(myParser.setUA(myUA).getBrowser(), {name: "MyBrowser", version: "1.3", major: "1", type : "bot"}); + assert.strictEqual(myParser.getBrowser().is('bot'), true); + + let myOwnListOfDevices = [ + [/(mytab) ([\w ]+)/i], [UAParser.DEVICE.VENDOR, UAParser.DEVICE.MODEL, [UAParser.DEVICE.TYPE, UAParser.DEVICE.TABLET]], + [/(myphone)/i], [UAParser.DEVICE.VENDOR, [UAParser.DEVICE.TYPE, UAParser.DEVICE.MOBILE]] + ]; + let myParser2 = new UAParser({ + browser: myOwnListOfBrowsers, + device: myOwnListOfDevices + }); + let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max'; + 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 () { + var UA_MAX_LENGTH = 500; + + // Real data from https://stackoverflow.com/questions/654921/how-big-can-a-user-agent-string-get#answer-6595973 + var uaString = 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; (R1 1.6); SLCC1; .NET CLR 2.0.50727; InfoPath.2; OfficeLiveConnector.1.3; OfficeLivePatch.0.0; .NET CLR 3.5.30729; .NET CLR 3.0.30618; 66760635803; runtime 11.00294; 876906799603; 97880703; 669602703; 9778063903; 877905603; 89670803; 96690803; 8878091903; 7879040603; 999608065603; 799808803; 6666059903; 669602102803; 888809342903; 696901603; 788907703; 887806555703; 97690214703; 66760903; 968909903; 796802422703; 8868026703; 889803611803; 898706903; 977806408603; 976900799903; 9897086903; 88780803; 798802301603; 9966008603; 66760703; 97890452603; 9789064803; 96990759803; 99960107703; 8868087903; 889801155603; 78890703; 8898070603; 89970603; 89970539603; 89970488703; 8789007603; 87890903; 877904603; 9887077703; 798804903; 97890264603; 967901703; 87890703; 97690420803; 79980706603; 9867086703; 996602846703; 87690803; 6989010903; 977809603; 666601903; 876905337803; 89670603; 89970200903; 786903603; 696901911703; 788905703; 896709803; 96890703; 998601903; 88980703; 666604769703; 978806603; 7988020803; 996608803; 788903297903; 98770043603; 899708803; 66960371603; 9669088903; 69990703; 99660519903; 97780603; 888801803; 9867071703; 79780803; 9779087603; 899708603; 66960456803; 898706824603; 78890299903; 99660703; 9768079803; 977901591603; 89670605603; 787903608603; 998607934903; 799808573903; 878909603; 979808146703; 9996088603; 797803154903; 69790603; 99660565603; 7869028603; 896707703; 97980965603; 976907191703; 88680703; 888809803; 69690903; 889805523703; 899707703; 997605035603; 89970029803; 9699094903; 877906803; 899707002703; 786905857603; 69890803; 97980051903; 997603978803; 9897097903; 66960141703; 7968077603; 977804603; 88980603; 989700803; 999607887803; 78690772803; 96990560903; 98970961603; 9996032903; 9699098703; 69890655603; 978903803; 698905066803; 977806903; 9789061703; 967903747703; 976900550903; 88980934703; 8878075803; 8977028703; 97980903; 9769006603; 786900803; 98770682703; 78790903; 878906967903; 87690399603; 99860976703; 796805703; 87990603; 968906803; 967904724603; 999606603; 988705903; 989702842603; 96790603; 99760703; 88980166703; 9799038903; 98670903; 697905248603; 7968043603; 66860703; 66860127903; 9779048903; 89670123903; 78890397703; 97890603; 87890803; 8789030603; 69990603; 88880763703; 9769000603; 96990203903; 978900405903; 7869022803; 699905422903; 97890703; 87990903; 878908703; 7998093903; 898702507603; 97780637603; 966907903; 896702603; 9769004803; 7869007903; 99660158803; 7899099603; 8977055803; 99660603; 7889080903; 66660981603; 997604603; 6969089803; 899701903; 9769072703; 666603903; 99860803; 997608803; 69790903; 88680756703; 979805677903; 9986047703; 89970803; 66660603; 96690903; 8997051603; 789901209803; 8977098903; 968900326803; 87790703; 98770024803; 697901794603; 69990803; 887805925803; 968908903; 97880603; 897709148703; 877909476903; 66760197703; 977908603; 698902703; 988706504803; 977802026603; 88680964703; 8878068703; 987705107903; 978902878703; 8898069803; 9768031703; 79680803; 79980803; 669609328703; 89870238703; 99960593903; 969904218703; 78890603; 9788000703; 69690630903; 889800982903; 988709748803; 7968052803; 99960007803; 969900800803; 668604817603; 66960903; 78790734603; 8868007703; 79780034903; 8878085903; 976907603; 89670830803; 877900903; 969904889703; 7978033903; 8987043903; 99860703; 979805903; 667603803; 976805348603; 999604127603; 97790701603; 78990342903; 98770672903; 87990253903; 9877027703; 97790803; 877901895603; 8789076903; 896708595603; 997601903; 799806903; 97690603; 87790371703; 667605603; 99760303703; 97680283803; 788902750803; 787909803; 79780603; 79880866903; 9986050903; 87890543903; 979800803; 97690179703; 876901603; 699909903; 96990192603; 878904903; 877904734903; 796801446903; 977904803; 9887044803; 797805565603; 98870789703; 7869093903; 87790727703; 797801232803; 666604803; 9778071903; 9799086703; 6969000903; 89670903; 8799075903; 897708903; 88680903; 97980362603; 97980503903; 889803256703; 88980388703; 789909376803; 69690703; 6969025903; 89970309903; 96690703; 877901847803; 968901903; 96690603; 88680607603; 7889001703; 789904761803; 976807703; 976902903; 878907889703; 9897014903; 896707046603; 696909903; 666603998903; 969902703; 79680421803; 9769075603; 798800192703; 97990903; 9689024903; 668604803; 969908671903; 9996094703; 69990642703; 97890895903; 977805619903; 79980859903; 88980443803; 98970649603; 997602703; 888802169903; 699907803; 667602028803; 786903283903; 997607703; 969909803; 798809925903; 9976045603; 97790903; 9789001903; 966903603; 9789069603; 968906603; 6989091803; 896701603; 6979059803; 978803903; 997606362603; 88980803; 98970803; 88880921703; 8997065703; 899700703; 698908703; 797801027903; 7889050903; 87890603; 78690703; 99660069703; 97980309903; 976800603; 666606803; 898707703; 79880019803; 66960250803; 7978049803; 88780602603; 79680903; 88880792703; 96990903; 667608603; 87790730903; 98970903; 9699032903; 8987004803; 88880703; 89770046603; 978800803; 969908903; 9798022603; 696901903; 799803703; 989703703; 668605903; 79780903; 998601371703; 796803339703; 87890922603; 898708903; 9966061903; 66960891903; 96790903; 8779050803; 98870858803; 976909298603; 9887029903; 669608703; 979806903; 878903803; 99960703; 9789086703; 979801803; 66960008703; 979806830803; 99760212703; 786906603; 797807603; 789907297703; 96990703; 786901603; 796807766603; 896702651603; 789902585603; 66660925903; 9986085703; 66960302703; 69890703; 789900703; 89970903; 9679060703; 9789002903; 979908821603; 986708140803; 976809828703; 7988082803; 79680997903; 99960803; 9788081903; 979805703; 787908603; 66960602803; 9887098703; 978803237703; 888806804603; 999604703; 977904703; 966904635703; 97680291703; 977809345603; 8878046703; 988709803; 976900773603; 989703903; 88780198603; 87790603; 986708703; 78890604703; 87790544803; 976809850903; 887806703; 987707527603; 79880803; 9897059603; 897709820603; 97880804803; 66960026703; 9789062803; 9867090803; 669600603; 8967087703; 78890903; 89770903; 97980703; 976802687603; 66860400803; 979901288603; 96990160903; 99860228903; 966900703; 66760603; 9689035703; 9779064703; 7968023603; 87890791903; 98770870603; 9798005803; 6969087903; 9779097903; 6979065703; 699903252603; 79780989703; 87690901803; 978905763903; 977809703; 97790369703; 899703269603; 8878012703; 78790803; 87690395603; 8888042803; 667607689903; 8977041803; 6666085603; 6999080703; 69990797803; 88680721603; 99660519803; 889807603; 87890146703; 699906325903; 89770603; 669608615903; 9779028803; 88880603; 97790703; 79780703; 97680355603; 6696024803; 78790784703; 97880329903; 9699077703; 89870803; 79680227903; 976905852703; 8997098903; 896704796703; 66860598803; 9897036703; 66960703; 9699094703; 9699008703; 97780485903; 999603179903; 89770834803; 96790445603; 79680460903; 9867009603; 89870328703; 799801035803; 989702903; 66960758903; 66860150803; 6686088603; 9877092803; 96990603; 99860603; 987703663603; 98870903; 699903325603; 87790803; 97680703; 8868030703; 9799030803; 89870703; 97680803; 9669054803; 6979097603; 987708046603; 999608603; 878904803; 998607408903; 968903903; 696900703; 977907491703; 6686033803; 669601803; 99960290603; 887809169903; 979803703; 69890903; 699901447903; 8987064903; 799800603; 98770903; 8997068703; 967903603; 66760146803; 978805087903; 697908138603; 799801603; 88780964903; 989708339903; 8967048603; 88880981603; 789909703; 796806603; 977905977603; 989700603; 97780703; 9669062603; 88980714603; 897709545903; 988701916703; 667604694903; 786905664603; 877900803; 886805490903; 89970559903; 99960531803; 7998033903; 98770803; 78890418703; 669600872803; 996605216603; 78690962703; 667604903; 996600903; 999608903; 9699083803; 787901803; 97780707603; 787905312703; 977805803; 8977033703; 97890708703; 989705521903; 978800703; 698905703; 78890376903; 878907703; 999602903; 986705903; 668602719603; 979901803; 997606903; 66760393903; 987703603; 78790338903; 96890803; 97680596803; 666601603; 977902178803; 877902803; 78790038603; 8868075703; 99960060603)'; + + it('greater than ' + UA_MAX_LENGTH + ' should be trimmed down', function () { + assert.strictEqual(UAParser(uaString).ua.length, UA_MAX_LENGTH); + }); +}); + +describe('Using Require.js', function () { + it('should loaded automatically', function(done) { + requirejs.config({ + baseUrl : 'dist', + paths : { + 'ua-parser-js' : 'ua-parser.min' + } + }); + requirejs(['ua-parser-js'], function(ua) { + var parser = new ua('Dillo/1.0'); + assert.deepStrictEqual(parser.getBrowser().name, 'Dillo'); + done(); + }); + }); +}); + +describe('Testing regexes', function () { + + var regexes; + + before('Read main js file', function () { + var code = fs.readFileSync('src/main/ua-parser.js', 'utf8').toString(); + var ast = parseJS(code, { sourceType: "script" }); + regexes = []; + traverse(ast, { + RegExpLiteral: (path) => { + regexes.push(path.node.pattern); + } + }); + + if (regexes.length === 0) { + throw new Error("Regexes cannot be empty!"); + } + }); + + describe('Begin testing', function () { + it('all regexes in main file', function () { + describe('Test against `safe-regex` module', function () { + regexes.forEach(function (regex) { + it(`Should pass \`safe-regex\`: ${regex}`, function () { + assert.strictEqual(safe(regex), true); + }); + }); + }); + }); + }); +}); + + +describe('is() utility method', function () { + let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); + + it('Should match full name', function () { + assert.strictEqual(uap.getBrowser().name, "IEMobile"); + assert.strictEqual(uap.getBrowser().is("IEMobile"), true); + assert.strictEqual(uap.getBrowser().is("IE"), false); + assert.strictEqual(uap.getBrowser().is("11.0"), false); + }); + + it('Should ignore "Browser" suffix', function () { + assert.strictEqual(uap.getBrowser().is("IEMobile Browser"), true); + }); + + it('Should ignore case', function () { + assert.strictEqual(uap.getEngine().name, "Trident"); + assert.strictEqual(uap.getEngine().is("tRiDeNt"), true); + assert.strictEqual(uap.getEngine().is("7.0"), false); + }); + + it('Should get exact name', function () { + assert.strictEqual(uap.getOS().name, "Windows Phone"); + assert.strictEqual(uap.getOS().is("Windows Phone"), true); + assert.strictEqual(uap.getOS().is("Windows Phone OS"), true); + assert.strictEqual(uap.getOS().is("Windows Mobile"), false); + assert.strictEqual(uap.getOS().is("Android"), false); + }); + + it('Should check all device properties', function () { + assert.deepEqual(uap.getDevice(), { + vendor : "Nokia", + model : "Lumia 635", + type : "mobile" + }); + assert.strictEqual(uap.getDevice().is("Nokia"), true); + assert.strictEqual(uap.getDevice().is("Lumia 635"), true); + assert.strictEqual(uap.getDevice().is("mobile"), true); + + assert.strictEqual(uap.getResult().device.is("Nokia"), true); + }); + + it('Should get result after reassignment', function () { + uap.setUA("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"); + assert.strictEqual(uap.getOS().name, "macOS"); + assert.strictEqual(uap.getOS().is("Mac OS"), true); + assert.strictEqual(uap.getOS().is("macOS"), true); + assert.strictEqual(uap.getOS().is("mac OS"), true); + + assert.strictEqual(uap.getOS().is("M ac"), false); + assert.strictEqual(uap.getOS().is("M a c "), false); + assert.strictEqual(uap.getOS().is("Mac OS OS"), false); + assert.strictEqual(uap.getOS().is("Mac OS X"), false); + + assert.strictEqual(uap.getBrowser().is("Chrome"), true); + assert.strictEqual(uap.getEngine().is("Blink"), true); + }); + + it('Should refrain from "undefined" until all properties are checked', function () { + assert.strictEqual(uap.getDevice().is("undefined"), false); + assert.strictEqual(uap.getDevice().is("Apple"), true); + + uap.setUA(""); + assert.strictEqual(uap.getDevice().model, undefined); + assert.strictEqual(uap.getDevice().is("undefined"), false); + assert.strictEqual(uap.getDevice().is(undefined), true); + }); + + //it('Should accept arch equivalent name', function () { + it('Should accept exact arch name', function () { + uap.setUA("Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:19.0) Gecko/20100101 Firefox/19.0"); + assert.strictEqual(uap.getCPU().architecture, "ia32"); + assert.strictEqual(uap.getCPU().is("ia32"), true); + assert.strictEqual(uap.getCPU().is("x86"), false); + + uap.setUA("Opera/9.80 (X11; Linux x86_64; U; Linux Mint; en) Presto/2.2.15 Version/10.10"); + assert.strictEqual(uap.getCPU().architecture, "amd64"); + assert.strictEqual(uap.getCPU().is("amd64"), true); + assert.strictEqual(uap.getCPU().is("x86-64"), false); + assert.strictEqual(uap.getCPU().is("x64"), false); + }); +}); + +describe('toString() utility method', function () { + it('Should return full name', function () { + let uap = new UAParser('Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 635) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537'); + assert.strictEqual(uap.getBrowser().name, "IEMobile"); + assert.strictEqual(uap.getBrowser().version, "11.0"); + assert.strictEqual(uap.getBrowser().major, "11"); + assert.strictEqual(uap.getBrowser().toString(), "IEMobile 11.0"); + + assert.strictEqual(uap.getCPU().architecture, "arm"); + assert.strictEqual(uap.getCPU().toString(), "arm"); + + assert.strictEqual(uap.getDevice().vendor, "Nokia"); + assert.strictEqual(uap.getDevice().model, "Lumia 635"); + assert.strictEqual(uap.getDevice().type, "mobile"); + assert.strictEqual(uap.getDevice().toString(), "Nokia Lumia 635"); + + assert.strictEqual(uap.getEngine().name, "Trident"); + assert.strictEqual(uap.getEngine().version, "7.0"); + assert.strictEqual(uap.getEngine().toString(), "Trident 7.0"); + + assert.strictEqual(uap.getOS().name, "Windows Phone"); + assert.strictEqual(uap.getOS().version, "8.1"); + assert.strictEqual(uap.getOS().toString(), "Windows Phone 8.1"); + }); +}); + +describe('Read user-agent data from req.headers', function () { + const ua = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)'; + const ext = { + engine : [ + [/(msie)/i], [[UAParser.ENGINE.NAME, 'Custom Browser 1']], + [/(edge)/i], [[UAParser.ENGINE.NAME, 'Custom Browser 2']] + ] + }; + const req = { + headers : { + 'user-agent' : 'Mozilla/5.0 (Windows NT 6.4; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36 Edge/12.0' + } + }; + + it('Can be called with UAParser(ua)', function () { + let engine = UAParser(ua).engine; + assert.strictEqual(engine.name, "Trident"); + }); + + it('Can be called with UAParser(ua, extensions)', function () { + let engine = UAParser(ua, ext).engine; + assert.strictEqual(engine.name, "Custom Browser 1"); + }); + + it('Can be called with UAParser(ua, extensions, headers)', function () { + let engine = UAParser(ua, ext, req.headers).engine; + assert.strictEqual(engine.name, "Custom Browser 1"); + }); + + it('Can be called with UAParser(ua, headers)', function () { + let engine = UAParser(ua, req.headers).engine; + assert.strictEqual(engine.name, "Trident"); + }); + + it('Can be called with UAParser(extensions, headers)', function () { + let engine = UAParser(ext, req.headers).engine; + assert.strictEqual(engine.name, "Custom Browser 2"); + }); + + it('Can be called with UAParser(headers)', function () { + let engine = UAParser(req.headers).engine; + assert.strictEqual(engine.name, "EdgeHTML"); + }); + + it('Fetch API\'s Header can be passed directly into headers', () => { + const reqHeaders = new Headers(); + reqHeaders.append('User-Agent', 'Midori/0.2.2 (X11; Linux i686; U; en-us) WebKit/531.2+'); + const { browser } = UAParser(reqHeaders); + assert.strictEqual(browser.is('Midori'), true); + }); +}); diff --git a/test/unit/ua-ch.js b/test/unit/ua-ch.js new file mode 100644 index 000000000..7d12d5023 --- /dev/null +++ b/test/unit/ua-ch.js @@ -0,0 +1,635 @@ +const assert = require('assert'); +const { UAParser } = require('../../src/main/ua-parser'); + +describe('Map UA-CH headers', () => { + + const headers = { + 'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"', + 'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"', + 'sec-ch-ua-arch' : '"arm"', + 'sec-ch-ua-bitness' : '"64"', + 'sec-ch-ua-mobile' : '?1', + 'sec-ch-ua-model' : '"Pixel 99"', + 'sec-ch-ua-platform' : '"Windows"', + 'sec-ch-ua-platform-version' : '"13"', + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' + }; + + let uap = UAParser(headers).withClientHints(); + let browser = new UAParser(headers).getBrowser().withClientHints(); + let cpu = new UAParser(headers).getCPU().withClientHints(); + let device = new UAParser(headers).getDevice().withClientHints(); + let engine = new UAParser(headers).getEngine().withClientHints(); + let os = new UAParser(headers).getOS().withClientHints(); + + it('Can read from client-hints headers using `withClientHints()`', () => { + + assert.strictEqual(uap.ua, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "93.0.1.2"); + assert.strictEqual(uap.browser.major, "93"); + assert.strictEqual(browser.name, "Chrome"); + assert.strictEqual(browser.version, "93.0.1.2"); + assert.strictEqual(browser.major, "93"); + assert.strictEqual(uap.cpu.architecture, "arm64"); + assert.strictEqual(cpu.architecture, "arm64"); + assert.strictEqual(uap.device.type, "mobile"); + assert.strictEqual(uap.device.model, "Pixel 99"); + assert.strictEqual(uap.device.vendor, "Google"); + assert.strictEqual(device.type, "mobile"); + assert.strictEqual(device.model, "Pixel 99"); + assert.strictEqual(device.vendor, "Google"); + assert.strictEqual(uap.engine.name, 'Blink'); + assert.strictEqual(uap.engine.version, '93.0.1.2'); + assert.strictEqual(engine.name, 'Blink'); + assert.strictEqual(engine.version, '93.0.1.2'); + assert.strictEqual(uap.os.name, "Windows"); + assert.strictEqual(uap.os.version, "11"); + assert.strictEqual(os.name, "Windows"); + assert.strictEqual(os.version, "11"); + }); + + it('Only read from user-agent header when called without `withClientHints()`', () => { + + uap = UAParser(headers); + browser = new UAParser(headers).getBrowser(); + cpu = new UAParser(headers).getCPU(); + device = new UAParser(headers).getDevice(); + engine = new UAParser(headers).getEngine(); + os = new UAParser(headers).getOS(); + + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "110.0.0.0"); + assert.strictEqual(uap.browser.major, "110"); + assert.strictEqual(uap.cpu.architecture, "amd64"); + assert.strictEqual(uap.device.type, undefined); + assert.strictEqual(uap.device.model, undefined); + assert.strictEqual(uap.device.vendor, undefined); + assert.strictEqual(uap.engine.name, 'Blink'); + assert.strictEqual(uap.engine.version, '110.0.0.0'); + assert.strictEqual(uap.os.name, "Linux"); + assert.strictEqual(uap.os.version, undefined); + }); + + it('Fallback to user-agent header when using `withClientHints()` but found no client hints-related headers', () => { + + const headers2 = { + 'sec-ch-ua-mobile' : '?1', + 'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36' + }; + + uap = UAParser(headers2).withClientHints(); + + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "110.0.0.0"); + assert.strictEqual(uap.browser.major, "110"); + assert.strictEqual(uap.cpu.architecture, "amd64"); + assert.strictEqual(uap.device.type, "mobile"); + assert.strictEqual(uap.device.model, undefined); + assert.strictEqual(uap.device.vendor, undefined); + assert.strictEqual(uap.engine.name, 'Blink'); + assert.strictEqual(uap.engine.version, '110.0.0.0'); + assert.strictEqual(uap.os.name, "Linux"); + assert.strictEqual(uap.os.version, undefined); + }); + + it('Can detect Apple silicon from client hints data', () => { + + // https://github.com/faisalman/ua-parser-js/issues/489#issuecomment-1479213579 + const httpHeadersFromAppleSilicon = { + 'sec-ch-ua-arch' : 'arm', + 'sec-ch-ua-platform' : 'macOS', + 'sec-ch-ua-mobile' : '?0', + 'sec-ch-ua' : '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"', + 'user-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0' + }; + + UAParser(httpHeadersFromAppleSilicon).withClientHints().then(ua => { + + // Only works in Chrome + /* + if (ua.os.is("macOS") && + ua.cpu.is("arm") && + !ua.device.is("mobile") && + !ua.device.is("tablet")) { + // possibly an Apple silicon device + } + */ + + assert.strictEqual(ua.os.is("macOS"), true); + assert.strictEqual(ua.cpu.is("arm"), true); + assert.strictEqual(ua.device.is("mobile"), false); + assert.strictEqual(ua.device.is("tablet"), false); + }); + }); + + it('Can detect form-factors from client-hints', () => { + + const FFVR = { + 'sec-ch-ua-form-factors' : '"VR"' + }; + + const FFEInk = { + 'sec-ch-ua-form-factors' : '"Tablet", "EInk"' + }; + + const FFUnknown = { + 'sec-ch-ua-form-factors' : '"Unknown"' + }; + + UAParser(FFVR).withClientHints().then(ua => { + assert.strictEqual(ua.device.type, 'xr'); + }); + + UAParser(FFEInk).withClientHints().then(ua => { + assert.strictEqual(ua.device.type, 'tablet'); + }); + + + UAParser(FFUnknown).withClientHints().then(ua => { + assert.strictEqual(ua.device.type, undefined); + }); + }); + + it('Avoid error on headers variation', () => { + + const headers2 = { + 'sec-ch-ua' : '"Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24"', + 'sec-ch-ua-full-version-list' : '"Google Chrome", "Chromium", "Not?A_Brand";v="24.0.0.0"', + 'sec-ch-ua-full-version' : '""', + 'sec-ch-ua-mobile' : '?0', + 'sec-ch-ua-arch' : '""', + 'sec-ch-ua-bitness' : '""', + 'sec-ch-ua-model' : '""', + 'sec-ch-ua-platform' : '"Windows"', + 'sec-ch-ua-platform-version' : '""', + 'sec-ch-ua-wow64' : '?0', + }; + + uap = UAParser(headers2).withClientHints(); + + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, undefined); + assert.strictEqual(uap.browser.major, undefined); + }); + + it('Prioritize more specific brand name regardless the order', () => { + + const headers3a = { + 'sec-ch-ua-full-version-list' : '"Not_A Brand;v=8, Chromium;v=120.0.6099.131, Google Chrome;v=120.0.6099.132"' + }; + const headers3b = { + 'sec-ch-ua-full-version-list' : '"Chromium;v=120.0.6099.131, Not_A Brand;v=8, Google Chrome;v=120.0.6099.132"' + }; + const headers3c = { + 'sec-ch-ua-full-version-list' : '"Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131, Not_A Brand;v=8"' + }; + const headers3d = { + 'sec-ch-ua-full-version-list' : '"Microsoft Edge;v=120.0.6099.133, Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131, Not_A Brand;v=8"' + }; + const headers3e = { + 'sec-ch-ua-full-version-list' : '"Chromium;v=120.0.6099.131, Google Chrome;v=120.0.6099.132, Microsoft Edge;v=120.0.6099.133, Not_A Brand;v=8"' + }; + const headers3f = { + 'sec-ch-ua-full-version-list' : '"Not_A Brand;v=8, Microsoft Edge;v=120.0.6099.133, Google Chrome;v=120.0.6099.132, Chromium;v=120.0.6099.131"' + }; + + uap = UAParser(headers3a).withClientHints(); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "120.0.6099.132"); + + uap = UAParser(headers3b).withClientHints(); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "120.0.6099.132"); + + uap = UAParser(headers3c).withClientHints(); + assert.strictEqual(uap.browser.name, "Chrome"); + assert.strictEqual(uap.browser.version, "120.0.6099.132"); + + uap = UAParser(headers3d).withClientHints(); + assert.strictEqual(uap.browser.name, "Edge"); + assert.strictEqual(uap.browser.version, "120.0.6099.133"); + + uap = UAParser(headers3e).withClientHints(); + assert.strictEqual(uap.browser.name, "Edge"); + assert.strictEqual(uap.browser.version, "120.0.6099.133"); + + uap = UAParser(headers3f).withClientHints(); + assert.strictEqual(uap.browser.name, "Edge"); + assert.strictEqual(uap.browser.version, "120.0.6099.133"); + }); +}); + +describe('UA-CH Headers tests', () => { + [ + { + headers : { + 'sec-ch-ua': '"Avast Secure Browser";v="131", "Chromium";v="131", "Not_A Brand";v="24"' + }, + expect: { + browser : { + name : 'Avast Secure Browser', + version : '131', + major : '131', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Not A(Brand";v="8", "Chromium";v="132", "Brave";v="132"' + }, + expect: { + browser : { + name : 'Brave', + version : '132', + major : '132', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Google Chrome";v="111", "Not(A:Brand";v="8", "Chromium";v="111"' + }, + expect: { + browser : { + name : 'Chrome', + version : '111', + major : '111', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Chromium";v="124", "HeadlessChrome";v="124", "Not-A.Brand";v="99"' + }, + expect: { + browser : { + name : 'Chrome Headless', + version : '124', + major : '124', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Android WebView";v="123", "Not:A-Brand";v="8", "Chromium";v="123"' + }, + expect: { + browser : { + name : 'Chrome WebView', + version : '123', + major : '123', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"DuckDuckGo";v="131", "Chromium";v="131", "Not_A Brand";v="24"' + }, + expect : { + browser : { + name : 'DuckDuckGo', + version : '131', + major : '131', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"' + }, + expect: { + browser : { + name : 'Edge', + version : '120', + major : '120', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '" Not;A Brand";v="99", "Microsoft Edge";v="103", "Chromium";v="103", "Microsoft Edge WebView2";v="104"' + }, + expect: { + browser : { + name : 'Edge WebView2', + version : '104', + major : '104', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "HuaweiBrowser";v="114"' + }, + expect: { + browser : { + name : 'Huawei Browser', + version : '114', + major : '114', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Miui Browser";v="123", "Not:A-Brand";v="8", "Chromium";v="123"' + }, + expect: { + browser : { + name : 'MIUI Browser', + version : '123', + major : '123', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Chromium";v="130", "Oculus Browser";v="36", "Not?A_Brand";v="99"' + }, + expect: { + browser : { + name : 'Oculus Browser', + version : '36', + major : '36', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Opera";v="116", "Chromium";v="131", "Not_A Brand";v="24"' + }, + expect: { + browser : { + name : 'Opera', + version : '116', + major : '116', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Chromium";v="128", "Not;A=Brand";v="24", "Opera GX";v="114"' + }, + expect: { + browser : { + name : 'Opera GX', + version : '114', + major : '114', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"OperaMobile";v="86", ";Not A Brand";v="99", "Opera";v="115", "Chromium";v="130"' + }, + expect: { + browser : { + name : 'Opera Mobi', + version : '86', + major : '86', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Chromium";v="132", "OperaMobile";v="87", "Opera";v="117", " Not A;Brand";v="99"' + }, + expect: { + browser : { + name : 'Opera Mobi', + version : '87', + major : '87', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Chromium";v="125", "Not.A/Brand";v="24", "Samsung Internet";v="27.0"' + }, + expect: { + browser : { + name : 'Samsung Internet', + version : '27.0', + major : '27', + type : undefined + } + } + }, + { + headers : { + 'sec-ch-ua': '"Chromium";v="130", "YaBrowser";v="24.12", "Not?A_Brand";v="99", "Yowser";v="2.5"' + }, + expect: { + browser : { + name : 'Yandex', + version : '24.12', + major : '24', + type : undefined + } + } + }, + ] + .forEach(test => { + const { browser } = UAParser(test.headers).withClientHints(); + assert.deepEqual(browser, test.expect.browser); + }); +}); + +describe('Identify vendor & type of device from given model name', () => { + [ + { + model: '220733SG', + expect: { + vendor : 'Xiaomi', + type : 'mobile' + } + }, + { + model: '5087Z', + expect: { + vendor : 'TCL', + type : 'mobile' + } + }, + { + model: '9137W', + expect: { + vendor : 'TCL', + type : 'tablet' + } + }, + { + model: 'BE2015', + expect: { + vendor : 'OnePlus', + type : 'mobile' + } + }, + { + model: 'CPH2389', + expect: { + vendor : 'OnePlus', + type : 'mobile' + } + }, + { + model: 'Infinix X669C', + expect: { + vendor : 'Infinix', + type : 'mobile' + } + }, + { + model: 'itel L6502', + expect: { + vendor : 'itel', + type : 'mobile' + } + }, + { + model: 'Lenovo TB-X606F', + expect: { + vendor : 'Lenovo', + type : 'tablet' + } + }, + { + model: 'LM-Q720', + expect: { + vendor : 'LG', + type : 'mobile' + } + }, + { + model: 'M2003J15SC', + expect: { + vendor : 'Xiaomi', + type : 'mobile' + } + }, + { + model: 'MAR-LX1A', + expect: { + vendor : 'Huawei', + type : 'mobile' + } + }, + { + model: 'moto g(20)', + expect: { + vendor : 'Motorola', + type : 'mobile' + } + }, + { + model: 'Nokia C210', + expect: { + vendor : 'Nokia', + type : 'mobile' + } + }, + { + model: 'Pixel 8', + expect: { + vendor : 'Google', + type : 'mobile' + } + }, + { + model: 'Redmi Note 9S', + expect: { + vendor : 'Xiaomi', + type : 'mobile' + } + }, + { + model: 'RMX3830', + expect: { + vendor : 'Realme', + type : 'mobile' + } + }, + { + model: 'SM-S536DL', + expect: { + vendor : 'Samsung', + type : 'mobile' + } + }, + { + model: 'SM-S546VL', + expect: { + vendor : 'Samsung', + type : 'mobile' + } + }, + { + model: 'SM-T875', + expect: { + vendor : 'Samsung', + type : 'tablet' + } + }, + { + model: 'STK-L21', + expect: { + vendor : 'Huawei', + type : 'mobile' + } + }, + { + model: 'T430W', + expect: { + vendor : 'TCL', + type : 'mobile' + } + }, + { + model: 'TECNO KI5k', + expect: { + vendor : 'TECNO', + type : 'mobile' + } + }, + { + model: 'vivo 1820', + expect: { + vendor : 'Vivo', + type : 'mobile' + } + }, + { + model: 'Xbox', + expect: { + vendor : 'Microsoft', + type : 'console' + } + } + ] + .forEach(test => { + it(`Solve "${test.model}"`, () => { + const headers = { + 'sec-ch-ua-model' : test.model, + }; + const { device } = UAParser(headers).withClientHints(); + assert.strictEqual(device.model, test.model); + assert.strictEqual(device.vendor, test.expect.vendor); + assert.strictEqual(device.type, test.expect.type); + }); + }); +}); \ No newline at end of file