mirror of
https://github.com/faisalman/ua-parser-js.git
synced 2025-09-28 00:18:45 +03:00
This commit is contained in:
parent
d99ff741f4
commit
3c3c03ceeb
37
readme.md
37
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 with relatively small footprint (~17KB minified, ~6KB gzipped) 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 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
|
||||||
@ -26,24 +26,20 @@ JavaScript library to detect Browser, Engine, OS, CPU, and Device type/model fro
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center" width="200px" rowspan="3"><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="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>
|
<td align="left" width="400px"><a href="https://www.npmjs.com/package/@51degrees/ua-parser-js">↗ @51degrees/ua-parser-js</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<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><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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>On 6 March, we’ll be hosting a demonstration of the 51Degrees UAParser. Register your place for the webinar <a href="https://event.webinarjam.com/register/36/6k2gqu5p">↗ here</a>.</p></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
### UAParser([user-agent:string][,extensions:object])
|
### UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])
|
||||||
|
|
||||||
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.
|
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:
|
Usually you can find the user agent in:
|
||||||
@ -55,7 +51,7 @@ When you call `UAParser` with the `new` keyword `UAParser` will return a new ins
|
|||||||
Like so:
|
Like so:
|
||||||
* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])`
|
* `new UAParser([user-agent:string][,extensions:object][,headers:object(since@1.1)])`
|
||||||
```js
|
```js
|
||||||
let parser = new UAParser("user-agent"); // you need to pass the user-agent for nodejs
|
let parser = new UAParser("your user-agent here"); // you need to pass the user-agent for nodejs
|
||||||
console.log(parser); // {}
|
console.log(parser); // {}
|
||||||
let parserResults = parser.getResult();
|
let parserResults = parser.getResult();
|
||||||
console.log(parserResults);
|
console.log(parserResults);
|
||||||
@ -297,24 +293,30 @@ If you want to detect something that's not currently provided by UAParser.js (eg
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Example:
|
// Example:
|
||||||
let myOwnListOfBrowsers = [
|
const myOwnListOfBrowsers = [
|
||||||
[/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']]
|
[/(mybrowser)\/([\w\.]+)/i], [UAParser.BROWSER.NAME, UAParser.BROWSER.VERSION, ['type', 'bot']]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const myUA = 'Mozilla/5.0 MyBrowser/1.3';
|
||||||
|
|
||||||
let myParser = new UAParser({ browser: myOwnListOfBrowsers });
|
let myParser = new UAParser({ browser: myOwnListOfBrowsers });
|
||||||
let myUA = 'Mozilla/5.0 MyBrowser/1.3';
|
|
||||||
console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3", major: "1", type : "bot"}
|
console.log(myParser.setUA(myUA).getBrowser()); // {name: "MyBrowser", version: "1.3", major: "1", type : "bot"}
|
||||||
console.log(myParser.getBrowser().is('bot')); // true
|
console.log(myParser.getBrowser().is('bot')); // true
|
||||||
|
|
||||||
// Another example:
|
// Another example:
|
||||||
let myOwnListOfDevices = [
|
const myOwnListOfDevices = [
|
||||||
[/(mytab) ([\w ]+)/i], [UAParser.DEVICE.VENDOR, UAParser.DEVICE.MODEL, [UAParser.DEVICE.TYPE, UAParser.DEVICE.TABLET]],
|
[/(mytab) ([\w ]+)/i], [UAParser.DEVICE.VENDOR, UAParser.DEVICE.MODEL, [UAParser.DEVICE.TYPE, UAParser.DEVICE.TABLET]],
|
||||||
[/(myphone)/i], [UAParser.DEVICE.VENDOR, [UAParser.DEVICE.TYPE, UAParser.DEVICE.MOBILE]]
|
[/(myphone)/i], [UAParser.DEVICE.VENDOR, [UAParser.DEVICE.TYPE, UAParser.DEVICE.MOBILE]]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max';
|
||||||
|
|
||||||
let myParser2 = new UAParser({
|
let myParser2 = new UAParser({
|
||||||
browser: myOwnListOfBrowsers,
|
browser: myOwnListOfBrowsers,
|
||||||
device: myOwnListOfDevices
|
device: myOwnListOfDevices
|
||||||
});
|
});
|
||||||
let myUA2 = 'Mozilla/5.0 MyTab 14 Pro Max';
|
|
||||||
console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}
|
console.log(myParser2.setUA(myUA2).getDevice()); // {vendor: "MyTab", model: "14 Pro Max", type: "tablet"}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -408,6 +410,17 @@ var uap = require('ua-parser-js');
|
|||||||
http.createServer(function (req, res) {
|
http.createServer(function (req, res) {
|
||||||
// get user-agent header
|
// get user-agent header
|
||||||
var ua = uap(req.headers['user-agent']);
|
var ua = uap(req.headers['user-agent']);
|
||||||
|
|
||||||
|
/* // BEGIN since@1.1 - you can also pass client-hints data to UAParser
|
||||||
|
|
||||||
|
var getHighEntropyValues = 'Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version, Sec-CH-UA-Arch, Sec-CH-UA-Bitness';
|
||||||
|
res.setHeader('Accept-CH', getHighEntropyValues);
|
||||||
|
res.setHeader('Critical-CH', getHighEntropyValues);
|
||||||
|
|
||||||
|
var ua = uap(req.headers);
|
||||||
|
|
||||||
|
// END since@1.1 */
|
||||||
|
|
||||||
// write the result as response
|
// write the result as response
|
||||||
res.end(JSON.stringify(ua, null, ' '));
|
res.end(JSON.stringify(ua, null, ' '));
|
||||||
})
|
})
|
||||||
|
382
src/ua-parser.js
382
src/ua-parser.js
@ -38,7 +38,15 @@
|
|||||||
WEARABLE = 'wearable',
|
WEARABLE = 'wearable',
|
||||||
EMBEDDED = 'embedded',
|
EMBEDDED = 'embedded',
|
||||||
USER_AGENT = 'user-agent',
|
USER_AGENT = 'user-agent',
|
||||||
UA_MAX_LENGTH = 350;
|
UA_MAX_LENGTH = 350,
|
||||||
|
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_PLATFORM_VER = CH_HEADER_PLATFORM + '-version';
|
||||||
|
|
||||||
var AMAZON = 'Amazon',
|
var AMAZON = 'Amazon',
|
||||||
APPLE = 'Apple',
|
APPLE = 'Apple',
|
||||||
@ -63,7 +71,8 @@
|
|||||||
ZTE = 'ZTE',
|
ZTE = 'ZTE',
|
||||||
FACEBOOK = 'Facebook',
|
FACEBOOK = 'Facebook',
|
||||||
CHROMIUM_OS = 'Chromium OS',
|
CHROMIUM_OS = 'Chromium OS',
|
||||||
MAC_OS = 'Mac OS';
|
MAC_OS = 'Mac OS',
|
||||||
|
WINDOWS = 'Windows';
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
// Helper
|
// Helper
|
||||||
@ -110,6 +119,8 @@
|
|||||||
|
|
||||||
var rgxMapper = function (ua, arrays) {
|
var rgxMapper = function (ua, arrays) {
|
||||||
|
|
||||||
|
if(!ua || !arrays) return;
|
||||||
|
|
||||||
var i = 0, j, k, p, q, matches, match;
|
var i = 0, j, k, p, q, matches, match;
|
||||||
|
|
||||||
// loop through all regexes maps
|
// loop through all regexes maps
|
||||||
@ -718,7 +729,7 @@
|
|||||||
/(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i
|
/(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i
|
||||||
], [NAME, [VERSION, strMapper, windowsVersionMap]], [
|
], [NAME, [VERSION, strMapper, windowsVersionMap]], [
|
||||||
/(win(?=3|9|n)|win 9x )([nt\d\.]+)/i
|
/(win(?=3|9|n)|win 9x )([nt\d\.]+)/i
|
||||||
], [[NAME, 'Windows'], [VERSION, strMapper, windowsVersionMap]], [
|
], [[NAME, WINDOWS], [VERSION, strMapper, windowsVersionMap]], [
|
||||||
|
|
||||||
// iOS/macOS
|
// iOS/macOS
|
||||||
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
|
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
|
||||||
@ -788,115 +799,174 @@
|
|||||||
// Constructor
|
// Constructor
|
||||||
////////////////
|
////////////////
|
||||||
|
|
||||||
function UAItem () {}
|
function UAItem (data) {
|
||||||
|
if (!data) return;
|
||||||
|
this.ua = data[0];
|
||||||
|
this.rgxMap = data[1];
|
||||||
|
this.data = (function (data) {
|
||||||
|
var is_ignoreProps = data[3],
|
||||||
|
is_ignoreRgx = data[4],
|
||||||
|
toString_props = data[5];
|
||||||
|
|
||||||
|
var UAData = function () {
|
||||||
|
for (var init_props in data[2]) {
|
||||||
|
this[data[2][init_props]] = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
UAData.prototype.is = function (strToCheck) {
|
||||||
|
var is = false;
|
||||||
|
for (var i in this) {
|
||||||
|
if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
|
||||||
|
is = true;
|
||||||
|
if (strToCheck != UNDEF_TYPE) break;
|
||||||
|
} else if (strToCheck == UNDEF_TYPE && is) {
|
||||||
|
is = !is;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return is;
|
||||||
|
};
|
||||||
|
UAData.prototype.toString = function () {
|
||||||
|
var str = EMPTY;
|
||||||
|
for (var i in toString_props) {
|
||||||
|
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
|
||||||
|
str += (str ? ' ' : EMPTY) + this[toString_props[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str ? str : UNDEF_TYPE;
|
||||||
|
};
|
||||||
|
return new UAData();
|
||||||
|
})(data);
|
||||||
|
}
|
||||||
UAItem.prototype.get = function (prop) {
|
UAItem.prototype.get = function (prop) {
|
||||||
if (!prop) {
|
if (!prop) return this.data;
|
||||||
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 (ua, rgxmap) {
|
UAItem.prototype.parse = function () {
|
||||||
rgxMapper.call(this.data, ua, rgxmap);
|
rgxMapper.call(this.data, this.ua, this.rgxMap);
|
||||||
|
return this;
|
||||||
};
|
};
|
||||||
UAItem.prototype.set = function (prop, val) {
|
UAItem.prototype.set = function (prop, val) {
|
||||||
this.data[prop] = val;
|
this.data[prop] = val;
|
||||||
};
|
return this;
|
||||||
UAItem.prototype.then = function (callback) {
|
|
||||||
return callback(this.data);
|
|
||||||
};
|
|
||||||
UAItem.createUAData = function (data) {
|
|
||||||
var is_ignoreProps = data.is_ignoreProps,
|
|
||||||
is_ignoreRgx = data.is_ignoreRgx,
|
|
||||||
toString_props = data.toString_props;
|
|
||||||
|
|
||||||
var UAData = function () {
|
|
||||||
for (var i in data.init_props) {
|
|
||||||
this[data.init_props[i]] = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
UAData.prototype.is = function (strToCheck) {
|
|
||||||
var is = false;
|
|
||||||
for (var i in this) {
|
|
||||||
if (this.hasOwnProperty(i) && !is_ignoreProps[i] && lowerize(this[i], is_ignoreRgx) === lowerize(strToCheck, is_ignoreRgx)) {
|
|
||||||
is = true;
|
|
||||||
if (strToCheck != UNDEF_TYPE) break;
|
|
||||||
} else if (strToCheck == UNDEF_TYPE && is) {
|
|
||||||
is = !is;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return is;
|
|
||||||
};
|
|
||||||
UAData.prototype.toString = function () {
|
|
||||||
var str = EMPTY;
|
|
||||||
for (var i in toString_props) {
|
|
||||||
if (typeof(this[toString_props[i]]) !== UNDEF_TYPE) {
|
|
||||||
str += (str ? ' ' : EMPTY) + this[toString_props[i]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return str ? str : UNDEF_TYPE;
|
|
||||||
};
|
|
||||||
return new UAData();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function UABrowser () {
|
function UABrowser (ua, browserMap) {
|
||||||
this.data = UAItem.createUAData({
|
UAItem.call(this, [
|
||||||
init_props : [NAME, VERSION, MAJOR],
|
ua,
|
||||||
is_ignoreProps : [VERSION, MAJOR],
|
browserMap,
|
||||||
is_ignoreRgx : ' ?browser$',
|
[NAME, VERSION, MAJOR],
|
||||||
toString_props : [NAME, VERSION]
|
[VERSION, MAJOR],
|
||||||
});
|
' ?browser$',
|
||||||
|
[NAME, VERSION]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
UABrowser.prototype = new UAItem();
|
UABrowser.prototype = new UAItem();
|
||||||
|
UABrowser.prototype.parse = function (uach) {
|
||||||
|
if (uach) {
|
||||||
|
var brands = uach[CH_HEADER_FULL_VER_LIST] || uach[CH_HEADER];
|
||||||
|
if (brands) {
|
||||||
|
brands = brands.replace(/\\?\"/g, EMPTY).split(', ');
|
||||||
|
for (var i in brands) {
|
||||||
|
var brand = brands[i].split(';v='),
|
||||||
|
brandName = brand[0],
|
||||||
|
brandVersion = brand[1];
|
||||||
|
if (!/not.a.brand/i.test(brandName) && (!this.get(NAME) || /chromi/i.test(this.get(NAME)))) {
|
||||||
|
this.set(NAME, brandName.replace(GOOGLE+' ', EMPTY))
|
||||||
|
.set(VERSION, brandVersion)
|
||||||
|
.set(MAJOR, majorize(brandVersion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.get(NAME)) UAItem.prototype.parse.call(this);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
function UACPU () {
|
function UACPU (ua, cpuMap) {
|
||||||
this.data = UAItem.createUAData({
|
UAItem.call(this, [
|
||||||
init_props : [ARCHITECTURE],
|
ua,
|
||||||
is_ignoreProps : [],
|
cpuMap,
|
||||||
toString_props : [ARCHITECTURE]
|
[ARCHITECTURE],
|
||||||
});
|
[],
|
||||||
|
null,
|
||||||
|
[ARCHITECTURE]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
UACPU.prototype = new UAItem();
|
UACPU.prototype = new UAItem();
|
||||||
|
UACPU.prototype.parse = function (uach) {
|
||||||
|
if (uach) {
|
||||||
|
var archName = uach[CH_HEADER_ARCH];
|
||||||
|
archName += (archName && uach[CH_HEADER_BITNESS] == '64') ? '64' : EMPTY;
|
||||||
|
rgxMapper.call(this.data, archName, this.rgxMap);
|
||||||
|
}
|
||||||
|
if (!this.get(ARCHITECTURE)) UAItem.prototype.parse.call(this);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
function UADevice () {
|
function UADevice (ua, deviceMap) {
|
||||||
this.data = UAItem.createUAData({
|
UAItem.call(this, [
|
||||||
init_props : [TYPE, MODEL, VENDOR],
|
ua,
|
||||||
is_ignoreProps : [],
|
deviceMap,
|
||||||
toString_props : [VENDOR, MODEL]
|
[TYPE, MODEL, VENDOR],
|
||||||
});
|
[],
|
||||||
|
null,
|
||||||
|
[VENDOR, MODEL]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
UADevice.prototype = new UAItem();
|
UADevice.prototype = new UAItem();
|
||||||
|
UADevice.prototype.parse = function (uach) {
|
||||||
|
if (uach) {
|
||||||
|
this.set(TYPE, uach[CH_HEADER_MOBILE] == '?1' ?
|
||||||
|
MOBILE :
|
||||||
|
this.get(TYPE))
|
||||||
|
.set(MODEL, uach[CH_HEADER_MODEL] ?
|
||||||
|
uach[CH_HEADER_MODEL].replace(/\"/g, EMPTY) :
|
||||||
|
this.get(MODEL));
|
||||||
|
}
|
||||||
|
if (!this.get(TYPE) && !this.get(MODEL)) UAItem.prototype.parse.call(this);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
function UAEngine () {
|
function UAEngine (ua, engineMap) {
|
||||||
this.data = UAItem.createUAData({
|
UAItem.call(this, [
|
||||||
init_props : [NAME, VERSION],
|
ua,
|
||||||
is_ignoreProps : [VERSION],
|
engineMap,
|
||||||
toString_props : [NAME, VERSION]
|
[NAME, VERSION],
|
||||||
});
|
[VERSION],
|
||||||
|
null,
|
||||||
|
[NAME, VERSION]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
UAEngine.prototype = new UAItem();
|
UAEngine.prototype = new UAItem();
|
||||||
|
|
||||||
function UAOS () {
|
function UAOS (ua, osMap) {
|
||||||
this.data = UAItem.createUAData({
|
UAItem.call(this, [
|
||||||
init_props : [NAME, VERSION],
|
ua,
|
||||||
is_ignoreProps : [VERSION],
|
osMap,
|
||||||
is_ignoreRgx : ' ?os$',
|
[NAME, VERSION],
|
||||||
toString_props : [NAME, VERSION]
|
[VERSION],
|
||||||
});
|
' ?os$',
|
||||||
|
[NAME, VERSION]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
UAOS.prototype = new UAItem();
|
UAOS.prototype = new UAItem();
|
||||||
|
UAOS.prototype.parse = function (uach) {
|
||||||
function UAResult () {
|
if (uach) {
|
||||||
this.data = {
|
var osName = uach[CH_HEADER_PLATFORM] ? uach[CH_HEADER_PLATFORM].replace(/\"/g, EMPTY) : undefined;
|
||||||
ua : '',
|
var osVersion = uach[CH_HEADER_PLATFORM_VER] ? uach[CH_HEADER_PLATFORM_VER].replace(/\"/g, EMPTY) : undefined;
|
||||||
browser : undefined,
|
osVersion = (osName == WINDOWS) ? (majorize(osVersion) >= 13 ? '11' : '10') : osVersion;
|
||||||
cpu : undefined,
|
this.set(NAME, osName)
|
||||||
device : undefined,
|
.set(VERSION, osVersion);
|
||||||
engine : undefined,
|
}
|
||||||
os : undefined
|
if (!this.get(NAME)) UAItem.prototype.parse.call(this);
|
||||||
};
|
if (/(chrome |mac)os/.test(this.get(NAME))) {
|
||||||
}
|
this.set(NAME, this.get(NAME)
|
||||||
UAResult.prototype = new UAItem();
|
.replace(/chrome os/i, CHROMIUM_OS)
|
||||||
|
.replace(/macos/i, MAC_OS));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
function UAParser (ua, extensions, headers) {
|
function UAParser (ua, extensions, headers) {
|
||||||
|
|
||||||
@ -908,6 +978,7 @@
|
|||||||
extensions = ua; // case UAParser(extensions)
|
extensions = ua; // case UAParser(extensions)
|
||||||
} else {
|
} else {
|
||||||
headers = ua; // case UAParser(headers)
|
headers = ua; // case UAParser(headers)
|
||||||
|
extensions = undefined;
|
||||||
}
|
}
|
||||||
ua = undefined;
|
ua = undefined;
|
||||||
} else if (typeof ua === STR_TYPE && !isExtensions(extensions)) {
|
} else if (typeof ua === STR_TYPE && !isExtensions(extensions)) {
|
||||||
@ -918,75 +989,114 @@
|
|||||||
if (!(this instanceof UAParser)) {
|
if (!(this instanceof UAParser)) {
|
||||||
return new UAParser(ua, extensions, headers).getResult();
|
return new UAParser(ua, extensions, headers).getResult();
|
||||||
}
|
}
|
||||||
var _navigator = (typeof window !== UNDEF_TYPE && window.navigator) ? window.navigator : undefined;
|
|
||||||
|
|
||||||
// _ua = user-supplied string || window.navigator.userAgent || user-agent header || empty
|
var navigator = (typeof window !== UNDEF_TYPE && window.navigator) ?
|
||||||
var _ua = ua || ((_navigator && _navigator.userAgent) ? _navigator.userAgent : (!ua && headers && headers[USER_AGENT] ? headers[USER_AGENT] : EMPTY));
|
window.navigator :
|
||||||
var _uach = (_navigator && _navigator.userAgentData) ? _navigator.userAgentData : undefined;
|
undefined,
|
||||||
var _rgxmap = extensions ? extend(regexes, extensions) : regexes;
|
|
||||||
|
userAgent = ua ||
|
||||||
|
((navigator && navigator.userAgent) ?
|
||||||
|
navigator.userAgent :
|
||||||
|
(headers && headers[USER_AGENT] ?
|
||||||
|
headers[USER_AGENT] :
|
||||||
|
EMPTY)),
|
||||||
|
|
||||||
|
clientHints = (navigator && navigator.userAgentData) ?
|
||||||
|
navigator.userAgentData :
|
||||||
|
undefined,
|
||||||
|
|
||||||
|
regexMap = extensions ?
|
||||||
|
extend(regexes, extensions) :
|
||||||
|
regexes;
|
||||||
|
|
||||||
// public methods
|
// public methods
|
||||||
this.getBrowser = function () {
|
this.getBrowser = function () {
|
||||||
var _browser = new UABrowser();
|
var browser = new UABrowser(userAgent, regexMap.browser);
|
||||||
_browser.parse(_ua, _rgxmap.browser);
|
if (headers && (headers[CH_HEADER_FULL_VER_LIST] || headers[CH_HEADER])) {
|
||||||
_browser.set(MAJOR, majorize(_browser.get(VERSION)));
|
browser.parse(headers);
|
||||||
// Brave-specific detection
|
} else {
|
||||||
if (_navigator && _navigator.brave && typeof _navigator.brave.isBrave == FUNC_TYPE) {
|
browser.parse();
|
||||||
_browser.set(NAME, 'Brave');
|
// Brave-specific detection
|
||||||
|
if (navigator && navigator.brave && typeof navigator.brave.isBrave == FUNC_TYPE) {
|
||||||
|
browser.set(NAME, 'Brave');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return _browser.get();
|
return browser
|
||||||
|
.set(MAJOR, majorize(browser.get(VERSION)))
|
||||||
|
.get();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getCPU = function () {
|
this.getCPU = function () {
|
||||||
var _cpu = new UACPU();
|
var cpu = new UACPU(userAgent, regexMap.cpu);
|
||||||
_cpu.parse(_ua, _rgxmap.cpu);
|
if (headers && headers[CH_HEADER_ARCH]) {
|
||||||
return _cpu.get();
|
cpu.parse(headers);
|
||||||
|
} else {
|
||||||
|
cpu.parse();
|
||||||
|
}
|
||||||
|
return cpu.get();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getDevice = function () {
|
this.getDevice = function () {
|
||||||
var _device = new UADevice();
|
var device = new UADevice(userAgent, regexMap.device);
|
||||||
_device.parse(_ua, _rgxmap.device);
|
if (headers && (headers[CH_HEADER_MOBILE] || headers[CH_HEADER_MODEL])) {
|
||||||
if (!_device.get(TYPE) && _uach && _uach.mobile) {
|
device.parse(headers);
|
||||||
_device.set(TYPE, MOBILE);
|
} else {
|
||||||
|
device.parse();
|
||||||
|
if (!device.get(TYPE) && clientHints && clientHints.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
|
return device.get();
|
||||||
if (_device.get(NAME) == 'Macintosh' && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) {
|
|
||||||
_device.set(MODEL, 'iPad');
|
|
||||||
_device.set(TYPE, TABLET);
|
|
||||||
}
|
|
||||||
return _device.get();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getEngine = function () {
|
this.getEngine = function () {
|
||||||
var _engine = new UAEngine();
|
return new UAEngine(userAgent, regexMap.engine)
|
||||||
_engine.parse(_ua, _rgxmap.engine);
|
.parse()
|
||||||
return _engine.get();
|
.get();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getOS = function () {
|
this.getOS = function () {
|
||||||
var _os = new UAOS();
|
var os = new UAOS(userAgent, regexMap.os);
|
||||||
_os.parse(_ua, _rgxmap.os);
|
if (headers && headers[CH_HEADER_PLATFORM]) {
|
||||||
if (!_os.get(NAME) && _uach && _uach.platform != 'Unknown') {
|
os.parse(headers);
|
||||||
_os.set(NAME, _uach.platform
|
} else {
|
||||||
.replace(/chrome os/i, CHROMIUM_OS)
|
os.parse();
|
||||||
.replace(/macos/i, MAC_OS)); // backward compatibility
|
if (!os.get(NAME) && clientHints && clientHints.platform != 'Unknown') {
|
||||||
|
os.set(NAME, clientHints.platform
|
||||||
|
.replace(/chrome os/i, CHROMIUM_OS)
|
||||||
|
.replace(/macos/i, MAC_OS)); // backward compatibility
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return _os.get();
|
return os.get();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getResult = function () {
|
this.getResult = function () {
|
||||||
var _result = new UAResult();
|
return {
|
||||||
_result.set('ua', _ua);
|
'ua' : userAgent,
|
||||||
_result.set('browser', this.getBrowser());
|
'browser' : this.getBrowser(),
|
||||||
_result.set('cpu', this.getCPU());
|
'cpu' : this.getCPU(),
|
||||||
_result.set('device', this.getDevice());
|
'device' : this.getDevice(),
|
||||||
_result.set('engine', this.getEngine());
|
'engine' : this.getEngine(),
|
||||||
_result.set('os', this.getOS());
|
'os' : this.getOS()
|
||||||
return _result.get();
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
this.getUA = function () {
|
this.getUA = function () {
|
||||||
return _ua;
|
return userAgent;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setUA = function (ua) {
|
this.setUA = function (ua) {
|
||||||
_ua = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
|
userAgent = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
this.setUA(_ua);
|
|
||||||
|
this.setUA(userAgent);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
64
test/test.js
64
test/test.js
@ -335,4 +335,68 @@ describe('Read user-agent data from req.headers', function () {
|
|||||||
let engine = UAParser(req.headers).engine;
|
let engine = UAParser(req.headers).engine;
|
||||||
assert.strictEqual(engine.name, "EdgeHTML");
|
assert.strictEqual(engine.name, "EdgeHTML");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Map UA-CH headers', function () {
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
'sec-ch-ua' : '"Chromium";v="93", "Google Chrome";v="93", " Not;A Brand";v="99"',
|
||||||
|
'sec-ch-ua-full-version-list' : '"Chromium";v="93.0.1.2", "Google Chrome";v="93.0.1.2", " Not;A Brand";v="99.0.1.2"',
|
||||||
|
'sec-ch-ua-arch' : 'ARM',
|
||||||
|
'sec-ch-ua-bitness' : '64',
|
||||||
|
'sec-ch-ua-mobile' : '?1',
|
||||||
|
'sec-ch-ua-model' : 'Pixel 99',
|
||||||
|
'sec-ch-ua-platform' : 'Windows',
|
||||||
|
'sec-ch-ua-platform-version' : '13',
|
||||||
|
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
|
||||||
|
};
|
||||||
|
|
||||||
|
const headers2 = {
|
||||||
|
'sec-ch-ua-mobile' : '?1',
|
||||||
|
'user-agent' : 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36'
|
||||||
|
};
|
||||||
|
|
||||||
|
let uap = UAParser(headers);
|
||||||
|
let browser = uap.browser;
|
||||||
|
let cpu = uap.cpu;
|
||||||
|
let device = uap.device;
|
||||||
|
let engine = uap.engine;
|
||||||
|
let os = uap.os;
|
||||||
|
|
||||||
|
it('Can read from client-hints headers', function () {
|
||||||
|
|
||||||
|
assert.strictEqual(browser.name, "Chrome");
|
||||||
|
assert.strictEqual(browser.version, "93.0.1.2");
|
||||||
|
assert.strictEqual(browser.major, "93");
|
||||||
|
assert.strictEqual(cpu.architecture, "arm64");
|
||||||
|
assert.strictEqual(device.type, "mobile");
|
||||||
|
assert.strictEqual(device.model, "Pixel 99");
|
||||||
|
assert.strictEqual(device.vendor, undefined);
|
||||||
|
assert.strictEqual(engine.name, 'Blink');
|
||||||
|
assert.strictEqual(engine.version, '110.0.0.0');
|
||||||
|
assert.strictEqual(os.name, "Windows");
|
||||||
|
assert.strictEqual(os.version, "11");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Can read from user-agent header', function () {
|
||||||
|
|
||||||
|
uap = UAParser(headers2);
|
||||||
|
browser = uap.browser;
|
||||||
|
cpu = uap.cpu;
|
||||||
|
device = uap.device;
|
||||||
|
engine = uap.engine;
|
||||||
|
os = uap.os;
|
||||||
|
|
||||||
|
assert.strictEqual(browser.name, "Chrome");
|
||||||
|
assert.strictEqual(browser.version, "110.0.0.0");
|
||||||
|
assert.strictEqual(browser.major, "110");
|
||||||
|
assert.strictEqual(cpu.architecture, "amd64");
|
||||||
|
assert.strictEqual(device.type, "mobile");
|
||||||
|
assert.strictEqual(device.model, undefined);
|
||||||
|
assert.strictEqual(device.vendor, undefined);
|
||||||
|
assert.strictEqual(engine.name, 'Blink');
|
||||||
|
assert.strictEqual(engine.version, '110.0.0.0');
|
||||||
|
assert.strictEqual(os.name, "Linux");
|
||||||
|
assert.strictEqual(os.version, "x86_64");
|
||||||
|
});
|
||||||
});
|
});
|
Loading…
x
Reference in New Issue
Block a user