mirror of
https://github.com/faisalman/ua-parser-js.git
synced 2025-09-27 16:08:47 +03:00
parent
5672a2e15c
commit
aff5a209f8
65
readme.md
65
readme.md
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
# UAParser.js
|
# UAParser.js
|
||||||
|
|
||||||
JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data that can be used either in browser (client-side) or node.js (server-side).
|
JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model from User-Agent & Client-Hints data that can be used either in browser (client-side) or node.js (server-side).
|
||||||
|
|
||||||
* Author : Faisal Salman <<f@faisalman.com>>
|
* Author : Faisal Salman <<f@faisalman.com>>
|
||||||
* Demo : https://faisalman.github.io/ua-parser-js
|
* Demo : https://faisalman.github.io/ua-parser-js
|
||||||
@ -56,25 +56,28 @@ console.log(parser); // {}
|
|||||||
let parserResults = parser.getResult();
|
let parserResults = parser.getResult();
|
||||||
console.log(parserResults);
|
console.log(parserResults);
|
||||||
/** {
|
/** {
|
||||||
"ua": "",
|
"ua" : "",
|
||||||
"browser": {},
|
"browser" : {},
|
||||||
"engine": {},
|
"engine" : {},
|
||||||
"os": {},
|
"os" : {},
|
||||||
"device": {},
|
"device" : {},
|
||||||
"cpu": {}
|
"cpu" : {}
|
||||||
|
|
||||||
|
// since@1.1
|
||||||
|
,"ua_ch" : {}
|
||||||
} */
|
} */
|
||||||
```
|
```
|
||||||
|
|
||||||
When you call UAParser without the `new` keyword, it will automatically call `getResult()` function and return the parsed results.
|
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@1.1)])`
|
* `UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])`
|
||||||
* returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} }`
|
* returns result object `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }`
|
||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
#### Methods table
|
#### Methods table
|
||||||
The methods are self explanatory, here's a small overview on all the available methods:
|
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: {} }`.
|
`{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }`.
|
||||||
|
|
||||||
* `getBrowser()` - returns the browser name and version.
|
* `getBrowser()` - returns the browser name and version.
|
||||||
* `getDevice()` - returns the device model, type, vendor.
|
* `getDevice()` - returns the device model, type, vendor.
|
||||||
@ -87,7 +90,7 @@ The methods are self explanatory, here's a small overview on all the available m
|
|||||||
---
|
---
|
||||||
|
|
||||||
* `getResult()`
|
* `getResult()`
|
||||||
* returns `{ ua: '', browser: UABrowser {}, cpu: UACPU {}, device: UADevice {}, engine: UAEngine {}, os: UAOS {} }`
|
* returns `{ ua: '', browser: {}, cpu: {}, device: {}, engine: {}, os: {} /* added since@1.1: ua_ch: {} */ }`
|
||||||
|
|
||||||
* `getBrowser()`
|
* `getBrowser()`
|
||||||
* returns `{ name: '', version: '' }`
|
* returns `{ name: '', version: '' }`
|
||||||
@ -180,7 +183,7 @@ Ubuntu, Unix, VectorLinux, Viera, watchOS, WebOS, Windows [Phone/Mobile], Zenwal
|
|||||||
* set UA string to be parsed
|
* set UA string to be parsed
|
||||||
* returns current instance
|
* returns current instance
|
||||||
|
|
||||||
#### * is() utility `since@1.1`
|
#### * `is()` utility `since@1.1`
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Is just a shorthand to check whether specified item has a property with equals value (case-insensitive)
|
// Is just a shorthand to check whether specified item has a property with equals value (case-insensitive)
|
||||||
@ -244,7 +247,7 @@ let engine = uap.getEngine();
|
|||||||
engine.is("Blink"); // true
|
engine.is("Blink"); // true
|
||||||
```
|
```
|
||||||
|
|
||||||
#### * toString() utility `since@1.1`
|
#### * `toString()` utility `since@1.1`
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Retrieve full-name values as a string
|
// Retrieve full-name values as a string
|
||||||
@ -285,9 +288,33 @@ engine.version; // "28.0.1500.95"
|
|||||||
engine.toString(); // "Blink 28.0.1500.95"
|
engine.toString(); // "Blink 28.0.1500.95"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### * `withClientHints()` `since@1.1`
|
||||||
|
|
||||||
|
Unlike reading user-agent data, accessing client-hints data in browser-environment must be done in an asynchronous way. Worry not, you can chain the UAParser's `get*` method with `withClientHints()` to read the client-hints data as well that will return the updated data as a `Promise`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
(async function () {
|
||||||
|
let ua = new UAParser();
|
||||||
|
|
||||||
|
// get browser data from user-agent only :
|
||||||
|
let browser = ua.getBrowser();
|
||||||
|
console.log('Using User-Agent: ', browser);
|
||||||
|
|
||||||
|
// get browser data from client-hints (with user-agent as fallback) :
|
||||||
|
browser = await ua.getBrowser().withClientHints();
|
||||||
|
console.log('Using Client-Hints: ', browser);
|
||||||
|
|
||||||
|
// alternatively :
|
||||||
|
ua.getBrowser().withClientHints().then(function (browser) {
|
||||||
|
console.log('Using Client-Hints: ', browser);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Extending Regex
|
## 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.
|
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.
|
||||||
|
|
||||||
* `UAParser([uastring,] extensions [,headers:object(since@1.1)])`
|
* `UAParser([uastring,] extensions [,headers:object(since@1.1)])`
|
||||||
|
|
||||||
@ -359,6 +386,18 @@ console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "1
|
|||||||
cpu: {
|
cpu: {
|
||||||
architecture: ""
|
architecture: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// added since@1.1:
|
||||||
|
,ua_ch: {
|
||||||
|
architecture: "",
|
||||||
|
brands: "",
|
||||||
|
bitness: "",
|
||||||
|
fullVersionList: "",
|
||||||
|
mobile: "",
|
||||||
|
model: "",
|
||||||
|
platform: "",
|
||||||
|
platformVersion: ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// Default result depends on current window.navigator.userAgent value
|
// Default result depends on current window.navigator.userAgent value
|
||||||
|
308
src/ua-parser.js
308
src/ua-parser.js
@ -46,7 +46,8 @@
|
|||||||
CH_HEADER_MOBILE = CH_HEADER + '-mobile',
|
CH_HEADER_MOBILE = CH_HEADER + '-mobile',
|
||||||
CH_HEADER_MODEL = CH_HEADER + '-model',
|
CH_HEADER_MODEL = CH_HEADER + '-model',
|
||||||
CH_HEADER_PLATFORM = CH_HEADER + '-platform',
|
CH_HEADER_PLATFORM = CH_HEADER + '-platform',
|
||||||
CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version';
|
CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + '-version',
|
||||||
|
CH_ALL_VALUES = ['brands', 'fullVersionList', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness'];
|
||||||
|
|
||||||
var AMAZON = 'Amazon',
|
var AMAZON = 'Amazon',
|
||||||
APPLE = 'Apple',
|
APPLE = 'Apple',
|
||||||
@ -74,6 +75,13 @@
|
|||||||
MAC_OS = 'Mac OS',
|
MAC_OS = 'Mac OS',
|
||||||
WINDOWS = 'Windows';
|
WINDOWS = 'Windows';
|
||||||
|
|
||||||
|
var NAVIGATOR = (typeof window !== UNDEF_TYPE && window.navigator) ?
|
||||||
|
window.navigator :
|
||||||
|
undefined,
|
||||||
|
NAVIGATOR_UADATA = (NAVIGATOR && NAVIGATOR.userAgentData) ?
|
||||||
|
NAVIGATOR.userAgentData :
|
||||||
|
undefined;
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
// Helper
|
// Helper
|
||||||
//////////
|
//////////
|
||||||
@ -813,19 +821,23 @@
|
|||||||
// Constructor
|
// Constructor
|
||||||
////////////////
|
////////////////
|
||||||
|
|
||||||
function UACHData (uach, isBrowser) {
|
function UAParserDataCH (uach, isHTTP_UACH) {
|
||||||
uach = uach || {};
|
uach = uach || {};
|
||||||
if (!isBrowser) {
|
initialize.call(this, CH_ALL_VALUES);
|
||||||
initialize.call(this, ['brands', MOBILE, MODEL, 'platform', 'platformVersion', ARCHITECTURE, 'bitness']);
|
if (isHTTP_UACH) {
|
||||||
if ((uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER])) {
|
var toArray = function (header) {
|
||||||
this.brands = [];
|
if (!header) return undefined;
|
||||||
var tokens = strip(/\\?\"/g, (uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER])).split(', ');
|
var arr = [];
|
||||||
|
var tokens = strip(/\\?\"/g, header).split(', ');
|
||||||
for (var i = 0; i < tokens.length; i++) {
|
for (var i = 0; i < tokens.length; i++) {
|
||||||
var token = tokens[i].split(';v=');
|
var token = tokens[i].split(';v=');
|
||||||
this.brands[i] = { brand : token[0], version : token[1] };
|
arr[i] = { brand : token[0], version : token[1] };
|
||||||
}
|
}
|
||||||
}
|
return arr;
|
||||||
this.mobile = uach[CH_HEADER_MOBILE] == '?1';
|
};
|
||||||
|
this.brands = toArray(uach[CH_HEADER]);
|
||||||
|
this.fullVersionList = toArray(uach[CH_HEADER_FULL_VER_LIST]);
|
||||||
|
this.mobile = /\?1/.test(uach[CH_HEADER_MOBILE]);
|
||||||
var setHeader = function (header) {
|
var setHeader = function (header) {
|
||||||
return header ? strip(/\"/g, header) : undefined;
|
return header ? strip(/\"/g, header) : undefined;
|
||||||
};
|
};
|
||||||
@ -834,25 +846,32 @@
|
|||||||
this.platformVersion = setHeader(uach[CH_HEADER_PLATFORM_VER]);
|
this.platformVersion = setHeader(uach[CH_HEADER_PLATFORM_VER]);
|
||||||
this.architecture = setHeader(uach[CH_HEADER_ARCH]);
|
this.architecture = setHeader(uach[CH_HEADER_ARCH]);
|
||||||
this.bitness = setHeader(uach[CH_HEADER_BITNESS]);
|
this.bitness = setHeader(uach[CH_HEADER_BITNESS]);
|
||||||
|
} else {
|
||||||
|
for (var prop in uach) {
|
||||||
|
this[prop] = uach[prop];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
function UAItem (data) {
|
function UAParserItem (data) {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
this.ua = data[0];
|
this.ua = data[0];
|
||||||
this.rgxMap = data[1];
|
this.uaCH = data[1];
|
||||||
this.uaCH = data[2];
|
this.rgxMap = data[3];
|
||||||
this.data = (function (data) {
|
this.data = (function (data) {
|
||||||
var init_props = data[3],
|
var ua = data[0],
|
||||||
is_ignoreProps = data[4],
|
itemType = data[2],
|
||||||
is_ignoreRgx = data[5],
|
rgxMap = data[3],
|
||||||
toString_props = data[6];
|
init_props = data[4],
|
||||||
|
is_ignoreProps = data[5],
|
||||||
|
is_ignoreRgx = data[6],
|
||||||
|
toString_props = data[7];
|
||||||
|
|
||||||
function UAData () {
|
function UAParserData () {
|
||||||
initialize.call(this, init_props);
|
initialize.call(this, init_props);
|
||||||
}
|
}
|
||||||
UAData.prototype.is = function (strToCheck) {
|
UAParserData.prototype.is = function (strToCheck) {
|
||||||
var is = false;
|
var is = false;
|
||||||
for (var i in this) {
|
for (var i in this) {
|
||||||
if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
|
if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
|
||||||
@ -865,7 +884,7 @@
|
|||||||
}
|
}
|
||||||
return is;
|
return is;
|
||||||
};
|
};
|
||||||
UAData.prototype.toString = function () {
|
UAParserData.prototype.toString = function () {
|
||||||
var str = EMPTY;
|
var str = EMPTY;
|
||||||
for (var i in toString_props) {
|
for (var i in toString_props) {
|
||||||
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
|
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
|
||||||
@ -874,36 +893,83 @@
|
|||||||
}
|
}
|
||||||
return str ? str : UNDEF_TYPE;
|
return str ? str : UNDEF_TYPE;
|
||||||
};
|
};
|
||||||
return new UAData();
|
UAParserData.prototype.withClientHints = function () {
|
||||||
|
if (!NAVIGATOR_UADATA) return;
|
||||||
|
return NAVIGATOR_UADATA
|
||||||
|
.getHighEntropyValues(CH_ALL_VALUES)
|
||||||
|
.then(function (res) {
|
||||||
|
|
||||||
|
var JS_UACH = new UAParserDataCH(res, false),
|
||||||
|
browser = new UAParserBrowser(ua, rgxMap, JS_UACH).get(),
|
||||||
|
cpu = new UAParserCPU(ua, rgxMap, JS_UACH).get(),
|
||||||
|
device = new UAParserDevice(ua, rgxMap, JS_UACH).get(),
|
||||||
|
engine = new UAParserEngine(ua, rgxMap).get(),
|
||||||
|
os = new UAParserOS(ua, rgxMap, JS_UACH).get();
|
||||||
|
|
||||||
|
switch (itemType) {
|
||||||
|
case 'browser':
|
||||||
|
return browser;
|
||||||
|
case 'cpu':
|
||||||
|
return cpu;
|
||||||
|
case 'device':
|
||||||
|
return device;
|
||||||
|
case 'engine':
|
||||||
|
return engine;
|
||||||
|
case 'os':
|
||||||
|
return os;
|
||||||
|
default :
|
||||||
|
return {
|
||||||
|
'ua' : ua,
|
||||||
|
'ua_ch' : JS_UACH,
|
||||||
|
'browser' : browser,
|
||||||
|
'cpu' : cpu,
|
||||||
|
'device' : device,
|
||||||
|
'engine' : engine,
|
||||||
|
'os' : os
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return new UAParserData();
|
||||||
})(data);
|
})(data);
|
||||||
}
|
}
|
||||||
UAItem.prototype.get = function (prop) {
|
UAParserItem.prototype.get = function (prop) {
|
||||||
if (!prop) return this.data;
|
if (!prop) return this.data;
|
||||||
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
|
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined;
|
||||||
};
|
};
|
||||||
UAItem.prototype.parse = function () {
|
UAParserItem.prototype.parse = function () {
|
||||||
rgxMapper.call(this.data, this.ua, this.rgxMap);
|
rgxMapper.call(this.data, this.ua, this.rgxMap);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
UAItem.prototype.set = function (prop, val) {
|
UAParserItem.prototype.set = function (prop, val) {
|
||||||
this.data[prop] = val;
|
this.data[prop] = val;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
function UABrowser (ua, browserMap, uach) {
|
function UAParserBrowser (ua, browserMap, uach) {
|
||||||
UAItem.call(this, [
|
UAParserItem.call(this, [
|
||||||
ua,
|
ua,
|
||||||
browserMap,
|
|
||||||
uach,
|
uach,
|
||||||
|
'browser',
|
||||||
|
browserMap,
|
||||||
[NAME, VERSION, MAJOR],
|
[NAME, VERSION, MAJOR],
|
||||||
[VERSION, MAJOR],
|
[VERSION, MAJOR],
|
||||||
' ?browser$',
|
' ?browser$',
|
||||||
[NAME, VERSION]
|
[NAME, VERSION]
|
||||||
]);
|
]);
|
||||||
|
this.parseCH();
|
||||||
|
if (!this.get(NAME)) {
|
||||||
|
this.parse();
|
||||||
|
// Brave-specific detection
|
||||||
|
if (NAVIGATOR && NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
|
||||||
|
this.set(NAME, 'Brave');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.set(MAJOR, majorize(this.get(VERSION)));
|
||||||
}
|
}
|
||||||
UABrowser.prototype = new UAItem();
|
UAParserBrowser.prototype = new UAParserItem();
|
||||||
UABrowser.prototype.parseCH = function () {
|
UAParserBrowser.prototype.parseCH = function () {
|
||||||
var brands = this.uaCH.brands;
|
var brands = this.uaCH.fullVersionList || this.uaCH.brands;
|
||||||
if (brands) {
|
if (brands) {
|
||||||
for (var i in brands) {
|
for (var i in brands) {
|
||||||
var brandName = brands[i].brand,
|
var brandName = brands[i].brand,
|
||||||
@ -918,38 +984,58 @@
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
function UACPU (ua, cpuMap, uach) {
|
function UAParserCPU (ua, cpuMap, uach) {
|
||||||
UAItem.call(this, [
|
UAParserItem.call(this, [
|
||||||
ua,
|
ua,
|
||||||
cpuMap,
|
|
||||||
uach,
|
uach,
|
||||||
|
'cpu',
|
||||||
|
cpuMap,
|
||||||
[ARCHITECTURE],
|
[ARCHITECTURE],
|
||||||
[],
|
[],
|
||||||
null,
|
null,
|
||||||
[ARCHITECTURE]
|
[ARCHITECTURE]
|
||||||
]);
|
]);
|
||||||
|
this.parseCH();
|
||||||
|
if (!this.get(ARCHITECTURE)) {
|
||||||
|
this.parse();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
UACPU.prototype = new UAItem();
|
UAParserCPU.prototype = new UAParserItem();
|
||||||
UACPU.prototype.parseCH = function () {
|
UAParserCPU.prototype.parseCH = function () {
|
||||||
var archName = this.uaCH.architecture;
|
var archName = this.uaCH.architecture;
|
||||||
archName += (archName && this.uaCH.bitness == '64') ? '64' : EMPTY;
|
if (archName) {
|
||||||
rgxMapper.call(this.data, archName, this.rgxMap);
|
archName += (archName && this.uaCH.bitness == '64') ? '64' : EMPTY;
|
||||||
|
rgxMapper.call(this.data, archName, this.rgxMap);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
function UADevice (ua, deviceMap, uach) {
|
function UAParserDevice (ua, deviceMap, uach) {
|
||||||
UAItem.call(this, [
|
UAParserItem.call(this, [
|
||||||
ua,
|
ua,
|
||||||
deviceMap,
|
|
||||||
uach,
|
uach,
|
||||||
|
'device',
|
||||||
|
deviceMap,
|
||||||
[TYPE, MODEL, VENDOR],
|
[TYPE, MODEL, VENDOR],
|
||||||
[],
|
[],
|
||||||
null,
|
null,
|
||||||
[VENDOR, MODEL]
|
[VENDOR, MODEL]
|
||||||
]);
|
]);
|
||||||
|
this.parseCH();
|
||||||
|
if (!this.get(TYPE) || !this.get(MODEL)) {
|
||||||
|
this.parse();
|
||||||
|
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(NAME) == 'Macintosh' && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
|
||||||
|
this.set(MODEL, 'iPad')
|
||||||
|
.set(TYPE, TABLET);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
UADevice.prototype = new UAItem();
|
UAParserDevice.prototype = new UAParserItem();
|
||||||
UADevice.prototype.parseCH = function () {
|
UAParserDevice.prototype.parseCH = function () {
|
||||||
if (this.uaCH.mobile) {
|
if (this.uaCH.mobile) {
|
||||||
this.set(TYPE, MOBILE);
|
this.set(TYPE, MOBILE);
|
||||||
}
|
}
|
||||||
@ -959,40 +1045,58 @@
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
function UAEngine (ua, engineMap) {
|
function UAParserEngine (ua, engineMap) {
|
||||||
UAItem.call(this, [
|
UAParserItem.call(this, [
|
||||||
ua,
|
ua,
|
||||||
engineMap,
|
|
||||||
null,
|
null,
|
||||||
|
'engine',
|
||||||
|
engineMap,
|
||||||
[NAME, VERSION],
|
[NAME, VERSION],
|
||||||
[VERSION],
|
[VERSION],
|
||||||
null,
|
null,
|
||||||
[NAME, VERSION]
|
[NAME, VERSION]
|
||||||
]);
|
]);
|
||||||
|
this.parse();
|
||||||
}
|
}
|
||||||
UAEngine.prototype = new UAItem();
|
UAParserEngine.prototype = new UAParserItem();
|
||||||
|
|
||||||
function UAOS (ua, osMap, uach) {
|
function UAParserOS (ua, osMap, uach) {
|
||||||
UAItem.call(this, [
|
UAParserItem.call(this, [
|
||||||
ua,
|
ua,
|
||||||
osMap,
|
|
||||||
uach,
|
uach,
|
||||||
|
'os',
|
||||||
|
osMap,
|
||||||
[NAME, VERSION],
|
[NAME, VERSION],
|
||||||
[VERSION],
|
[VERSION],
|
||||||
' ?os$',
|
' ?os$',
|
||||||
[NAME, VERSION]
|
[NAME, VERSION]
|
||||||
]);
|
]);
|
||||||
|
this.parseCH();
|
||||||
|
if (!this.get(NAME)) {
|
||||||
|
this.parse();
|
||||||
|
if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA.platform != 'Unknown') {
|
||||||
|
this.set(NAME, NAVIGATOR_UADATA.platform
|
||||||
|
.replace(/chrome os/i, CHROMIUM_OS)
|
||||||
|
.replace(/macos/i, MAC_OS)); // backward compatibility
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
UAOS.prototype = new UAItem();
|
UAParserOS.prototype = new UAParserItem();
|
||||||
UAOS.prototype.parseCH = function (uach) {
|
UAParserOS.prototype.parseCH = function () {
|
||||||
var osName = this.uaCH.platform;
|
var osName = this.uaCH.platform;
|
||||||
var osVersion = this.uaCH.platformVersion;
|
if(osName) {
|
||||||
osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion;
|
var osVersion = this.uaCH.platformVersion;
|
||||||
this.set(NAME, osName)
|
osVersion = (osName == WINDOWS) ? (parseInt(majorize(osVersion), 10) >= 13 ? '11' : '10') : osVersion;
|
||||||
.set(VERSION, osVersion);
|
this.set(NAME, osName)
|
||||||
|
.set(VERSION, osVersion);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function UAParserResult (ua, uach) {
|
||||||
|
UAParserItem.call(this, [ua, uach]);
|
||||||
|
}
|
||||||
|
UAParserResult.prototype = new UAParserItem();
|
||||||
|
|
||||||
function UAParser (ua, extensions, headers) {
|
function UAParser (ua, extensions, headers) {
|
||||||
|
|
||||||
@ -1016,22 +1120,14 @@
|
|||||||
return new UAParser(ua, extensions, headers).getResult();
|
return new UAParser(ua, extensions, headers).getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
var navigator = (typeof window !== UNDEF_TYPE && window.navigator) ?
|
var userAgent = ua ||
|
||||||
window.navigator :
|
((NAVIGATOR && NAVIGATOR.userAgent) ?
|
||||||
undefined,
|
NAVIGATOR.userAgent :
|
||||||
|
|
||||||
userAgent = ua ||
|
|
||||||
((navigator && navigator.userAgent) ?
|
|
||||||
navigator.userAgent :
|
|
||||||
(headers && headers[USER_AGENT] ?
|
(headers && headers[USER_AGENT] ?
|
||||||
headers[USER_AGENT] :
|
headers[USER_AGENT] :
|
||||||
EMPTY)),
|
EMPTY)),
|
||||||
|
|
||||||
clientHints = new UACHData(headers, false),
|
HTTP_UACH = new UAParserDataCH(headers, true),
|
||||||
|
|
||||||
userAgentData = (navigator && navigator.userAgentData) ?
|
|
||||||
navigator.userAgentData :
|
|
||||||
undefined,
|
|
||||||
|
|
||||||
regexMap = extensions ?
|
regexMap = extensions ?
|
||||||
extend(regexes, extensions) :
|
extend(regexes, extensions) :
|
||||||
@ -1039,85 +1135,35 @@
|
|||||||
|
|
||||||
// public methods
|
// public methods
|
||||||
this.getBrowser = function () {
|
this.getBrowser = function () {
|
||||||
var browser = new UABrowser(userAgent, regexMap.browser, clientHints);
|
return new UAParserBrowser(userAgent, regexMap.browser, HTTP_UACH).get();
|
||||||
if (headers && (headers[CH_HEADER_FULL_VER_LIST] || headers[CH_HEADER])) {
|
|
||||||
browser.parseCH();
|
|
||||||
}
|
|
||||||
if (!browser.get(NAME)) {
|
|
||||||
browser.parse();
|
|
||||||
// Brave-specific detection
|
|
||||||
if (navigator && navigator.brave && typeof navigator.brave.isBrave == FUNC_TYPE) {
|
|
||||||
browser.set(NAME, 'Brave');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return browser
|
|
||||||
.set(MAJOR, majorize(browser.get(VERSION)))
|
|
||||||
.get();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getCPU = function () {
|
this.getCPU = function () {
|
||||||
var cpu = new UACPU(userAgent, regexMap.cpu, clientHints);
|
return new UAParserCPU(userAgent, regexMap.cpu, HTTP_UACH).get();
|
||||||
if (headers && headers[CH_HEADER_ARCH]) {
|
|
||||||
cpu.parseCH();
|
|
||||||
}
|
|
||||||
if (!cpu.get(ARCHITECTURE)) {
|
|
||||||
cpu.parse();
|
|
||||||
}
|
|
||||||
return cpu.get();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getDevice = function () {
|
this.getDevice = function () {
|
||||||
var device = new UADevice(userAgent, regexMap.device, clientHints);
|
return new UAParserDevice(userAgent, regexMap.device, HTTP_UACH).get();
|
||||||
if (headers && (headers[CH_HEADER_MOBILE] || headers[CH_HEADER_MODEL])) {
|
|
||||||
device.parseCH();
|
|
||||||
}
|
|
||||||
if (!device.get(TYPE) || !device.get(MODEL)) {
|
|
||||||
device.parse();
|
|
||||||
if (!device.get(TYPE) && userAgentData && userAgentData.mobile) {
|
|
||||||
device.set(TYPE, MOBILE);
|
|
||||||
}
|
|
||||||
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
|
|
||||||
if (device.get(NAME) == 'Macintosh' && navigator && typeof navigator.standalone !== UNDEF_TYPE && navigator.maxTouchPoints && navigator.maxTouchPoints > 2) {
|
|
||||||
device
|
|
||||||
.set(MODEL, 'iPad')
|
|
||||||
.set(TYPE, TABLET);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return device.get();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getEngine = function () {
|
this.getEngine = function () {
|
||||||
return new UAEngine(userAgent, regexMap.engine)
|
return new UAParserEngine(userAgent, regexMap.engine).get();
|
||||||
.parse()
|
|
||||||
.get();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getOS = function () {
|
this.getOS = function () {
|
||||||
var os = new UAOS(userAgent, regexMap.os, clientHints);
|
return new UAParserOS(userAgent, regexMap.os, HTTP_UACH).get();
|
||||||
if (headers && headers[CH_HEADER_PLATFORM]) {
|
|
||||||
os.parseCH();
|
|
||||||
}
|
|
||||||
if (!os.get(NAME)) {
|
|
||||||
os.parse();
|
|
||||||
if (!os.get(NAME) && userAgentData && userAgentData.platform != 'Unknown') {
|
|
||||||
os.set(NAME, userAgentData.platform
|
|
||||||
.replace(/chrome os/i, CHROMIUM_OS)
|
|
||||||
.replace(/macos/i, MAC_OS)); // backward compatibility
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return os.get();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getResult = function () {
|
this.getResult = function () {
|
||||||
return {
|
return new UAParserResult(userAgent, HTTP_UACH)
|
||||||
'ua' : userAgent,
|
.set('ua', userAgent)
|
||||||
'ua_ch' : clientHints,
|
.set('ua_ch', HTTP_UACH)
|
||||||
'browser' : this.getBrowser(),
|
.set('browser', this.getBrowser())
|
||||||
'cpu' : this.getCPU(),
|
.set('cpu', this.getCPU())
|
||||||
'device' : this.getDevice(),
|
.set('device', this.getDevice())
|
||||||
'engine' : this.getEngine(),
|
.set('engine', this.getEngine())
|
||||||
'os' : this.getOS()
|
.set('os', this.getOS())
|
||||||
};
|
.get();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getUA = function () {
|
this.getUA = function () {
|
||||||
|
10
test/test.js
10
test/test.js
@ -82,7 +82,7 @@ describe('Returns', function () {
|
|||||||
assert.deepEqual(new UAParser('').getResult(),
|
assert.deepEqual(new UAParser('').getResult(),
|
||||||
{
|
{
|
||||||
ua : '',
|
ua : '',
|
||||||
ua_ch : { architecture: undefined, bitness: undefined, brands: undefined, mobile: false, model: undefined, platform: undefined, platformVersion: undefined },
|
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 },
|
browser: { name: undefined, version: undefined, major: undefined },
|
||||||
cpu: { architecture: undefined },
|
cpu: { architecture: undefined },
|
||||||
device: { vendor: undefined, model: undefined, type: undefined },
|
device: { vendor: undefined, model: undefined, type: undefined },
|
||||||
@ -401,3 +401,11 @@ describe('Map UA-CH headers', function () {
|
|||||||
assert.strictEqual(os.version, "x86_64");
|
assert.strictEqual(os.version, "x86_64");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Map UA-CH JS', function () {
|
||||||
|
it('Can read client hints from browser', async function () {
|
||||||
|
let ua = new UAParser();
|
||||||
|
let browser = await ua.getBrowser().withClientHints();
|
||||||
|
// TODO : create tests
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user