mirror of
https://github.com/faisalman/ua-parser-js.git
synced 2025-11-16 15:11:50 +03:00
Compare commits
47 Commits
2.0.0-alph
...
2.0.0-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d168b75a3a | ||
|
|
6e26e38247 | ||
|
|
3cb567c154 | ||
|
|
f76d8983ca | ||
|
|
22eae9f70c | ||
|
|
f2e4b242ce | ||
|
|
153831d2ed | ||
|
|
072a82b87b | ||
|
|
15d17e97a1 | ||
|
|
df9144b493 | ||
|
|
a519d2b879 | ||
|
|
102dc51683 | ||
|
|
1a806453f9 | ||
|
|
3d5c70457e | ||
|
|
a74ebeb82e | ||
|
|
4c77c5ef21 | ||
|
|
9102871dea | ||
|
|
1653d376ca | ||
|
|
29fb85658a | ||
|
|
c3be7326b8 | ||
|
|
5a26ac146e | ||
|
|
35c2b91534 | ||
|
|
feefb81cd0 | ||
|
|
0ac5028137 | ||
|
|
1b17315935 | ||
|
|
f92bb9ef65 | ||
|
|
f659659500 | ||
|
|
6c58ac39cd | ||
|
|
432a2ee72d | ||
|
|
c2f17004b8 | ||
|
|
f5af76a2b3 | ||
|
|
99baf60d50 | ||
|
|
46f38adb83 | ||
|
|
ff26813708 | ||
|
|
e62cded083 | ||
|
|
2fb0c72898 | ||
|
|
801c2409b3 | ||
|
|
a8951ec282 | ||
|
|
07c9e36ebe | ||
|
|
407b23262c | ||
|
|
16b416d9ea | ||
|
|
05747dba37 | ||
|
|
625ece73e2 | ||
|
|
e01663b48f | ||
|
|
59d8d836c2 | ||
|
|
b385a73340 | ||
|
|
4711805a1c |
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**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.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**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.
|
||||
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/custom.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Custom issue template
|
||||
about: Describe this issue template's purpose here.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -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.
|
||||
78
.github/workflows/analysis-codeql.yml
vendored
Normal file
78
.github/workflows/analysis-codeql.yml
vendored
Normal file
@@ -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}}"
|
||||
20
.github/workflows/analysis-dependency.yml
vendored
Normal file
20
.github/workflows/analysis-dependency.yml
vendored
Normal file
@@ -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
|
||||
72
.github/workflows/analysis-scorecard.yml
vendored
Normal file
72
.github/workflows/analysis-scorecard.yml
vendored
Normal file
@@ -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
|
||||
22
.github/workflows/publish-github-packages.yml
vendored
Normal file
22
.github/workflows/publish-github-packages.yml
vendored
Normal file
@@ -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 }}
|
||||
23
.github/workflows/publish-npm-packages.yml
vendored
Normal file
23
.github/workflows/publish-npm-packages.yml
vendored
Normal file
@@ -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 }}
|
||||
@@ -1,4 +1,4 @@
|
||||
name: ua-parser-js-run-test
|
||||
name: UAParser.js CI-Test
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
@@ -12,9 +12,12 @@ jobs:
|
||||
matrix:
|
||||
arch: [amd64, ppc64le]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- name: Run the test
|
||||
run: |
|
||||
npm install
|
||||
npm run test-ci
|
||||
npm ci
|
||||
npx playwright install
|
||||
npm test
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
playwright-report/
|
||||
test-results/
|
||||
|
||||
### vim ###
|
||||
.*.s[a-w][a-z]
|
||||
|
||||
18
.travis.yml
18
.travis.yml
@@ -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
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ua-parser-js",
|
||||
"version": "2.0.0-alpha.2",
|
||||
"version": "2.0.0-alpha.3",
|
||||
"authors": [
|
||||
"Faisal Salman <f@faisalman.com>"
|
||||
],
|
||||
|
||||
@@ -7,11 +7,19 @@
|
||||
- What's new:
|
||||
- Add some new methods in result object:
|
||||
- Add support for client hints: `withClientHints()`
|
||||
- Add support for feature detection: `withFeatureCheck()`
|
||||
- Utility for easy comparison: `is()`
|
||||
- Utility to print full-name: `toString()`
|
||||
- Add support for ES module `import { UAParser } from 'ua-parser-js'`
|
||||
- Provide Enums `'ua-parser-js/enums'`
|
||||
- Provide Extensions `'ua-parser-js/extensions'`
|
||||
- Provide Helpers `'ua-parser-js/helpers'`
|
||||
|
||||
## Version 2.0.0-alpha.3
|
||||
- Add `withFeatureCheck()` method
|
||||
- Add `isFrozenUA()` method in `helpers` submodule
|
||||
- Add `MediaPlayers` & `Modules` in `extensions` submodule
|
||||
- Fix issue with ESM import
|
||||
|
||||
## Version 2.0.0-alpha.2
|
||||
- Fix browser result always returning Chromium when using `withClientHints()`
|
||||
|
||||
4
dist/ua-parser.min.js
vendored
4
dist/ua-parser.min.js
vendored
File diff suppressed because one or more lines are too long
4
dist/ua-parser.pack.js
vendored
4
dist/ua-parser.pack.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 161.93 86.82" style="enable-background:new 0 0 161.93 86.82;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#232628;}
|
||||
.st1{fill:#E62E3A;}
|
||||
.st2{fill:none;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M116.93,0.68H103.5V17.5h13.43c15.24,0,25.53,10.53,25.53,25.53v0.24c0,15-10.28,25.29-25.53,25.29H103.5
|
||||
v16.82h13.43c26.62,0,45-18.51,45-42.34v-0.24C161.93,18.95,143.54,0.68,116.93,0.68"/>
|
||||
<path class="st1" d="M8.11,0.68h52.02v16.33H24.07l-0.97,14.64c3.39-0.85,6.41-1.45,11.13-1.45c16.7,0,29.64,7.99,29.64,27.22
|
||||
c0,18.03-12.7,29.4-32.18,29.4c-13.55,0-23.47-4.84-31.7-12.7l11.25-13.43c6.41,5.81,12.7,9.19,20.2,9.19
|
||||
c8.71,0,14.15-4.23,14.15-11.86c0-7.38-5.93-11.61-15-11.61c-5.32,0-10.16,1.45-14.16,3.15L5.69,42.42L8.11,0.68z"/>
|
||||
<polygon class="st1" points="86.14,0 98.84,0 98.84,85.29 80.45,85.29 80.45,18.87 65.69,22.5 61.82,7.26 "/>
|
||||
</g>
|
||||
<rect x="144.06" y="110.12" class="st2" width="566.93" height="113.39"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
3775
package-lock.json
generated
Normal file
3775
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
Package.describe({
|
||||
name: 'faisalman:ua-parser-js',
|
||||
version: '2.0.0-alpha.2',
|
||||
version: '2.0.0-alpha.3',
|
||||
summary: 'Lightweight JavaScript-based user-agent string parser',
|
||||
git: 'https://github.com/faisalman/ua-parser-js.git',
|
||||
documentation: 'readme.md'
|
||||
|
||||
59
package.json
59
package.json
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"title": "UAParser.js",
|
||||
"name": "ua-parser-js",
|
||||
"version": "2.0.0-alpha.2",
|
||||
"version": "2.0.0-alpha.3",
|
||||
"author": "Faisal Salman <f@faisalman.com> (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",
|
||||
@@ -141,20 +142,25 @@
|
||||
"Zach Bjornson <zbbjornson@gmail.com>"
|
||||
],
|
||||
"type": "commonjs",
|
||||
"main": "src/ua-parser.js",
|
||||
"module": "src/ua-parser.mjs",
|
||||
"main": "src/main/ua-parser.js",
|
||||
"module": "src/main/ua-parser.mjs",
|
||||
"browser": "dist/ua-parser.pack.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"require": "./src/ua-parser.js",
|
||||
"import": "./src/ua-parser.mjs"
|
||||
"require": "./src/main/ua-parser.js",
|
||||
"import": "./src/main/ua-parser.mjs"
|
||||
},
|
||||
"./enums": {
|
||||
"require": "./src/enum/ua-parser-enum.js",
|
||||
"import": "./src/enum/ua-parser-enum.mjs"
|
||||
"require": "./src/enums/ua-parser-enums.js",
|
||||
"import": "./src/enums/ua-parser-enums.mjs"
|
||||
},
|
||||
"./extensions": {
|
||||
"require": "./src/extension/ua-parser-extension.js",
|
||||
"import": "./src/extension/ua-parser-extension.mjs"
|
||||
"require": "./src/extensions/ua-parser-extensions.js",
|
||||
"import": "./src/extensions/ua-parser-extensions.mjs"
|
||||
},
|
||||
"./helpers": {
|
||||
"require": "./src/helpers/ua-parser-helpers.js",
|
||||
"import": "./src/helpers/ua-parser-helpers.mjs"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
@@ -162,32 +168,24 @@
|
||||
"src"
|
||||
],
|
||||
"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 && node -e \"const fs=require('fs');fs.writeFileSync('src/ua-parser.mjs','// Generated ESM version of UAParser.js\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/ua-parser.js\\n\\nconst window = undefined;\\n\\n'+fs.readFileSync('src/ua-parser.js','utf-8').replace(/\\(func[\\s\\S]+strict\\';/ig,'').replace(/\\/[\\/\\s]+export[\\s\\S]+/ig,'export {UAParser};'),'utf-8');fs.writeFileSync('src/enum/ua-parser-enum.mjs','// Generated ESM version of UAParser.js enums\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/enum/ua-parser-enum.js\\n\\n'+fs.readFileSync('src/enum/ua-parser-enum.js','utf-8').replace(/module\\.exports =/ig,'export'),'utf-8');fs.writeFileSync('src/extension/ua-parser-extension.mjs','// Generated ESM version of UAParser.js extensions\\n// DO NOT EDIT THIS FILE!\\n// Source: /src/extension/ua-parser-extension.js\\n\\n'+fs.readFileSync('src/extension/ua-parser-extension.js','utf-8').replace(/const UA.+\\)/ig,'import UAParser from \\'ua-parser-js\\'').replace(/module\\.exports =/ig,'export'),'utf-8')\"",
|
||||
"test": "jshint src/ua-parser.js && mocha -R nyan test",
|
||||
"test-ci": "jshint src/ua-parser.js && mocha -R spec test",
|
||||
"verup": "node ./node_modules/verup",
|
||||
"version": "node ./node_modules/verup 0"
|
||||
},
|
||||
"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})"
|
||||
]
|
||||
"build": "./script/build-dist.sh && ./script/build-module.js",
|
||||
"fuzz": "jazzer ./test/jazzer-fuzz-test.js --sync",
|
||||
"test": "./script/test-all.sh",
|
||||
"test:jshint": "jshint src && jshint script",
|
||||
"test:lockfile-lint": "npx lockfile-lint -p package-lock.json",
|
||||
"test:mocha": "mocha -R list test/mocha*js",
|
||||
"test:playwright": "playwright test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/parser": "7.15.8",
|
||||
"@babel/traverse": "7.15.4",
|
||||
"jshint": "~2.12.0",
|
||||
"@jazzer.js/core": "^1.4.0",
|
||||
"@playwright/test": "~1.32.2",
|
||||
"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"
|
||||
"uglify-js": "~3.12.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -199,6 +197,7 @@
|
||||
},
|
||||
"directories": {
|
||||
"dist": "dist",
|
||||
"script": "script",
|
||||
"src": "src",
|
||||
"test": "test"
|
||||
},
|
||||
|
||||
166
readme.md
166
readme.md
@@ -3,11 +3,14 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/faisalman/ua-parser-js"><img src="https://travis-ci.org/faisalman/ua-parser-js.svg?branch=master"></a>
|
||||
<a href="https://www.npmjs.com/package/ua-parser-js"><img src="https://img.shields.io/npm/v/ua-parser-js.svg"></a>
|
||||
<a href="https://www.npmjs.com/package/ua-parser-js"><img src="https://img.shields.io/npm/dw/ua-parser-js.svg"></a>
|
||||
<a href="https://www.jsdelivr.com/package/npm/ua-parser-js"><img src="https://data.jsdelivr.com/v1/package/npm/ua-parser-js/badge"></a>
|
||||
<a href="https://cdnjs.com/libraries/UAParser.js"><img src="https://img.shields.io/cdnjs/v/UAParser.js.svg"></a>
|
||||
<a href="https://www.npmjs.com/package/ua-parser-js"><img src="https://img.shields.io/npm/dw/ua-parser-js?color=red&logo=npm&label=NPM%20DOWNLOADS&style=for-the-badge"></a>
|
||||
<a href="https://www.jsdelivr.com/package/npm/ua-parser-js"><img src="https://img.shields.io/jsdelivr/gh/hw/faisalman/ua-parser-js?logo=jsdelivr&style=for-the-badge"></a>
|
||||
<a href="https://github.com/faisalman/ua-parser-js"><img src="https://img.shields.io/github/stars/faisalman/ua-parser-js?color=yellow&logo=github&style=for-the-badge"></a>
|
||||
<a href="https://bundlephobia.com/package/ua-parser-js@1.0.35"><img src="https://img.shields.io/bundlephobia/minzip/ua-parser-js?logo=hackthebox&logoColor=white&style=for-the-badge"/></a>
|
||||
<a href="https://github.com/faisalman/ua-parser-js/graphs/contributors"><img src="https://img.shields.io/github/contributors/faisalman/ua-parser-js?color=purple&logo=githubsponsors&style=for-the-badge"></a>
|
||||
<a href="https://www.npmjs.com/package/ua-parser-js"><img src="https://img.shields.io/npm/v/ua-parser-js.svg?logo=npm&color=red&style=for-the-badge"></a>
|
||||
<a href="https://cdnjs.com/libraries/UAParser.js"><img src="https://img.shields.io/cdnjs/v/UAParser.js.svg?color=orange&style=for-the-badge"></a>
|
||||
<img src="https://img.shields.io/ossf-scorecard/github.com/faisalman/ua-parser-js?label=openssf%20scorecard&style=for-the-badge">
|
||||
</p>
|
||||
|
||||
# UAParser.js
|
||||
@@ -17,6 +20,9 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
|
||||
* Author : Faisal Salman <<f@faisalman.com>>
|
||||
* Demo : https://faisalman.github.io/ua-parser-js
|
||||
* Source : https://github.com/faisalman/ua-parser-js
|
||||
* Documentation :
|
||||
* v1 : https://github.com/faisalman/ua-parser-js/tree/1.0.35#documentation
|
||||
* v2 : https://faisalman.github.io/ua-parser-js-docs/v2
|
||||
|
||||
***
|
||||
|
||||
@@ -26,14 +32,6 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" width="200px" rowspan="2"><a href="https://www.npmjs.com/package/@51degrees/ua-parser-js"><img src="images/51degrees.svg" alt="51degrees" width="75%" height="75%" ></a></td>
|
||||
<td align="left" width="400px"><a href="https://www.npmjs.com/package/@51degrees/ua-parser-js">↗ @51degrees/ua-parser-js</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><br/><p>UAParser.js has been upgraded to detect comprehensive device data based on the User-Agent and User-Agent Client Hints.</p><p>This package supports all device types including Apple and Android devices and can be used either in a browser (client-side) or Node.js environment (server-side).</p><p>Visit <a href="https://www.npmjs.com/package/@51degrees/ua-parser-js">↗ 51Degrees <u>UAParser</u></a> to get started.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<a href="https://opencollective.com/ua-parser-js">↗ Become a sponsor</a>
|
||||
</td>
|
||||
@@ -47,39 +45,47 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
|
||||
What's new & breaking, please read [CHANGELOG](changelog.md) before upgrading.
|
||||
|
||||
# Documentation
|
||||
### UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)])
|
||||
|
||||
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"]`.
|
||||
### `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):IData`
|
||||
|
||||
In browser environment you don't need to pass the user-agent string to the function, as it should automatically get the string from the `window.navigator.userAgent`. Whereas in nodejs environment, the user-agent string must be passed in order for the function to work (usually you can find the user-agent in: `request.headers["user-agent"]`).
|
||||
|
||||
## Constructor
|
||||
|
||||
#### * `new UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):UAParser`
|
||||
|
||||
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([user-agent:string][,extensions:object][,headers:object(since@2.0)])`
|
||||
|
||||
```js
|
||||
let parser = new UAParser("your user-agent here"); // 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" : {}
|
||||
} */
|
||||
/*
|
||||
{
|
||||
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([user-agent:string][,extensions:object][,headers:object(since@2.0)])`
|
||||
* returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
|
||||
#### * `UAParser([user-agent:string][,extensions:object][,headers:object(since@2.0)]):IData`
|
||||
|
||||
## Methods
|
||||
When you call `UAParser` without the `new` keyword, it will automatically call `getResult()` function and return the parsed results.
|
||||
|
||||
```sh
|
||||
returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
|
||||
```
|
||||
|
||||
## `UAParser`:
|
||||
|
||||
#### 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:
|
||||
* `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.
|
||||
@@ -88,17 +94,21 @@ The methods are self explanatory, here's a small overview on all the available m
|
||||
* `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.
|
||||
* `setUA(ua)` - set a custom user-agent to be parsed.
|
||||
|
||||
---
|
||||
|
||||
* `getResult()`
|
||||
* returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
|
||||
|
||||
* `getBrowser()`
|
||||
* returns `{ name: '', version: '' }`
|
||||
#### * `getResult():IData`
|
||||
|
||||
```sh
|
||||
returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
|
||||
```
|
||||
|
||||
#### * `getBrowser():IData`
|
||||
|
||||
```sh
|
||||
returns `{ name: '', version: '' }`
|
||||
|
||||
# Possible 'browser.name':
|
||||
2345Explorer, 360 Browser, Amaya, Android Browser, Arora, Avant, Avast, AVG,
|
||||
BIDUBrowser, Baidu, Basilisk, Blazer, Bolt, Brave, Bowser, Camino, Chimera,
|
||||
@@ -113,16 +123,18 @@ 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, [Mobile] Safari,
|
||||
Sailfish Browser, Samsung Browser, SeaMonkey, Silk, Skyfire, Sleipnir, Slim,
|
||||
SlimBrowser, Swiftfox, Tesla, TikTok, Tizen Browser, UCBrowser, UP.Browser, Viera,
|
||||
Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m, Whale Browser, ...
|
||||
SlimBrowser, Snapchat, Swiftfox, Tesla, TikTok, Tizen Browser, UCBrowser,
|
||||
UP.Browser, Viera, Vivaldi, Waterfox, WeChat, Weibo, Yandex, baidu, iCab, w3m,
|
||||
Whale Browser, ...
|
||||
|
||||
# 'browser.version' determined dynamically
|
||||
```
|
||||
|
||||
* `getDevice()`
|
||||
* returns `{ model: '', type: '', vendor: '' }`
|
||||
#### * `getDevice():IData`
|
||||
|
||||
```sh
|
||||
returns `{ model: '', type: '', vendor: '' }`
|
||||
|
||||
# Possible 'device.type':
|
||||
console, mobile, tablet, smarttv, wearable, embedded
|
||||
|
||||
@@ -135,18 +147,20 @@ console, mobile, tablet, smarttv, wearable, embedded
|
||||
|
||||
# Possible 'device.vendor':
|
||||
Acer, Alcatel, Amazon, Apple, Archos, ASUS, AT&T, BenQ, BlackBerry, Dell,
|
||||
Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Jolla, Kobo,
|
||||
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, ...
|
||||
Essential, Facebook, Fairphone, GeeksPhone, Google, HP, HTC, Huawei, Infinix, Jolla,
|
||||
Kobo, 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, Tecno, Tesla, Vivo, Vodafone, Xbox, Xiaomi,
|
||||
Zebra, ZTE, ...
|
||||
|
||||
# 'device.model' determined dynamically
|
||||
```
|
||||
|
||||
* `getEngine()`
|
||||
* returns `{ name: '', version: '' }`
|
||||
#### * `getEngine():IData`
|
||||
|
||||
```sh
|
||||
returns `{ name: '', version: '' }`
|
||||
|
||||
# Possible 'engine.name'
|
||||
Amaya, Blink, EdgeHTML, Flow, Gecko, Goanna, iCab, KHTML, LibWeb, Links, Lynx,
|
||||
NetFront, NetSurf, Presto, Tasman, Trident, w3m, WebKit
|
||||
@@ -154,10 +168,11 @@ NetFront, NetSurf, Presto, Tasman, Trident, w3m, WebKit
|
||||
# 'engine.version' determined dynamically
|
||||
```
|
||||
|
||||
* `getOS()`
|
||||
* returns `{ name: '', version: '' }`
|
||||
#### * `getOS():IData`
|
||||
|
||||
```sh
|
||||
returns `{ name: '', version: '' }`
|
||||
|
||||
# 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,
|
||||
@@ -172,22 +187,42 @@ Zenwalk, ...
|
||||
# 'os.version' determined dynamically
|
||||
```
|
||||
|
||||
* `getCPU()`
|
||||
* returns `{ architecture: '' }`
|
||||
#### * `getCPU():IData`
|
||||
|
||||
```sh
|
||||
returns `{ architecture: '' }`
|
||||
|
||||
# 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
|
||||
#### * `getUA():string`
|
||||
|
||||
* `setUA(uastring)`
|
||||
* set UA string to be parsed
|
||||
* returns current instance
|
||||
```sh
|
||||
returns user-agent string of current instance
|
||||
```
|
||||
|
||||
#### * `is():boolean` utility `since@2.0`
|
||||
#### * `setUA(ua:string):UAParser`
|
||||
|
||||
```sh
|
||||
set user-agent string to be parsed
|
||||
returns current instance
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `IData`: `since@2.0`
|
||||
|
||||
#### Methods table
|
||||
The methods are self explanatory, here's a small overview on all the available methods:
|
||||
* `is(value)` - returns `true` if the passed value matches a value of current object, `false` otherwise
|
||||
* `toString()` - returns the full-name values of current object as a string
|
||||
* `withClientHints()` - returns an object with re-updated data from client hints
|
||||
* `withFeatureCheck()` - returns an object with re-updated data from feature detection
|
||||
|
||||
---
|
||||
|
||||
#### * `is(value:string):boolean`
|
||||
|
||||
```js
|
||||
// Is just a shorthand comparison to check whether the value of specified item equals one of its properties (in a case-insensitive way)
|
||||
@@ -246,7 +281,7 @@ let engine = uap.getEngine();
|
||||
engine.is("Blink"); // true
|
||||
```
|
||||
|
||||
#### * `toString():string` utility `since@2.0`
|
||||
#### * `toString():string`
|
||||
|
||||
```js
|
||||
// Retrieve full-name values as a string
|
||||
@@ -287,7 +322,7 @@ engine.version; // "28.0.1500.95"
|
||||
engine.toString(); // "Blink 28.0.1500.95"
|
||||
```
|
||||
|
||||
#### * `withClientHints():Promise<object>|Thenable<object>|object` `since@2.0`
|
||||
#### * `withClientHints():Promise<IData>|Thenable<IData>|IData`
|
||||
|
||||
Recently, Chrome limits the information exposed through user-agent and introduces a new experimental set of data called "client-hints". In browser-environment, obtaining the client-hints data via JavaScript must be done in an asynchronous way. In `UAParser` you can chain the result object from `get*` method with `withClientHints()` to also read the client-hints data from the browser and return the updated data as a `Promise`.
|
||||
|
||||
@@ -346,6 +381,21 @@ new UAParser(request.headers)
|
||||
});
|
||||
```
|
||||
|
||||
#### * `withFeatureCheck():IData`
|
||||
|
||||
This method allows us to examine other features beyond `navigator.userAgent` to further improve detection of the following:
|
||||
- browser : Brave (check for `navigator.isBrave`)
|
||||
- device : iPad (check for `navigator.standalone` & `navigator.maxTouchPoints`)
|
||||
|
||||
```js
|
||||
// suppose this code runs on iPad
|
||||
const withoutFeatureCheck = UAParser();
|
||||
const withFeatureCheck = UAParser().withFeatureCheck();
|
||||
|
||||
console.log(withoutFeatureCheck.device); // { vendor : "Apple", model : "Macintosh", type : undefined }
|
||||
console.log(withFeatureCheck.device); // { vendor : "Apple", model : "iPad", type : "tablet" }
|
||||
```
|
||||
|
||||
## Extending Regex
|
||||
|
||||
If you want to detect something that's not currently provided by UAParser.js (eg: `bots`, specific apps, etc), you can pass a list of regexes to extend internal UAParser.js regexes with your own.
|
||||
|
||||
13
script/build-dist.sh
Executable file
13
script/build-dist.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/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"
|
||||
|
||||
# minified
|
||||
echo "Generate ${MIN_PATH}"
|
||||
uglifyjs $SRC_PATH -o $MIN_PATH --comments "/^ UA/"
|
||||
|
||||
# packed
|
||||
echo "Generate ${PACK_PATH}"
|
||||
uglifyjs $SRC_PATH -o $PACK_PATH --comments "/^ UA/" --compress --mangle
|
||||
57
script/build-module.js
Executable file
57
script/build-module.js
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env node
|
||||
/* jshint esversion: 6 */
|
||||
const fs = require('fs');
|
||||
const PATH = {
|
||||
main : {
|
||||
src : 'src/main/ua-parser.js',
|
||||
dest : 'src/main/ua-parser.mjs',
|
||||
title : ''
|
||||
},
|
||||
enums : {
|
||||
src : 'src/enums/ua-parser-enums.js',
|
||||
dest :'src/enums/ua-parser-enums.mjs',
|
||||
title : 'enums'
|
||||
},
|
||||
extensions : {
|
||||
src : 'src/extensions/ua-parser-extensions.js',
|
||||
dest : 'src/extensions/ua-parser-extensions.mjs',
|
||||
title : 'extensions'
|
||||
},
|
||||
helpers : {
|
||||
src : 'src/helpers/ua-parser-helpers.js',
|
||||
dest : 'src/helpers/ua-parser-helpers.mjs',
|
||||
title : 'helpers'
|
||||
}
|
||||
};
|
||||
const generateMJS = (module, replacers) => {
|
||||
const { src, dest, title } = PATH[module];
|
||||
let text = fs.readFileSync(src, 'utf-8');
|
||||
replacers.forEach(replacer => {
|
||||
text = text.replace(replacer[0], replacer[1]);
|
||||
});
|
||||
|
||||
console.log(`Generate ${dest}`);
|
||||
|
||||
fs.writeFileSync(dest,
|
||||
`// Generated ESM version of UAParser.js ${title}
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /${src}
|
||||
|
||||
${text}`, 'utf-8');
|
||||
};
|
||||
|
||||
// ua-parser.mjs
|
||||
generateMJS('main', [
|
||||
[/\(func[\s\S]+strict\';/ig, ''],
|
||||
[/esversion\: 3/ig, 'esversion: 6'],
|
||||
[/\/[\/\s]+export[\s\S]+/ig,'export {UAParser};']
|
||||
]);
|
||||
|
||||
// ua-parser-enum.mjs
|
||||
generateMJS('enums', [[/module\.exports =/ig, 'export']]);
|
||||
|
||||
// ua-parser-extension.mjs
|
||||
generateMJS('extensions', [[/module\.exports =/ig, 'export']]);
|
||||
|
||||
// ua-parser-helpers.mjs
|
||||
generateMJS('helpers', [[/module\.exports =/ig, 'export']]);
|
||||
26
script/test-all.sh
Executable file
26
script/test-all.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo '
|
||||
- run build
|
||||
'
|
||||
npm run build || exit 1
|
||||
|
||||
echo '
|
||||
- lint js code
|
||||
'
|
||||
npm run test:jshint || 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
|
||||
@@ -2,4 +2,6 @@
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Please report security issues to `f@faisalman.com`
|
||||
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.
|
||||
@@ -1,10 +1,12 @@
|
||||
///////////////////////////////////////////////
|
||||
/* Enums for UAParser.js v2.0.0-alpha.2
|
||||
/* Enums for UAParser.js v2.0.0-alpha.3
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT License */
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
const BrowserName = Object.freeze({
|
||||
CHROME : 'Chrome',
|
||||
EDGE : 'Edge',
|
||||
@@ -98,4 +100,4 @@ module.exports = {
|
||||
DeviceVendor,
|
||||
EngineName,
|
||||
OSName
|
||||
}
|
||||
};
|
||||
@@ -1,14 +1,16 @@
|
||||
// Generated ESM version of UAParser.js enums
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /src/enum/ua-parser-enum.js
|
||||
// Source: /src/enums/ua-parser-enums.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Enums for UAParser.js v2.0.0-alpha.2
|
||||
/* Enums for UAParser.js v2.0.0-alpha.3
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT License */
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
const BrowserName = Object.freeze({
|
||||
CHROME : 'Chrome',
|
||||
EDGE : 'Edge',
|
||||
@@ -102,4 +104,4 @@ export {
|
||||
DeviceVendor,
|
||||
EngineName,
|
||||
OSName
|
||||
}
|
||||
};
|
||||
@@ -1,120 +0,0 @@
|
||||
///////////////////////////////////////////////
|
||||
/* Extensions for UAParser.js v2.0.0-alpha.2
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT License */
|
||||
//////////////////////////////////////////////
|
||||
|
||||
const MODEL = 'model';
|
||||
const NAME = 'name';
|
||||
const TYPE = 'type';
|
||||
const VENDOR = 'vendor';
|
||||
const VERSION = 'version';
|
||||
const MOBILE = 'mobile';
|
||||
const TABLET = 'tablet';
|
||||
|
||||
const Bots = Object.freeze({
|
||||
browser : [
|
||||
// Googlebot / BingBot / MSNBot / FacebookBot
|
||||
[/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
|
||||
]
|
||||
});
|
||||
|
||||
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]], [
|
||||
|
||||
/\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
|
||||
], [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]
|
||||
]
|
||||
]
|
||||
});
|
||||
|
||||
const Emails = Object.freeze({
|
||||
browser : [
|
||||
// Microsoft Outlook / Thunderbird
|
||||
[/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']]
|
||||
]
|
||||
});
|
||||
|
||||
const Tools = Object.freeze({
|
||||
browser : [
|
||||
// wget / curl / lynx
|
||||
[/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'tool']]
|
||||
]
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
Bots,
|
||||
ExtraDevices,
|
||||
Emails,
|
||||
Tools
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// Generated ESM version of UAParser.js extensions
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /src/extension/ua-parser-extension.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Extensions for UAParser.js v2.0.0-alpha.2
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT License */
|
||||
//////////////////////////////////////////////
|
||||
|
||||
const MODEL = 'model';
|
||||
const NAME = 'name';
|
||||
const TYPE = 'type';
|
||||
const VENDOR = 'vendor';
|
||||
const VERSION = 'version';
|
||||
const MOBILE = 'mobile';
|
||||
const TABLET = 'tablet';
|
||||
|
||||
const Bots = Object.freeze({
|
||||
browser : [
|
||||
// Googlebot / BingBot / MSNBot / FacebookBot
|
||||
[/((?:google|bing|msn|facebook)bot(?:\-[imagevdo]{5})?|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
|
||||
]
|
||||
});
|
||||
|
||||
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]], [
|
||||
|
||||
/\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
|
||||
], [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]
|
||||
]
|
||||
]
|
||||
});
|
||||
|
||||
const Emails = Object.freeze({
|
||||
browser : [
|
||||
// Microsoft Outlook / Thunderbird
|
||||
[/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']]
|
||||
]
|
||||
});
|
||||
|
||||
const Tools = Object.freeze({
|
||||
browser : [
|
||||
// wget / curl / lynx
|
||||
[/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'tool']]
|
||||
]
|
||||
});
|
||||
|
||||
export {
|
||||
Bots,
|
||||
ExtraDevices,
|
||||
Emails,
|
||||
Tools
|
||||
}
|
||||
248
src/extensions/ua-parser-extensions.js
Normal file
248
src/extensions/ua-parser-extensions.js
Normal file
@@ -0,0 +1,248 @@
|
||||
///////////////////////////////////////////////
|
||||
/* Extensions for UAParser.js v2.0.0-alpha.3
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT 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 Apps = Object.freeze({
|
||||
browser : [
|
||||
[/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']]
|
||||
]
|
||||
});
|
||||
|
||||
const Bots = Object.freeze({
|
||||
browser : [
|
||||
// Googlebot / BingBot / MSNBot / FacebookBot
|
||||
[/((?:google|bing|msn|facebook)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
|
||||
|
||||
// GPTBot - https://platform.openai.com/docs/gptbot
|
||||
[/(gptbot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
|
||||
|
||||
// Slackbot - https://api.slack.com/robots
|
||||
[/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
|
||||
]
|
||||
});
|
||||
|
||||
const CLIs = Object.freeze({
|
||||
browser : [
|
||||
// wget / curl / lynx
|
||||
[/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']]
|
||||
]
|
||||
});
|
||||
|
||||
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]], [
|
||||
|
||||
/\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
|
||||
], [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]]
|
||||
]
|
||||
});
|
||||
|
||||
const Emails = Object.freeze({
|
||||
browser : [
|
||||
// Microsoft Outlook / Thunderbird
|
||||
[/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']]
|
||||
]
|
||||
});
|
||||
|
||||
const MediaPlayers = Object.freeze({
|
||||
browser : [[
|
||||
|
||||
/(apple(?:coremedia|))\/([\w\._]+)/i, // Generic Apple CoreMedia
|
||||
/(coremedia) v([\w\._]+)/i
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(aqualung|lyssna|bsplayer)\/([\w\.-]+)/i // Aqualung/Lyssna/BSPlayer
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(ares|ossproxy)\s([\w\.-]+)/i // Ares/OSSProxy
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\/([\w\.-]+)/i,
|
||||
// Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC
|
||||
// NSPlayer/PSP-InternetRadioPlayer/Videos
|
||||
/(clementine|music player daemon)\s([\w\.-]+)/i, // Clementine/MPD
|
||||
/(lg player|nexplayer)\s([\d\.]+)/i,
|
||||
/player\/(nexplayer|lg player)\s([\w\.-]+)/i // NexPlayer/LG Player
|
||||
], [NAME, VERSION], [
|
||||
/(nexplayer)\s([\w\.-]+)/i // Nexplayer
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(flrp)\/([\w\.-]+)/i // Flip Player
|
||||
], [[NAME, 'Flip Player'], VERSION], [
|
||||
|
||||
/(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i
|
||||
// FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit
|
||||
], [NAME], [
|
||||
|
||||
/(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i
|
||||
// Gstreamer
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player
|
||||
/(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i,
|
||||
// Java/urllib/requests/wget/cURL
|
||||
/(lavf)([\d\.]+)/i // Lavf (FFMPEG)
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(htc_one_s)\/([\d\.]+)/i, // HTC One S
|
||||
], [[NAME, /_/g, ' '], VERSION], [
|
||||
|
||||
/(mplayer)(?:\s|\/)(?:(?:sherpya-){0,1}svn)(?:-|\s)(r\d+(?:-\d+[\w\.-]+))/i,
|
||||
// MPlayer SVN
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(mplayer)(?:\s|\/)([\w\.-]+)/i, // MPlayer
|
||||
/(mplayer) unknown-([\w\.\-]+)/i // MPlayer UNKNOWN
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(mplayer)/i, // MPlayer (no other info)
|
||||
/(yourmuze)/i, // YourMuze
|
||||
/(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime
|
||||
], [NAME], [
|
||||
|
||||
/(nero (?:home|scout))\/([\w\.-]+)/i // Nero Home/Nero Scout
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(nokia\d+)\/([\w\.-]+)/i // Nokia
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/\s(songbird)\/([\w\.-]+)/i // Songbird/Philips-Songbird
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(winamp)3 version ([\w\.-]+)/i, // Winamp
|
||||
/(winamp)\s([\w\.-]+)/i,
|
||||
/(winamp)mpeg\/([\w\.-]+)/i
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info)
|
||||
// inlight radio
|
||||
], [NAME], [
|
||||
|
||||
/(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\/([\w\.-]+)/i
|
||||
// QuickTime/RealMedia/RadioApp/RadioClientApplication/
|
||||
// SoundTap/Totem/Stagefright/Streamium
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(smp)([\d\.]+)/i // SMP
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(vlc) media player - version ([\w\.]+)/i, // VLC Videolan
|
||||
/(vlc)\/([\w\.-]+)/i,
|
||||
/(xbmc|gvfs|xine|xmms|irapp)\/([\w\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp
|
||||
/(foobar2000)\/([\d\.]+)/i, // Foobar2000
|
||||
/(itunes)\/([\d\.]+)/i // iTunes
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(wmplayer)\/([\w\.-]+)/i, // Windows Media Player
|
||||
/(windows-media-player)\/([\w\.-]+)/i
|
||||
], [[NAME, /-/g, ' '], VERSION], [
|
||||
|
||||
/windows\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ (home media server)/i,
|
||||
// Windows Media Server
|
||||
], [VERSION, [NAME, 'Windows']], [
|
||||
|
||||
/(com\.riseupradioalarm)\/([\d\.]*)/i // RiseUP Radio Alarm
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(rad.io)\s([\d\.]+)/i, // Rad.io
|
||||
/(radio.(?:de|at|fr))\s([\d\.]+)/i
|
||||
], [[NAME, 'rad.io'], VERSION]
|
||||
]
|
||||
});
|
||||
|
||||
const Modules = Object.freeze({
|
||||
browser : [
|
||||
// Axios/jsdom/Scrapy
|
||||
[/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']]
|
||||
]
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
Apps,
|
||||
Bots,
|
||||
CLIs,
|
||||
ExtraDevices,
|
||||
Emails,
|
||||
MediaPlayers,
|
||||
Modules
|
||||
};
|
||||
252
src/extensions/ua-parser-extensions.mjs
Normal file
252
src/extensions/ua-parser-extensions.mjs
Normal file
@@ -0,0 +1,252 @@
|
||||
// Generated ESM version of UAParser.js extensions
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /src/extensions/ua-parser-extensions.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Extensions for UAParser.js v2.0.0-alpha.3
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT 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 Apps = Object.freeze({
|
||||
browser : [
|
||||
[/chatlyio\/([\d\.]+)/i], [VERSION, 'Slack', [TYPE, 'app']]
|
||||
]
|
||||
});
|
||||
|
||||
const Bots = Object.freeze({
|
||||
browser : [
|
||||
// Googlebot / BingBot / MSNBot / FacebookBot
|
||||
[/((?:google|bing|msn|facebook)bot(?:[\-imagevdo]{0,6})|bingpreview)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
|
||||
|
||||
// GPTBot - https://platform.openai.com/docs/gptbot
|
||||
[/(gptbot)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']],
|
||||
|
||||
// Slackbot - https://api.slack.com/robots
|
||||
[/(slack(?:bot)?(?:-imgproxy|-linkexpanding)?) ([\w\.]+)/i], [NAME, VERSION, [TYPE, 'bot']]
|
||||
]
|
||||
});
|
||||
|
||||
const CLIs = Object.freeze({
|
||||
browser : [
|
||||
// wget / curl / lynx
|
||||
[/(wget|curl|lynx)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'cli']]
|
||||
]
|
||||
});
|
||||
|
||||
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]], [
|
||||
|
||||
/\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
|
||||
], [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]]
|
||||
]
|
||||
});
|
||||
|
||||
const Emails = Object.freeze({
|
||||
browser : [
|
||||
// Microsoft Outlook / Thunderbird
|
||||
[/(microsoft outlook|thunderbird)[\s\/]([\w\.]+)/i], [NAME, VERSION, [TYPE, 'email']]
|
||||
]
|
||||
});
|
||||
|
||||
const MediaPlayers = Object.freeze({
|
||||
browser : [[
|
||||
|
||||
/(apple(?:coremedia|))\/([\w\._]+)/i, // Generic Apple CoreMedia
|
||||
/(coremedia) v([\w\._]+)/i
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(aqualung|lyssna|bsplayer)\/([\w\.-]+)/i // Aqualung/Lyssna/BSPlayer
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(ares|ossproxy)\s([\w\.-]+)/i // Ares/OSSProxy
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(audacious|audimusicstream|amarok|bass|core|dalvik|gnomemplayer|music on console|nsplayer|psp-internetradioplayer|videos)\/([\w\.-]+)/i,
|
||||
// Audacious/AudiMusicStream/Amarok/BASS/OpenCORE/Dalvik/GnomeMplayer/MoC
|
||||
// NSPlayer/PSP-InternetRadioPlayer/Videos
|
||||
/(clementine|music player daemon)\s([\w\.-]+)/i, // Clementine/MPD
|
||||
/(lg player|nexplayer)\s([\d\.]+)/i,
|
||||
/player\/(nexplayer|lg player)\s([\w\.-]+)/i // NexPlayer/LG Player
|
||||
], [NAME, VERSION], [
|
||||
/(nexplayer)\s([\w\.-]+)/i // Nexplayer
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(flrp)\/([\w\.-]+)/i // Flip Player
|
||||
], [[NAME, 'Flip Player'], VERSION], [
|
||||
|
||||
/(fstream|nativehost|queryseekspider|ia-archiver|facebookexternalhit)/i
|
||||
// FStream/NativeHost/QuerySeekSpider/IA Archiver/facebookexternalhit
|
||||
], [NAME], [
|
||||
|
||||
/(gstreamer) souphttpsrc.+libsoup\/([\w\.-]+)/i
|
||||
// Gstreamer
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(htc streaming player)\s[\w_]+\s\/\s([\d\.]+)/i, // HTC Streaming Player
|
||||
/(java|python-urllib|python-requests|wget|libcurl)\/([\w\.-_]+)/i,
|
||||
// Java/urllib/requests/wget/cURL
|
||||
/(lavf)([\d\.]+)/i // Lavf (FFMPEG)
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(htc_one_s)\/([\d\.]+)/i, // HTC One S
|
||||
], [[NAME, /_/g, ' '], VERSION], [
|
||||
|
||||
/(mplayer)(?:\s|\/)(?:(?:sherpya-){0,1}svn)(?:-|\s)(r\d+(?:-\d+[\w\.-]+))/i,
|
||||
// MPlayer SVN
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(mplayer)(?:\s|\/)([\w\.-]+)/i, // MPlayer
|
||||
/(mplayer) unknown-([\w\.\-]+)/i // MPlayer UNKNOWN
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(mplayer)/i, // MPlayer (no other info)
|
||||
/(yourmuze)/i, // YourMuze
|
||||
/(media player classic|nero showtime)/i // Media Player Classic/Nero ShowTime
|
||||
], [NAME], [
|
||||
|
||||
/(nero (?:home|scout))\/([\w\.-]+)/i // Nero Home/Nero Scout
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(nokia\d+)\/([\w\.-]+)/i // Nokia
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/\s(songbird)\/([\w\.-]+)/i // Songbird/Philips-Songbird
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(winamp)3 version ([\w\.-]+)/i, // Winamp
|
||||
/(winamp)\s([\w\.-]+)/i,
|
||||
/(winamp)mpeg\/([\w\.-]+)/i
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(ocms-bot|tapinradio|tunein radio|unknown|winamp|inlight radio)/i // OCMS-bot/tap in radio/tunein/unknown/winamp (no other info)
|
||||
// inlight radio
|
||||
], [NAME], [
|
||||
|
||||
/(quicktime|rma|radioapp|radioclientapplication|soundtap|totem|stagefright|streamium)\/([\w\.-]+)/i
|
||||
// QuickTime/RealMedia/RadioApp/RadioClientApplication/
|
||||
// SoundTap/Totem/Stagefright/Streamium
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(smp)([\d\.]+)/i // SMP
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(vlc) media player - version ([\w\.]+)/i, // VLC Videolan
|
||||
/(vlc)\/([\w\.-]+)/i,
|
||||
/(xbmc|gvfs|xine|xmms|irapp)\/([\w\.-]+)/i, // XBMC/gvfs/Xine/XMMS/irapp
|
||||
/(foobar2000)\/([\d\.]+)/i, // Foobar2000
|
||||
/(itunes)\/([\d\.]+)/i // iTunes
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(wmplayer)\/([\w\.-]+)/i, // Windows Media Player
|
||||
/(windows-media-player)\/([\w\.-]+)/i
|
||||
], [[NAME, /-/g, ' '], VERSION], [
|
||||
|
||||
/windows\/([\w\.-]+) upnp\/[\d\.]+ dlnadoc\/[\d\.]+ (home media server)/i,
|
||||
// Windows Media Server
|
||||
], [VERSION, [NAME, 'Windows']], [
|
||||
|
||||
/(com\.riseupradioalarm)\/([\d\.]*)/i // RiseUP Radio Alarm
|
||||
], [NAME, VERSION], [
|
||||
|
||||
/(rad.io)\s([\d\.]+)/i, // Rad.io
|
||||
/(radio.(?:de|at|fr))\s([\d\.]+)/i
|
||||
], [[NAME, 'rad.io'], VERSION]
|
||||
]
|
||||
});
|
||||
|
||||
const Modules = Object.freeze({
|
||||
browser : [
|
||||
// Axios/jsdom/Scrapy
|
||||
[/\b(axios|jsdom|scrapy)\/([\w\.]+)/i], [NAME, VERSION, [TYPE, 'module']]
|
||||
]
|
||||
});
|
||||
|
||||
export {
|
||||
Apps,
|
||||
Bots,
|
||||
CLIs,
|
||||
ExtraDevices,
|
||||
Emails,
|
||||
MediaPlayers,
|
||||
Modules
|
||||
};
|
||||
42
src/helpers/ua-parser-helpers.js
Normal file
42
src/helpers/ua-parser-helpers.js
Normal file
@@ -0,0 +1,42 @@
|
||||
///////////////////////////////////////////////
|
||||
/* Helpers for UAParser.js v2.0.0-alpha.3
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT License */
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
/*
|
||||
# Reference:
|
||||
https://www.chromium.org/updates/ua-reduction/
|
||||
|
||||
# Desktop
|
||||
---
|
||||
Format:
|
||||
Mozilla/5.0 (<unifiedPlatform>) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/<majorVersion>.0.0.0 Safari/537.36
|
||||
|
||||
Possible <unifiedPlatform> values:
|
||||
- Windows NT 10.0; Win64; x64
|
||||
- Macintosh; Intel Mac OS X 10_15_7
|
||||
- X11; Linux x86_64
|
||||
- X11; CrOS x86_64 14541.0.0
|
||||
- Fuchsia
|
||||
|
||||
# Mobile & Tablet: (except iOS/Android WebView)
|
||||
---
|
||||
Format:
|
||||
Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/<majorVersion>.0.0.0 <deviceCompat> Safari/537.36
|
||||
|
||||
Possible <deviceCompat> values:
|
||||
- "Mobile"
|
||||
- "" (empty string for Tablets & Desktop)
|
||||
*/
|
||||
|
||||
const frozenUA = /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/;
|
||||
|
||||
const isFrozenUA = str => frozenUA.test(str);
|
||||
|
||||
module.exports = {
|
||||
isFrozenUA
|
||||
};
|
||||
46
src/helpers/ua-parser-helpers.mjs
Normal file
46
src/helpers/ua-parser-helpers.mjs
Normal file
@@ -0,0 +1,46 @@
|
||||
// Generated ESM version of UAParser.js helpers
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /src/helpers/ua-parser-helpers.js
|
||||
|
||||
///////////////////////////////////////////////
|
||||
/* Helpers for UAParser.js v2.0.0-alpha.3
|
||||
https://github.com/faisalman/ua-parser-js
|
||||
Author: Faisal Salman <f@faisalman.com>
|
||||
MIT License */
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/*jshint esversion: 6 */
|
||||
|
||||
/*
|
||||
# Reference:
|
||||
https://www.chromium.org/updates/ua-reduction/
|
||||
|
||||
# Desktop
|
||||
---
|
||||
Format:
|
||||
Mozilla/5.0 (<unifiedPlatform>) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/<majorVersion>.0.0.0 Safari/537.36
|
||||
|
||||
Possible <unifiedPlatform> values:
|
||||
- Windows NT 10.0; Win64; x64
|
||||
- Macintosh; Intel Mac OS X 10_15_7
|
||||
- X11; Linux x86_64
|
||||
- X11; CrOS x86_64 14541.0.0
|
||||
- Fuchsia
|
||||
|
||||
# Mobile & Tablet: (except iOS/Android WebView)
|
||||
---
|
||||
Format:
|
||||
Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/<majorVersion>.0.0.0 <deviceCompat> Safari/537.36
|
||||
|
||||
Possible <deviceCompat> values:
|
||||
- "Mobile"
|
||||
- "" (empty string for Tablets & Desktop)
|
||||
*/
|
||||
|
||||
const frozenUA = /Mozilla\/5\.0 \((Windows NT 10\.0; Win64; x64|Macintosh; Intel Mac OS X 10_15_7|X11; Linux x86_64|X11; CrOS x86_64 14541\.0\.0|Fuchsia|Linux; Android 10; K)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/\d+\.0\.0\.0 (Mobile )?Safari\/537\.36/;
|
||||
|
||||
const isFrozenUA = str => frozenUA.test(str);
|
||||
|
||||
export {
|
||||
isFrozenUA
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/* UAParser.js v2.0.0-alpha.2
|
||||
/* UAParser.js v2.0.0-alpha.3
|
||||
Copyright © 2012-2023 Faisal Salman <f@faisalman.com>
|
||||
MIT License *//*
|
||||
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
|
||||
@@ -8,16 +8,19 @@
|
||||
Source : https://github.com/faisalman/ua-parser-js */
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* jshint esversion: 3 */
|
||||
/* globals window */
|
||||
|
||||
(function (window, undefined) {
|
||||
|
||||
'use strict';
|
||||
|
||||
|
||||
//////////////
|
||||
// Constants
|
||||
/////////////
|
||||
|
||||
|
||||
var LIBVERSION = '2.0.0-alpha.2',
|
||||
var LIBVERSION = '2.0.0-alpha.3',
|
||||
EMPTY = '',
|
||||
UNKNOWN = '?',
|
||||
FUNC_TYPE = 'function',
|
||||
@@ -47,12 +50,12 @@
|
||||
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_MOBILE = CH_HEADER + '-mobile',
|
||||
CH_HEADER_MODEL = CH_HEADER + '-model',
|
||||
CH_HEADER_PLATFORM = CH_HEADER + '-platform',
|
||||
CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS,
|
||||
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', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'],
|
||||
CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, BITNESS],
|
||||
UA_BROWSER = 'browser',
|
||||
UA_CPU = 'cpu',
|
||||
UA_DEVICE = 'device',
|
||||
@@ -93,18 +96,7 @@
|
||||
// Helper
|
||||
//////////
|
||||
|
||||
var assignFromEntries = function (arr) {
|
||||
for (var i in arr) {
|
||||
var propName = arr[i];
|
||||
if (typeof propName == OBJ_TYPE && propName.length == 2) {
|
||||
this[propName[0]] = propName[1];
|
||||
} else {
|
||||
this[propName] = undefined;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
extend = function (regexes, extensions) {
|
||||
var extend = function (regexes, extensions) {
|
||||
var mergedRegexes = {};
|
||||
for (var i in regexes) {
|
||||
mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i];
|
||||
@@ -148,6 +140,17 @@
|
||||
majorize = function (version) {
|
||||
return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined;
|
||||
},
|
||||
setProps = function (arr) {
|
||||
for (var i in arr) {
|
||||
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 str.replace(pattern, EMPTY);
|
||||
},
|
||||
@@ -241,18 +244,7 @@
|
||||
// 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 = {
|
||||
var windowsVersionMap = {
|
||||
'ME' : '4.90',
|
||||
'NT 3.11' : 'NT3.51',
|
||||
'NT 4.0' : 'NT4.0',
|
||||
@@ -355,7 +347,7 @@
|
||||
/(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp
|
||||
/safari (line)\/([\w\.]+)/i, // Line App for iOS
|
||||
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
|
||||
/(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram
|
||||
/(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat
|
||||
], [NAME, VERSION], [
|
||||
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
|
||||
], [VERSION, [NAME, 'GSA']], [
|
||||
@@ -384,7 +376,7 @@
|
||||
/version\/([\w\.\,]+) .*(safari)/i // Safari
|
||||
], [VERSION, NAME], [
|
||||
/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
|
||||
], [NAME, [VERSION, strMapper, oldSafariMap]], [
|
||||
], [NAME, [VERSION, '1']], [
|
||||
|
||||
/(webkit|khtml)\/([\w\.]+)/i
|
||||
], [NAME, VERSION], [
|
||||
@@ -425,7 +417,7 @@
|
||||
/((?:i[346]|x)86)[;\)]/i // IA32 (x86)
|
||||
], [[ARCHITECTURE, 'ia32']], [
|
||||
|
||||
/\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
|
||||
/\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
|
||||
], [[ARCHITECTURE, 'arm64']], [
|
||||
|
||||
/\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF
|
||||
@@ -482,7 +474,7 @@
|
||||
], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
|
||||
|
||||
// Xiaomi
|
||||
/\b(poco[\w ]+)(?: bui|\))/i, // Xiaomi POCO
|
||||
/\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
|
||||
@@ -590,7 +582,7 @@
|
||||
], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
|
||||
|
||||
// MIXED
|
||||
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i,
|
||||
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i,
|
||||
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
|
||||
/(hp) ([\w ]+\w)/i, // HP iPAQ
|
||||
/(asus)-?(\w+)/i, // Asus
|
||||
@@ -635,7 +627,7 @@
|
||||
], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
|
||||
/crkey/i // Google Chromecast
|
||||
], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
|
||||
/droid.+aft(\w)( bui|\))/i // Fire TV
|
||||
/droid.+aft(\w+)( bui|\))/i // Fire TV
|
||||
], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
|
||||
/\(dtv[\);].+(aquos)/i,
|
||||
/(aquos-tv[\w ]+)\)/i // Sharp
|
||||
@@ -740,7 +732,7 @@
|
||||
|
||||
// iOS/macOS
|
||||
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
|
||||
/ios;fbsv\/([\d\.]+)/i,
|
||||
/(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
|
||||
/cfnetwork\/.+darwin/i
|
||||
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
|
||||
/(mac os x) ?([\w\. ]*)/i,
|
||||
@@ -809,23 +801,23 @@
|
||||
|
||||
var defaultProps = (function () {
|
||||
var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
|
||||
assignFromEntries.call(props.init, [
|
||||
setProps.call(props.init, [
|
||||
[UA_BROWSER, [NAME, VERSION, MAJOR]],
|
||||
[UA_CPU, [ARCHITECTURE]],
|
||||
[UA_DEVICE, [TYPE, MODEL, VENDOR]],
|
||||
[UA_ENGINE, [NAME, VERSION]],
|
||||
[UA_OS, [NAME, VERSION]]
|
||||
]);
|
||||
assignFromEntries.call(props.isIgnore, [
|
||||
setProps.call(props.isIgnore, [
|
||||
[UA_BROWSER, [VERSION, MAJOR]],
|
||||
[UA_ENGINE, [VERSION]],
|
||||
[UA_OS, [VERSION]]
|
||||
]);
|
||||
assignFromEntries.call(props.isIgnoreRgx, [
|
||||
setProps.call(props.isIgnoreRgx, [
|
||||
[UA_BROWSER, / ?browser$/i],
|
||||
[UA_OS, / ?os$/i]
|
||||
]);
|
||||
assignFromEntries.call(props.toString, [
|
||||
setProps.call(props.toString, [
|
||||
[UA_BROWSER, [NAME, VERSION]],
|
||||
[UA_CPU, [ARCHITECTURE]],
|
||||
[UA_DEVICE, [VENDOR, MODEL]],
|
||||
@@ -835,34 +827,47 @@
|
||||
return props;
|
||||
})();
|
||||
|
||||
var createUAParserData = function (itemType, ua, rgxMap, uaCH) {
|
||||
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 UAParserData () {
|
||||
assignFromEntries.call(this, init_props);
|
||||
function IData () {
|
||||
setProps.call(this, init_props);
|
||||
}
|
||||
UAParserData.prototype.withClientHints = function () {
|
||||
|
||||
|
||||
IData.prototype.getItem = function () {
|
||||
return item;
|
||||
};
|
||||
|
||||
IData.prototype.withClientHints = function () {
|
||||
|
||||
// nodejs / non-client-hints browsers
|
||||
if (!NAVIGATOR_UADATA) {
|
||||
return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get();
|
||||
return item
|
||||
.parseCH()
|
||||
.get();
|
||||
}
|
||||
|
||||
// browsers based on chromium 85+
|
||||
return NAVIGATOR_UADATA
|
||||
.getHighEntropyValues(CH_ALL_VALUES)
|
||||
.then(function (res) {
|
||||
var JS_UACH = new UAParserDataCH(res, false);
|
||||
return new UAParserItem(itemType, ua, rgxMap, JS_UACH).parseCH().get();
|
||||
return item
|
||||
.setCH(new UACHData(res, false))
|
||||
.parseCH()
|
||||
.get();
|
||||
});
|
||||
};
|
||||
|
||||
IData.prototype.withFeatureCheck = function () {
|
||||
return item.detectFeature().get();
|
||||
};
|
||||
|
||||
if (itemType != UA_RESULT) {
|
||||
UAParserData.prototype.is = function (strToCheck) {
|
||||
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)) {
|
||||
@@ -875,7 +880,7 @@
|
||||
}
|
||||
return is;
|
||||
};
|
||||
UAParserData.prototype.toString = function () {
|
||||
IData.prototype.toString = function () {
|
||||
var str = EMPTY;
|
||||
for (var i in toString_props) {
|
||||
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
|
||||
@@ -887,40 +892,39 @@
|
||||
}
|
||||
|
||||
if (!NAVIGATOR_UADATA) {
|
||||
UAParserData.prototype.then = function (cb) {
|
||||
IData.prototype.then = function (cb) {
|
||||
var that = this;
|
||||
var UAParserDataResolve = function () {
|
||||
var IDataResolve = function () {
|
||||
for (var prop in that) {
|
||||
if (that.hasOwnProperty(prop)) {
|
||||
this[prop] = that[prop];
|
||||
}
|
||||
}
|
||||
};
|
||||
UAParserDataResolve.prototype = {
|
||||
is : UAParserData.prototype.is,
|
||||
toString : UAParserData.prototype.toString
|
||||
IDataResolve.prototype = {
|
||||
is : IData.prototype.is,
|
||||
toString : IData.prototype.toString
|
||||
};
|
||||
var resolveData = new UAParserDataResolve();
|
||||
var resolveData = new IDataResolve();
|
||||
cb(resolveData);
|
||||
return resolveData;
|
||||
};
|
||||
}
|
||||
|
||||
return new UAParserData();
|
||||
return new IData();
|
||||
};
|
||||
|
||||
/////////////////
|
||||
// Constructor
|
||||
////////////////
|
||||
|
||||
function UAParserDataCH (uach, isHTTP_UACH) {
|
||||
function UACHData (uach, isHttpUACH) {
|
||||
uach = uach || {};
|
||||
assignFromEntries.call(this, CH_ALL_VALUES);
|
||||
if (isHTTP_UACH) {
|
||||
assignFromEntries.call(this, [
|
||||
setProps.call(this, CH_ALL_VALUES);
|
||||
if (isHttpUACH) {
|
||||
setProps.call(this, [
|
||||
[BRANDS, itemListToArray(uach[CH_HEADER])],
|
||||
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
|
||||
[BRANDS, itemListToArray(uach[CH_HEADER])],
|
||||
[MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
|
||||
[MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
|
||||
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
|
||||
@@ -933,127 +937,151 @@
|
||||
if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop];
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
function UAParserItem (itemType, ua, rgxMap, uaCH) {
|
||||
assignFromEntries.call(this, [
|
||||
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 ua = this.ua,
|
||||
uaCH = this.uaCH,
|
||||
rgxMap = this.rgxMap;
|
||||
|
||||
switch (this.itemType) {
|
||||
case UA_BROWSER:
|
||||
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
|
||||
if (brands) {
|
||||
for (var i in brands) {
|
||||
var brandName = brands[i].brand,
|
||||
brandVersion = brands[i].version;
|
||||
if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
|
||||
this.set(NAME, strip(GOOGLE+' ', brandName))
|
||||
.set(VERSION, brandVersion)
|
||||
.set(MAJOR, majorize(brandVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UA_CPU:
|
||||
var archName = uaCH[ARCHITECTURE];
|
||||
if (archName) {
|
||||
if (archName && uaCH[BITNESS] == '64') archName += '64';
|
||||
rgxMapper.call(this.data, archName + ';', rgxMap);
|
||||
}
|
||||
break;
|
||||
case UA_DEVICE:
|
||||
if (uaCH[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
if (uaCH[MODEL]) {
|
||||
this.set(MODEL, uaCH[MODEL]);
|
||||
}
|
||||
break;
|
||||
case UA_OS:
|
||||
var osName = uaCH[PLATFORM];
|
||||
if(osName) {
|
||||
var osVersion = uaCH[PLATFORMVER];
|
||||
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
|
||||
this.set(NAME, osName)
|
||||
.set(VERSION, osVersion);
|
||||
}
|
||||
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', createUAParserData(itemType, ua, rgxMap, uaCH)]
|
||||
['data', createIData(this, itemType)]
|
||||
]);
|
||||
this.parse();
|
||||
var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == ua;
|
||||
switch(this.itemType) {
|
||||
case UA_BROWSER:
|
||||
// Brave-specific detection
|
||||
if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
|
||||
this.set(NAME, 'Brave');
|
||||
}
|
||||
this.set(MAJOR, majorize(this.get(VERSION)));
|
||||
break;
|
||||
case UA_DEVICE:
|
||||
if (isSelfNav && !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 (isSelfNav && 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:
|
||||
if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
|
||||
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
|
||||
}
|
||||
break;
|
||||
case UA_RESULT:
|
||||
var createUAParserItem = function (itemType) {
|
||||
return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get();
|
||||
};
|
||||
this.set('ua', ua)
|
||||
.set(UA_BROWSER, createUAParserItem(UA_BROWSER))
|
||||
.set(UA_CPU, createUAParserItem(UA_CPU))
|
||||
.set(UA_DEVICE, createUAParserItem(UA_DEVICE))
|
||||
.set(UA_ENGINE, createUAParserItem(UA_ENGINE))
|
||||
.set(UA_OS, createUAParserItem(UA_OS))
|
||||
.get();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
UAParserItem.prototype.get = function (prop) {
|
||||
if (!prop) return this.data;
|
||||
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
|
||||
};
|
||||
UAParserItem.prototype.parse = function () {
|
||||
if (this.itemType != UA_RESULT) {
|
||||
rgxMapper.call(this.data, this.ua, this.rgxMap);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
UAParserItem.prototype.parseCH = function () {
|
||||
var ua = this.ua,
|
||||
uaCH = this.uaCH,
|
||||
rgxMap = this.rgxMap;
|
||||
|
||||
switch (this.itemType) {
|
||||
case UA_BROWSER:
|
||||
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
|
||||
if (brands) {
|
||||
for (var i in brands) {
|
||||
var brandName = brands[i].brand,
|
||||
brandVersion = brands[i].version;
|
||||
if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
|
||||
this.set(NAME, strip(GOOGLE+' ', brandName))
|
||||
.set(VERSION, brandVersion)
|
||||
.set(MAJOR, majorize(brandVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UA_CPU:
|
||||
var archName = uaCH[ARCHITECTURE];
|
||||
if (archName) {
|
||||
if (archName && uaCH[BITNESS] == '64') archName += '64';
|
||||
rgxMapper.call(this.data, archName + ';', rgxMap);
|
||||
}
|
||||
break;
|
||||
case UA_DEVICE:
|
||||
if (uaCH[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
if (uaCH[MODEL]) {
|
||||
this.set(MODEL, uaCH[MODEL]);
|
||||
}
|
||||
break;
|
||||
case UA_OS:
|
||||
var osName = uaCH[PLATFORM];
|
||||
if(osName) {
|
||||
var osVersion = uaCH[PLATFORMVER];
|
||||
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
|
||||
this.set(NAME, osName)
|
||||
.set(VERSION, osVersion);
|
||||
}
|
||||
break;
|
||||
case UA_RESULT:
|
||||
var createUAParserItemWithCH = function (itemType) {
|
||||
return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get();
|
||||
};
|
||||
this.set('ua', ua)
|
||||
.set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER))
|
||||
.set(UA_CPU, createUAParserItemWithCH(UA_CPU))
|
||||
.set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE))
|
||||
.set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE))
|
||||
.set(UA_OS, createUAParserItemWithCH(UA_OS));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
UAParserItem.prototype.set = function (prop, val) {
|
||||
this.data[prop] = val;
|
||||
return this;
|
||||
};
|
||||
|
||||
function UAParser (ua, extensions, headers) {
|
||||
|
||||
@@ -1077,39 +1105,54 @@
|
||||
return new UAParser(ua, extensions, headers).getResult();
|
||||
}
|
||||
|
||||
var userAgent = ua ||
|
||||
((NAVIGATOR && NAVIGATOR.userAgent) ?
|
||||
NAVIGATOR.userAgent :
|
||||
(headers && headers[USER_AGENT] ?
|
||||
headers[USER_AGENT] :
|
||||
EMPTY)),
|
||||
|
||||
HTTP_UACH = new UAParserDataCH(headers, true),
|
||||
var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string
|
||||
((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent
|
||||
(headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers
|
||||
EMPTY)), // empty string
|
||||
|
||||
httpUACH = new UACHData(headers, true),
|
||||
regexMap = extensions ?
|
||||
extend(defaultRegexes, extensions) :
|
||||
defaultRegexes,
|
||||
|
||||
createUAParserItemFunc = function (itemType) {
|
||||
return function () {
|
||||
return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).get();
|
||||
};
|
||||
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
|
||||
assignFromEntries.call(this, [
|
||||
['getBrowser', createUAParserItemFunc(UA_BROWSER)],
|
||||
['getCPU', createUAParserItemFunc(UA_CPU)],
|
||||
['getDevice', createUAParserItemFunc(UA_DEVICE)],
|
||||
['getEngine', createUAParserItemFunc(UA_ENGINE)],
|
||||
['getOS', createUAParserItemFunc(UA_OS)],
|
||||
['getResult', createUAParserItemFunc(UA_RESULT)],
|
||||
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) {
|
||||
userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
|
||||
if (typeof ua === STR_TYPE)
|
||||
userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
|
||||
return this;
|
||||
}]
|
||||
]).setUA(userAgent);
|
||||
])
|
||||
.setUA(userAgent);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// Generated ESM version of UAParser.js
|
||||
// Generated ESM version of UAParser.js
|
||||
// DO NOT EDIT THIS FILE!
|
||||
// Source: /src/ua-parser.js
|
||||
|
||||
const window = undefined;
|
||||
// Source: /src/main/ua-parser.js
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/* UAParser.js v2.0.0-alpha.2
|
||||
/* UAParser.js v2.0.0-alpha.3
|
||||
Copyright © 2012-2023 Faisal Salman <f@faisalman.com>
|
||||
MIT License *//*
|
||||
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
|
||||
@@ -14,14 +12,17 @@ const window = undefined;
|
||||
Source : https://github.com/faisalman/ua-parser-js */
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* jshint esversion: 6 */
|
||||
/* globals window */
|
||||
|
||||
|
||||
|
||||
//////////////
|
||||
// Constants
|
||||
/////////////
|
||||
|
||||
|
||||
var LIBVERSION = '2.0.0-alpha.2',
|
||||
var LIBVERSION = '2.0.0-alpha.3',
|
||||
EMPTY = '',
|
||||
UNKNOWN = '?',
|
||||
FUNC_TYPE = 'function',
|
||||
@@ -51,12 +52,12 @@ const window = undefined;
|
||||
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_MOBILE = CH_HEADER + '-mobile',
|
||||
CH_HEADER_MODEL = CH_HEADER + '-model',
|
||||
CH_HEADER_PLATFORM = CH_HEADER + '-platform',
|
||||
CH_HEADER_BITNESS = CH_HEADER + '-' + BITNESS,
|
||||
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', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'],
|
||||
CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, BITNESS],
|
||||
UA_BROWSER = 'browser',
|
||||
UA_CPU = 'cpu',
|
||||
UA_DEVICE = 'device',
|
||||
@@ -97,18 +98,7 @@ const window = undefined;
|
||||
// Helper
|
||||
//////////
|
||||
|
||||
var assignFromEntries = function (arr) {
|
||||
for (var i in arr) {
|
||||
var propName = arr[i];
|
||||
if (typeof propName == OBJ_TYPE && propName.length == 2) {
|
||||
this[propName[0]] = propName[1];
|
||||
} else {
|
||||
this[propName] = undefined;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
extend = function (regexes, extensions) {
|
||||
var extend = function (regexes, extensions) {
|
||||
var mergedRegexes = {};
|
||||
for (var i in regexes) {
|
||||
mergedRegexes[i] = extensions[i] && extensions[i].length % 2 === 0 ? extensions[i].concat(regexes[i]) : regexes[i];
|
||||
@@ -152,6 +142,17 @@ const window = undefined;
|
||||
majorize = function (version) {
|
||||
return typeof(version) === STR_TYPE ? strip(/[^\d\.]/g, version).split('.')[0] : undefined;
|
||||
},
|
||||
setProps = function (arr) {
|
||||
for (var i in arr) {
|
||||
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 str.replace(pattern, EMPTY);
|
||||
},
|
||||
@@ -245,18 +246,7 @@ const window = undefined;
|
||||
// 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 = {
|
||||
var windowsVersionMap = {
|
||||
'ME' : '4.90',
|
||||
'NT 3.11' : 'NT3.51',
|
||||
'NT 4.0' : 'NT4.0',
|
||||
@@ -359,7 +349,7 @@ const window = undefined;
|
||||
/(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp
|
||||
/safari (line)\/([\w\.]+)/i, // Line App for iOS
|
||||
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
|
||||
/(chromium|instagram)[\/ ]([-\w\.]+)/i // Chromium/Instagram
|
||||
/(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat
|
||||
], [NAME, VERSION], [
|
||||
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
|
||||
], [VERSION, [NAME, 'GSA']], [
|
||||
@@ -388,7 +378,7 @@ const window = undefined;
|
||||
/version\/([\w\.\,]+) .*(safari)/i // Safari
|
||||
], [VERSION, NAME], [
|
||||
/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
|
||||
], [NAME, [VERSION, strMapper, oldSafariMap]], [
|
||||
], [NAME, [VERSION, '1']], [
|
||||
|
||||
/(webkit|khtml)\/([\w\.]+)/i
|
||||
], [NAME, VERSION], [
|
||||
@@ -429,7 +419,7 @@ const window = undefined;
|
||||
/((?:i[346]|x)86)[;\)]/i // IA32 (x86)
|
||||
], [[ARCHITECTURE, 'ia32']], [
|
||||
|
||||
/\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
|
||||
/\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
|
||||
], [[ARCHITECTURE, 'arm64']], [
|
||||
|
||||
/\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF
|
||||
@@ -486,7 +476,7 @@ const window = undefined;
|
||||
], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
|
||||
|
||||
// Xiaomi
|
||||
/\b(poco[\w ]+)(?: bui|\))/i, // Xiaomi POCO
|
||||
/\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
|
||||
@@ -594,7 +584,7 @@ const window = undefined;
|
||||
], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
|
||||
|
||||
// MIXED
|
||||
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i,
|
||||
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i,
|
||||
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
|
||||
/(hp) ([\w ]+\w)/i, // HP iPAQ
|
||||
/(asus)-?(\w+)/i, // Asus
|
||||
@@ -639,7 +629,7 @@ const window = undefined;
|
||||
], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
|
||||
/crkey/i // Google Chromecast
|
||||
], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
|
||||
/droid.+aft(\w)( bui|\))/i // Fire TV
|
||||
/droid.+aft(\w+)( bui|\))/i // Fire TV
|
||||
], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
|
||||
/\(dtv[\);].+(aquos)/i,
|
||||
/(aquos-tv[\w ]+)\)/i // Sharp
|
||||
@@ -744,7 +734,7 @@ const window = undefined;
|
||||
|
||||
// iOS/macOS
|
||||
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
|
||||
/ios;fbsv\/([\d\.]+)/i,
|
||||
/(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
|
||||
/cfnetwork\/.+darwin/i
|
||||
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
|
||||
/(mac os x) ?([\w\. ]*)/i,
|
||||
@@ -813,23 +803,23 @@ const window = undefined;
|
||||
|
||||
var defaultProps = (function () {
|
||||
var props = { init : {}, isIgnore : {}, isIgnoreRgx : {}, toString : {}};
|
||||
assignFromEntries.call(props.init, [
|
||||
setProps.call(props.init, [
|
||||
[UA_BROWSER, [NAME, VERSION, MAJOR]],
|
||||
[UA_CPU, [ARCHITECTURE]],
|
||||
[UA_DEVICE, [TYPE, MODEL, VENDOR]],
|
||||
[UA_ENGINE, [NAME, VERSION]],
|
||||
[UA_OS, [NAME, VERSION]]
|
||||
]);
|
||||
assignFromEntries.call(props.isIgnore, [
|
||||
setProps.call(props.isIgnore, [
|
||||
[UA_BROWSER, [VERSION, MAJOR]],
|
||||
[UA_ENGINE, [VERSION]],
|
||||
[UA_OS, [VERSION]]
|
||||
]);
|
||||
assignFromEntries.call(props.isIgnoreRgx, [
|
||||
setProps.call(props.isIgnoreRgx, [
|
||||
[UA_BROWSER, / ?browser$/i],
|
||||
[UA_OS, / ?os$/i]
|
||||
]);
|
||||
assignFromEntries.call(props.toString, [
|
||||
setProps.call(props.toString, [
|
||||
[UA_BROWSER, [NAME, VERSION]],
|
||||
[UA_CPU, [ARCHITECTURE]],
|
||||
[UA_DEVICE, [VENDOR, MODEL]],
|
||||
@@ -839,34 +829,47 @@ const window = undefined;
|
||||
return props;
|
||||
})();
|
||||
|
||||
var createUAParserData = function (itemType, ua, rgxMap, uaCH) {
|
||||
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 UAParserData () {
|
||||
assignFromEntries.call(this, init_props);
|
||||
function IData () {
|
||||
setProps.call(this, init_props);
|
||||
}
|
||||
UAParserData.prototype.withClientHints = function () {
|
||||
|
||||
|
||||
IData.prototype.getItem = function () {
|
||||
return item;
|
||||
};
|
||||
|
||||
IData.prototype.withClientHints = function () {
|
||||
|
||||
// nodejs / non-client-hints browsers
|
||||
if (!NAVIGATOR_UADATA) {
|
||||
return new UAParserItem(itemType, ua, rgxMap, uaCH).parseCH().get();
|
||||
return item
|
||||
.parseCH()
|
||||
.get();
|
||||
}
|
||||
|
||||
// browsers based on chromium 85+
|
||||
return NAVIGATOR_UADATA
|
||||
.getHighEntropyValues(CH_ALL_VALUES)
|
||||
.then(function (res) {
|
||||
var JS_UACH = new UAParserDataCH(res, false);
|
||||
return new UAParserItem(itemType, ua, rgxMap, JS_UACH).parseCH().get();
|
||||
return item
|
||||
.setCH(new UACHData(res, false))
|
||||
.parseCH()
|
||||
.get();
|
||||
});
|
||||
};
|
||||
|
||||
IData.prototype.withFeatureCheck = function () {
|
||||
return item.detectFeature().get();
|
||||
};
|
||||
|
||||
if (itemType != UA_RESULT) {
|
||||
UAParserData.prototype.is = function (strToCheck) {
|
||||
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)) {
|
||||
@@ -879,7 +882,7 @@ const window = undefined;
|
||||
}
|
||||
return is;
|
||||
};
|
||||
UAParserData.prototype.toString = function () {
|
||||
IData.prototype.toString = function () {
|
||||
var str = EMPTY;
|
||||
for (var i in toString_props) {
|
||||
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
|
||||
@@ -891,40 +894,39 @@ const window = undefined;
|
||||
}
|
||||
|
||||
if (!NAVIGATOR_UADATA) {
|
||||
UAParserData.prototype.then = function (cb) {
|
||||
IData.prototype.then = function (cb) {
|
||||
var that = this;
|
||||
var UAParserDataResolve = function () {
|
||||
var IDataResolve = function () {
|
||||
for (var prop in that) {
|
||||
if (that.hasOwnProperty(prop)) {
|
||||
this[prop] = that[prop];
|
||||
}
|
||||
}
|
||||
};
|
||||
UAParserDataResolve.prototype = {
|
||||
is : UAParserData.prototype.is,
|
||||
toString : UAParserData.prototype.toString
|
||||
IDataResolve.prototype = {
|
||||
is : IData.prototype.is,
|
||||
toString : IData.prototype.toString
|
||||
};
|
||||
var resolveData = new UAParserDataResolve();
|
||||
var resolveData = new IDataResolve();
|
||||
cb(resolveData);
|
||||
return resolveData;
|
||||
};
|
||||
}
|
||||
|
||||
return new UAParserData();
|
||||
return new IData();
|
||||
};
|
||||
|
||||
/////////////////
|
||||
// Constructor
|
||||
////////////////
|
||||
|
||||
function UAParserDataCH (uach, isHTTP_UACH) {
|
||||
function UACHData (uach, isHttpUACH) {
|
||||
uach = uach || {};
|
||||
assignFromEntries.call(this, CH_ALL_VALUES);
|
||||
if (isHTTP_UACH) {
|
||||
assignFromEntries.call(this, [
|
||||
setProps.call(this, CH_ALL_VALUES);
|
||||
if (isHttpUACH) {
|
||||
setProps.call(this, [
|
||||
[BRANDS, itemListToArray(uach[CH_HEADER])],
|
||||
[FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
|
||||
[BRANDS, itemListToArray(uach[CH_HEADER])],
|
||||
[MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
|
||||
[MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
|
||||
[PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
|
||||
@@ -937,127 +939,151 @@ const window = undefined;
|
||||
if(this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE) this[prop] = uach[prop];
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
function UAParserItem (itemType, ua, rgxMap, uaCH) {
|
||||
assignFromEntries.call(this, [
|
||||
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 ua = this.ua,
|
||||
uaCH = this.uaCH,
|
||||
rgxMap = this.rgxMap;
|
||||
|
||||
switch (this.itemType) {
|
||||
case UA_BROWSER:
|
||||
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
|
||||
if (brands) {
|
||||
for (var i in brands) {
|
||||
var brandName = brands[i].brand,
|
||||
brandVersion = brands[i].version;
|
||||
if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
|
||||
this.set(NAME, strip(GOOGLE+' ', brandName))
|
||||
.set(VERSION, brandVersion)
|
||||
.set(MAJOR, majorize(brandVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UA_CPU:
|
||||
var archName = uaCH[ARCHITECTURE];
|
||||
if (archName) {
|
||||
if (archName && uaCH[BITNESS] == '64') archName += '64';
|
||||
rgxMapper.call(this.data, archName + ';', rgxMap);
|
||||
}
|
||||
break;
|
||||
case UA_DEVICE:
|
||||
if (uaCH[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
if (uaCH[MODEL]) {
|
||||
this.set(MODEL, uaCH[MODEL]);
|
||||
}
|
||||
break;
|
||||
case UA_OS:
|
||||
var osName = uaCH[PLATFORM];
|
||||
if(osName) {
|
||||
var osVersion = uaCH[PLATFORMVER];
|
||||
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
|
||||
this.set(NAME, osName)
|
||||
.set(VERSION, osVersion);
|
||||
}
|
||||
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', createUAParserData(itemType, ua, rgxMap, uaCH)]
|
||||
['data', createIData(this, itemType)]
|
||||
]);
|
||||
this.parse();
|
||||
var isSelfNav = NAVIGATOR && NAVIGATOR.userAgent == ua;
|
||||
switch(this.itemType) {
|
||||
case UA_BROWSER:
|
||||
// Brave-specific detection
|
||||
if (isSelfNav && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
|
||||
this.set(NAME, 'Brave');
|
||||
}
|
||||
this.set(MAJOR, majorize(this.get(VERSION)));
|
||||
break;
|
||||
case UA_DEVICE:
|
||||
if (isSelfNav && !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 (isSelfNav && 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:
|
||||
if (isSelfNav && !this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
|
||||
this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
|
||||
}
|
||||
break;
|
||||
case UA_RESULT:
|
||||
var createUAParserItem = function (itemType) {
|
||||
return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).get();
|
||||
};
|
||||
this.set('ua', ua)
|
||||
.set(UA_BROWSER, createUAParserItem(UA_BROWSER))
|
||||
.set(UA_CPU, createUAParserItem(UA_CPU))
|
||||
.set(UA_DEVICE, createUAParserItem(UA_DEVICE))
|
||||
.set(UA_ENGINE, createUAParserItem(UA_ENGINE))
|
||||
.set(UA_OS, createUAParserItem(UA_OS))
|
||||
.get();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
UAParserItem.prototype.get = function (prop) {
|
||||
if (!prop) return this.data;
|
||||
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
|
||||
};
|
||||
UAParserItem.prototype.parse = function () {
|
||||
if (this.itemType != UA_RESULT) {
|
||||
rgxMapper.call(this.data, this.ua, this.rgxMap);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
UAParserItem.prototype.parseCH = function () {
|
||||
var ua = this.ua,
|
||||
uaCH = this.uaCH,
|
||||
rgxMap = this.rgxMap;
|
||||
|
||||
switch (this.itemType) {
|
||||
case UA_BROWSER:
|
||||
var brands = uaCH[FULLVERLIST] || uaCH[BRANDS];
|
||||
if (brands) {
|
||||
for (var i in brands) {
|
||||
var brandName = brands[i].brand,
|
||||
brandVersion = brands[i].version;
|
||||
if (!/not.a.brand/i.test(brandName) && (i < 1 || /chromi/i.test(this.get(NAME)))) {
|
||||
this.set(NAME, strip(GOOGLE+' ', brandName))
|
||||
.set(VERSION, brandVersion)
|
||||
.set(MAJOR, majorize(brandVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UA_CPU:
|
||||
var archName = uaCH[ARCHITECTURE];
|
||||
if (archName) {
|
||||
if (archName && uaCH[BITNESS] == '64') archName += '64';
|
||||
rgxMapper.call(this.data, archName + ';', rgxMap);
|
||||
}
|
||||
break;
|
||||
case UA_DEVICE:
|
||||
if (uaCH[MOBILE]) {
|
||||
this.set(TYPE, MOBILE);
|
||||
}
|
||||
if (uaCH[MODEL]) {
|
||||
this.set(MODEL, uaCH[MODEL]);
|
||||
}
|
||||
break;
|
||||
case UA_OS:
|
||||
var osName = uaCH[PLATFORM];
|
||||
if(osName) {
|
||||
var osVersion = uaCH[PLATFORMVER];
|
||||
if (osName == WINDOWS) osVersion = (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10');
|
||||
this.set(NAME, osName)
|
||||
.set(VERSION, osVersion);
|
||||
}
|
||||
break;
|
||||
case UA_RESULT:
|
||||
var createUAParserItemWithCH = function (itemType) {
|
||||
return new UAParserItem(itemType, ua, rgxMap[itemType], uaCH).parseCH().get();
|
||||
};
|
||||
this.set('ua', ua)
|
||||
.set(UA_BROWSER, createUAParserItemWithCH(UA_BROWSER))
|
||||
.set(UA_CPU, createUAParserItemWithCH(UA_CPU))
|
||||
.set(UA_DEVICE, createUAParserItemWithCH(UA_DEVICE))
|
||||
.set(UA_ENGINE, createUAParserItemWithCH(UA_ENGINE))
|
||||
.set(UA_OS, createUAParserItemWithCH(UA_OS));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
UAParserItem.prototype.set = function (prop, val) {
|
||||
this.data[prop] = val;
|
||||
return this;
|
||||
};
|
||||
|
||||
function UAParser (ua, extensions, headers) {
|
||||
|
||||
@@ -1081,39 +1107,54 @@ const window = undefined;
|
||||
return new UAParser(ua, extensions, headers).getResult();
|
||||
}
|
||||
|
||||
var userAgent = ua ||
|
||||
((NAVIGATOR && NAVIGATOR.userAgent) ?
|
||||
NAVIGATOR.userAgent :
|
||||
(headers && headers[USER_AGENT] ?
|
||||
headers[USER_AGENT] :
|
||||
EMPTY)),
|
||||
|
||||
HTTP_UACH = new UAParserDataCH(headers, true),
|
||||
var userAgent = typeof ua === STR_TYPE ? ua : // Passed user-agent string
|
||||
((NAVIGATOR && NAVIGATOR.userAgent) ? NAVIGATOR.userAgent : // navigator.userAgent
|
||||
(headers && headers[USER_AGENT] ? headers[USER_AGENT] : // User-Agent from passed headers
|
||||
EMPTY)), // empty string
|
||||
|
||||
httpUACH = new UACHData(headers, true),
|
||||
regexMap = extensions ?
|
||||
extend(defaultRegexes, extensions) :
|
||||
defaultRegexes,
|
||||
|
||||
createUAParserItemFunc = function (itemType) {
|
||||
return function () {
|
||||
return new UAParserItem(itemType, userAgent, itemType == UA_RESULT ? regexMap : regexMap[itemType], HTTP_UACH).get();
|
||||
};
|
||||
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
|
||||
assignFromEntries.call(this, [
|
||||
['getBrowser', createUAParserItemFunc(UA_BROWSER)],
|
||||
['getCPU', createUAParserItemFunc(UA_CPU)],
|
||||
['getDevice', createUAParserItemFunc(UA_DEVICE)],
|
||||
['getEngine', createUAParserItemFunc(UA_ENGINE)],
|
||||
['getOS', createUAParserItemFunc(UA_OS)],
|
||||
['getResult', createUAParserItemFunc(UA_RESULT)],
|
||||
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) {
|
||||
userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
|
||||
if (typeof ua === STR_TYPE)
|
||||
userAgent = ua.length > UA_MAX_LENGTH ? trim(ua, UA_MAX_LENGTH) : ua;
|
||||
return this;
|
||||
}]
|
||||
]).setUA(userAgent);
|
||||
])
|
||||
.setUA(userAgent);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
20
test/jazzer-fuzz-test.js
Normal file
20
test/jazzer-fuzz-test.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const { FuzzedDataProvider } = require('@jazzer.js/core');
|
||||
const UAParser = require('ua-parser-js');
|
||||
const UA_MAX_LENGTH = 350;
|
||||
|
||||
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}`);
|
||||
}
|
||||
};
|
||||
79
test/mocha-test-extension.js
Normal file
79
test/mocha-test-extension.js
Normal file
@@ -0,0 +1,79 @@
|
||||
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('ua-parser-js');
|
||||
const Ext = require('ua-parser-js/extensions');
|
||||
|
||||
describe('Bots', () => {
|
||||
it('Can detect bots', () => {
|
||||
const googleBot = 'Googlebot-Video/1.0';
|
||||
const gptBot = 'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; GPTBot/1.0; +https://openai.com/gptbot)';
|
||||
const msnBot = 'msnbot-media/1.1 (+http://search.msn.com/msnbot.htm)';
|
||||
const bingPreview = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b';
|
||||
const opera = 'Opera/8.5 (Macintosh; PPC Mac OS X; U; en)';
|
||||
const wget = 'Wget/1.21.1';
|
||||
const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)';
|
||||
const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)';
|
||||
const 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)';
|
||||
|
||||
const botParser = new UAParser(Ext.Bots);
|
||||
assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"});
|
||||
assert.deepEqual(botParser.setUA(gptBot).getBrowser(), {name: "GPTBot", version: "1.0", major: "1", type: "bot"});
|
||||
assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"});
|
||||
assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"});
|
||||
assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8"});
|
||||
|
||||
// try merging Bots & CLIs
|
||||
const botsAndCLIs = { browser : [...Ext.Bots.browser, ...Ext.CLIs.browser]};
|
||||
const botsAndCLIsParser = new UAParser(botsAndCLIs);
|
||||
assert.deepEqual(botsAndCLIsParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"cli"});
|
||||
assert.deepEqual(botsAndCLIsParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"});
|
||||
|
||||
const emailParser = new UAParser(Ext.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 moduleParser = new UAParser(Ext.Modules);
|
||||
assert.deepEqual(moduleParser.setUA(axios).getBrowser(), {name: "axios", version: "1.3.5", major: "1", type: "module"});
|
||||
assert.deepEqual(moduleParser.setUA(jsdom).getBrowser(), {name: "jsdom", version: "20.0.3", major: "20", type: "module"});
|
||||
assert.deepEqual(moduleParser.setUA(scrapy).getBrowser(), {name: "Scrapy", version: "1.5.0", major: "1", type: "module"});
|
||||
});
|
||||
});
|
||||
|
||||
// TODO : move test spec to JSON file
|
||||
|
||||
describe('Testing regexes', () => {
|
||||
|
||||
let regexes;
|
||||
|
||||
before('Read main js file', () => {
|
||||
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('Begin testing', () => {
|
||||
it('all regexes in extension file', () => {
|
||||
regexes.forEach(regex => {
|
||||
describe('Test against `safe-regex` : ' + regex, () => {
|
||||
it('should be safe from potentially vulnerable regex', () => {
|
||||
assert.strictEqual(safe(regex), true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
35
test/mocha-test-helpers.js
Normal file
35
test/mocha-test-helpers.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const { isFrozenUA } = require('ua-parser-js/helpers');
|
||||
const assert = require('assert');
|
||||
|
||||
describe('isFrozenUA', () => {
|
||||
it('Returns whether a user agent is frozen', () => {
|
||||
|
||||
const regularWindowsUA = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36";
|
||||
const freezedWindowsUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36";
|
||||
|
||||
const regularMacUA = "";
|
||||
const freezedMacUA = "";
|
||||
|
||||
const regularLinuxUA = "";
|
||||
const freezedLinuxUA = "";
|
||||
|
||||
const regularCrOSUA = "";
|
||||
const freezedCrOSUA = "";
|
||||
|
||||
const regularFuchsiaUA = "";
|
||||
const freezedFuchsiaUA = "";
|
||||
|
||||
const regularMobileUA = "Mozilla/5.0 (Linux; Android 9; SM-A205U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Mobile Safari/537.36";
|
||||
const freezedMobileUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Mobile Safari/537.36";
|
||||
|
||||
const regularTabletUA = "Mozilla/5.0 (Linux; Android 9; SM-T810) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.1234.56 Safari/537.36";
|
||||
const freezedTabletUA = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36";
|
||||
|
||||
assert.strictEqual(isFrozenUA(regularWindowsUA), false);
|
||||
assert.strictEqual(isFrozenUA(freezedWindowsUA), true);
|
||||
assert.strictEqual(isFrozenUA(regularMobileUA), false);
|
||||
assert.strictEqual(isFrozenUA(freezedMobileUA), true);
|
||||
assert.strictEqual(isFrozenUA(regularTabletUA), false);
|
||||
assert.strictEqual(isFrozenUA(freezedTabletUA), true);
|
||||
});
|
||||
});
|
||||
@@ -4,12 +4,12 @@ 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 UAParser = require('ua-parser-js');
|
||||
var browsers = require('./specs/browser-all.json');
|
||||
var cpus = require('./specs/cpu-all.json');
|
||||
var devices = require('./specs/device-all.json');
|
||||
var engines = require('./specs/engine-all.json');
|
||||
var os = require('./specs/os-all.json');
|
||||
var parser = new UAParser();
|
||||
var methods = [
|
||||
{
|
||||
@@ -82,7 +82,6 @@ describe('Returns', function () {
|
||||
assert.deepEqual(new UAParser('').getResult(),
|
||||
{
|
||||
ua : '',
|
||||
//ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, fullVersionList: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined },
|
||||
browser: { name: undefined, version: undefined, major: undefined },
|
||||
cpu: { architecture: undefined },
|
||||
device: { vendor: undefined, model: undefined, type: undefined },
|
||||
@@ -160,7 +159,7 @@ describe('Testing regexes', function () {
|
||||
var regexes;
|
||||
|
||||
before('Read main js file', function () {
|
||||
var code = fs.readFileSync('src/ua-parser.js', 'utf8').toString();
|
||||
var code = fs.readFileSync('src/main/ua-parser.js', 'utf8').toString();
|
||||
var ast = parseJS(code, { sourceType: "script" });
|
||||
regexes = [];
|
||||
traverse(ast, {
|
||||
@@ -362,43 +361,6 @@ describe('Map UA-CH headers', function () {
|
||||
let engine = new UAParser(headers).getEngine().withClientHints();
|
||||
let os = new UAParser(headers).getOS().withClientHints();
|
||||
|
||||
/* let ua_ch = {
|
||||
"architecture": "ARM",
|
||||
"bitness": "64",
|
||||
"brands": [
|
||||
{
|
||||
"brand": "Chromium",
|
||||
"version": "93"
|
||||
},
|
||||
{
|
||||
"brand": "Google Chrome",
|
||||
"version": "93"
|
||||
},
|
||||
{
|
||||
"brand": " Not;A Brand",
|
||||
"version": "99"
|
||||
}
|
||||
],
|
||||
"fullVersionList": [
|
||||
{
|
||||
"brand": "Chromium",
|
||||
"version": "93.0.1.2"
|
||||
},
|
||||
{
|
||||
"brand": "Google Chrome",
|
||||
"version": "93.0.1.2"
|
||||
},
|
||||
{
|
||||
"brand": " Not;A Brand",
|
||||
"version": "99.0.1.2"
|
||||
}
|
||||
],
|
||||
"mobile": true,
|
||||
"model": "Pixel 99",
|
||||
"platform": "Windows",
|
||||
"platformVersion": "13"
|
||||
};*/
|
||||
|
||||
it('Can read from client-hints headers using `withClientHints()`', function () {
|
||||
|
||||
//assert.deepEqual(uap.ua_ch, ua_ch);
|
||||
@@ -436,7 +398,6 @@ describe('Map UA-CH headers', function () {
|
||||
engine = new UAParser(headers).getEngine();
|
||||
os = new UAParser(headers).getOS();
|
||||
|
||||
//assert.deepEqual(uap.ua_ch, ua_ch);
|
||||
assert.strictEqual(uap.browser.name, "Chrome");
|
||||
assert.strictEqual(uap.browser.version, "110.0.0.0");
|
||||
assert.strictEqual(uap.browser.major, "110");
|
||||
@@ -459,18 +420,6 @@ describe('Map UA-CH headers', function () {
|
||||
|
||||
uap = UAParser(headers2).withClientHints();
|
||||
|
||||
/* ua_ch = {
|
||||
"architecture": undefined,
|
||||
"bitness": undefined,
|
||||
"brands": undefined,
|
||||
"fullVersionList": undefined,
|
||||
"mobile": true,
|
||||
"model": undefined,
|
||||
"platform": undefined,
|
||||
"platformVersion": undefined
|
||||
};
|
||||
|
||||
assert.deepEqual(uap.ua_ch, ua_ch);*/
|
||||
assert.strictEqual(uap.browser.name, "Chrome");
|
||||
assert.strictEqual(uap.browser.version, "110.0.0.0");
|
||||
assert.strictEqual(uap.browser.major, "110");
|
||||
@@ -513,70 +462,4 @@ describe('Map UA-CH headers', function () {
|
||||
assert.strictEqual(ua.device.is("tablet"), false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Map UA-CH JS', () => {
|
||||
|
||||
it('does not throw when using withClientHints() in non-supported environment', () => {
|
||||
assert.doesNotThrow(() => {
|
||||
new UAParser().getResult().withClientHints();
|
||||
});
|
||||
});
|
||||
|
||||
window = { navigator : { userAgent : '' , userAgentData : new NavigatorUAData() } };
|
||||
function NavigatorUAData () {
|
||||
this.mobile = false;
|
||||
this.platform = '';
|
||||
this.brands = [{ brand : 'A Chromium-based Browser', version : '1' }];
|
||||
this.getHighEntropyValues = (values) => {
|
||||
return new Promise((resolve) => {
|
||||
const result = {
|
||||
brands : [{ brand : 'A Chromium-based Browser', version : '1' }],
|
||||
fullVersionList : [{ brand : 'A Chromium-based Browser', version : '1.2.3' }],
|
||||
architecture : 'x86',
|
||||
bitness : '64',
|
||||
mobile : true,
|
||||
model : 'Galaxy S3',
|
||||
platform : 'Android',
|
||||
platformVersion : '1000'
|
||||
};
|
||||
resolve(result);
|
||||
});
|
||||
}
|
||||
};
|
||||
delete require.cache[require.resolve('./../src/ua-parser')];
|
||||
const UAParserWithWindow = require('./../src/ua-parser');
|
||||
|
||||
it('Can read client hints from browser', async () => {
|
||||
|
||||
let uap = new UAParserWithWindow()
|
||||
|
||||
let os = await uap.getOS().withClientHints();
|
||||
|
||||
assert.strictEqual(os.name, 'Android');
|
||||
assert.strictEqual(os.is('Android'), true);
|
||||
assert.strictEqual(os.toString(), 'Android 1000');
|
||||
|
||||
let result = await uap.getResult().withClientHints();
|
||||
|
||||
assert.strictEqual(result.browser.name, 'A Chromium-based Browser');
|
||||
assert.strictEqual(result.browser.version, '1.2.3');
|
||||
assert.strictEqual(result.cpu.architecture, 'amd64');
|
||||
assert.strictEqual(result.os.name, 'Android');
|
||||
|
||||
await uap.getDevice().withClientHints().then((device) => {
|
||||
assert.strictEqual(device.type, 'mobile');
|
||||
assert.strictEqual(device.vendor, undefined);
|
||||
assert.strictEqual(device.model, 'Galaxy S3');
|
||||
});
|
||||
|
||||
let result_without_ch = uap.getResult();
|
||||
|
||||
assert.strictEqual(result_without_ch.browser.name, undefined);
|
||||
|
||||
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");
|
||||
|
||||
// TODO : create full tests
|
||||
});
|
||||
});
|
||||
77
test/playwright-test-browser.spec.mjs
Normal file
77
test/playwright-test-browser.spec.mjs
Normal file
@@ -0,0 +1,77 @@
|
||||
// @ts-check
|
||||
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`;
|
||||
|
||||
test.describe('test input', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(localHtml);
|
||||
});
|
||||
|
||||
test('accept empty string', async ({ page }) => {
|
||||
// @ts-ignore
|
||||
const uap = await page.evaluate(async () => await UAParser(''));
|
||||
expect(uap).toHaveProperty('ua', '');
|
||||
});
|
||||
});
|
||||
|
||||
test('read client hints data', async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
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'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await page.goto(localHtml);
|
||||
|
||||
// @ts-ignore
|
||||
const uap = await page.evaluate(async () => await UAParser().withClientHints());
|
||||
|
||||
expect(uap).toHaveProperty('browser.name', 'New Browser');
|
||||
expect(uap).toHaveProperty('os.name', 'New OS');
|
||||
});
|
||||
|
||||
test('detect Brave', async ({ page }) => {
|
||||
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', 'Chrome Headless');
|
||||
|
||||
// @ts-ignore
|
||||
uap = await page.evaluate(() => UAParser().withFeatureCheck());
|
||||
expect(uap).toHaveProperty('browser.name', 'Brave');
|
||||
});
|
||||
@@ -199,6 +199,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",
|
||||
@@ -1084,8 +1124,8 @@
|
||||
"expect" :
|
||||
{
|
||||
"name" : "Safari",
|
||||
"version" : "2.0.4",
|
||||
"major" : "2"
|
||||
"version" : "1",
|
||||
"major" : "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1328,6 +1368,56 @@
|
||||
"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",
|
||||
@@ -1428,6 +1518,16 @@
|
||||
"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",
|
||||
@@ -1769,5 +1869,15 @@
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -23,6 +23,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)",
|
||||
@@ -1,4 +1,14 @@
|
||||
[{
|
||||
[
|
||||
{
|
||||
"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": "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": {
|
||||
@@ -187,6 +197,15 @@
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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",
|
||||
@@ -413,31 +432,31 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"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 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-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-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",
|
||||
@@ -447,8 +466,8 @@
|
||||
"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": {
|
||||
@@ -456,8 +475,8 @@
|
||||
"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": {
|
||||
@@ -465,8 +484,8 @@
|
||||
"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": {
|
||||
@@ -475,6 +494,15 @@
|
||||
"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",
|
||||
@@ -485,13 +513,13 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"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",
|
||||
@@ -564,8 +592,8 @@
|
||||
"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": {
|
||||
@@ -573,8 +601,8 @@
|
||||
"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": {
|
||||
@@ -582,8 +610,8 @@
|
||||
"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": {
|
||||
@@ -591,8 +619,8 @@
|
||||
"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": {
|
||||
@@ -600,8 +628,8 @@
|
||||
"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": {
|
||||
@@ -609,8 +637,8 @@
|
||||
"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": {
|
||||
@@ -745,6 +773,51 @@
|
||||
"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 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 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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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",
|
||||
@@ -925,6 +998,24 @@
|
||||
"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",
|
||||
@@ -943,6 +1034,15 @@
|
||||
"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",
|
||||
@@ -1061,23 +1161,30 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"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" : "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": "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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1215,6 +1322,15 @@
|
||||
"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 N9",
|
||||
"ua": "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13",
|
||||
@@ -1314,6 +1430,15 @@
|
||||
"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": "undefined",
|
||||
"model": "HD1913",
|
||||
"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",
|
||||
@@ -1332,6 +1457,15 @@
|
||||
"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": "OPPO",
|
||||
"model": "CPH2413",
|
||||
"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",
|
||||
@@ -1494,6 +1628,33 @@
|
||||
"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 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": "undefined",
|
||||
"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": "undefined",
|
||||
"model": "RMX3363",
|
||||
"type": "mobile"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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 ",
|
||||
@@ -1683,6 +1844,24 @@
|
||||
"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",
|
||||
@@ -1736,7 +1915,7 @@
|
||||
"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",
|
||||
@@ -1755,6 +1934,15 @@
|
||||
"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",
|
||||
@@ -1840,9 +2028,9 @@
|
||||
"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"
|
||||
"vendor": "Samsung",
|
||||
"model": "SM-P605",
|
||||
"type": "tablet"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1953,6 +2141,15 @@
|
||||
"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",
|
||||
@@ -2116,40 +2313,45 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc" : "Tesla",
|
||||
"ua" : "Mozilla/5.0 (X11; GNU/Linux) AppleWebKit/601.1 (KHTML, like Gecko) Tesla QtCarBrowser Safari/601.1",
|
||||
"expect" :
|
||||
{
|
||||
"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": "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" :
|
||||
{
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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" :
|
||||
{
|
||||
"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" :
|
||||
{
|
||||
"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"
|
||||
@@ -2317,6 +2519,51 @@
|
||||
"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 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",
|
||||
@@ -2326,6 +2573,15 @@
|
||||
"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": "undefined",
|
||||
"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",
|
||||
@@ -2362,6 +2618,51 @@
|
||||
"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": "undefined",
|
||||
"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": "undefined",
|
||||
"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": "undefined",
|
||||
"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": "PlayStation 4",
|
||||
"ua": "Mozilla/5.0 (PlayStation 4 3.00) AppleWebKit/537.73 (KHTML, like Gecko)",
|
||||
@@ -2461,6 +2762,15 @@
|
||||
"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",
|
||||
@@ -2772,6 +3082,15 @@
|
||||
"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": "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",
|
||||
@@ -2907,6 +3226,15 @@
|
||||
"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",
|
||||
@@ -2925,6 +3253,15 @@
|
||||
"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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"desc": "Unknown Mobile using Firefox",
|
||||
"ua": "Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0",
|
||||
@@ -3006,4 +3343,4 @@
|
||||
"type": "mobile"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -773,6 +773,15 @@
|
||||
"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" : "watchOS",
|
||||
"ua" : "server-bag [Watch OS,8.4,19S546,Watch3,4]",
|
||||
@@ -1,36 +0,0 @@
|
||||
const assert = require('assert');
|
||||
const safeRegex = require('safe-regex');
|
||||
const UAParser = require('ua-parser-js');
|
||||
const { Bots, Emails, Tools } = require('ua-parser-js/extensions');
|
||||
|
||||
describe('Bots', () => {
|
||||
it('Can detect bots', () => {
|
||||
const googleBot = 'Googlebot-Video/1.0';
|
||||
const msnBot = 'msnbot-media/1.1 (+http://search.msn.com/msnbot.htm)';
|
||||
const bingPreview = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534+ (KHTML, like Gecko) BingPreview/1.0b';
|
||||
const opera = 'Opera/8.5 (Macintosh; PPC Mac OS X; U; en)';
|
||||
const wget = 'Wget/1.21.1';
|
||||
const facebookBot = 'Mozilla/5.0 (compatible; FacebookBot/1.0; +https://developers.facebook.com/docs/sharing/webmasters/facebookbot/)';
|
||||
const outlook = 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Microsoft Outlook 16.0.9126; Microsoft Outlook 16.0.9126; ms-office; MSOffice 16)';
|
||||
const thunderbird = 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.13.0';
|
||||
|
||||
const botParser = new UAParser(Bots);
|
||||
assert.deepEqual(botParser.setUA(googleBot).getBrowser(), {name: "Googlebot-Video", version: "1.0", major: "1", type: "bot"});
|
||||
assert.deepEqual(botParser.setUA(msnBot).getBrowser(), {name: "msnbot-media", version: "1.1", major: "1", type: "bot"});
|
||||
assert.deepEqual(botParser.setUA(bingPreview).getBrowser(), {name: "BingPreview", version: "1.0b", major: "1", type: "bot"});
|
||||
assert.deepEqual(botParser.setUA(opera).getBrowser(), {name: "Opera", version: "8.5", major: "8"});
|
||||
|
||||
// try merging Bots & Tools
|
||||
const botsAndTools = { browser : [...Bots.browser, ...Tools.browser]};
|
||||
const botolParser = new UAParser(botsAndTools);
|
||||
assert.deepEqual(botolParser.setUA(wget).getBrowser(), {name: "Wget", version: "1.21.1", major: "1", type:"tool"});
|
||||
assert.deepEqual(botolParser.setUA(facebookBot).getBrowser(), {name: "FacebookBot", version: "1.0", major: "1", type:"bot"});
|
||||
|
||||
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"});
|
||||
});
|
||||
});
|
||||
|
||||
// TODO : move test spec to JSON file
|
||||
// TODO : check for safe-regex
|
||||
Reference in New Issue
Block a user