Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
"use strict";
const BROWSERS = [
{
name: "Firefox",
mustContain: new RegExp(`(?:Firefox|FxiOS)\/(${getVersionRegex(1, 1)})`),
},
{
name: "Opera",
mustContain: new RegExp(`(?:OPR|Opera)\/(${getVersionRegex(1, 1)})`),
},
{
name: "Safari",
mustContain: new RegExp(`Version\/(${getVersionRegex(1, 1)}).+Safari`),
mustNotContain: new RegExp("Chrome|Chromium"),
},
{
name: "Edge",
mustContain: new RegExp(`Edge\/(${getVersionRegex(0, 1)})`),
},
{
name: "Chrome",
mustContain: new RegExp(`(?:Chrome|CriOS)\/(${getVersionRegex(1, 1)})`),
},
{
name: "IE",
mustContain: new RegExp(`MSIE (${getVersionRegex(1, 1)})`),
},
];
const OSES = [
{
name: "iOS",
minMinorVersionCount: 0,
mustContain: new RegExp(`CPU iPhone OS (${getVersionRegex(0, 2)})`),
},
{
name: "iPadOS",
minMinorVersionCount: 0,
mustContain: new RegExp(`CPU OS (${getVersionRegex(0, 2)})`),
},
{
name: "Windows Phone",
minMinorVersionCount: 1,
mustContain: new RegExp(`Windows Phone (${getVersionRegex(1, 2)})`),
},
{
name: "Chrome OS",
minMinorVersionCount: 1,
mustContain: new RegExp(`CrOS .+ (${getVersionRegex(1, 2)})`),
},
{
name: "Android",
minMinorVersionCount: 0,
mustContain: new RegExp(`Android (${getVersionRegex(0, 2)})`),
},
{
name: "Windows NT",
minMinorVersionCount: 1,
mustContain: new RegExp(`Windows NT (${getVersionRegex(1, 2)})`),
},
{
name: "Mac OSX",
minMinorVersionCount: 1,
mustContain: new RegExp(`Intel Mac OS X (${getVersionRegex(1, 2)})`),
},
{
name: "Linux",
mustContain: new RegExp("Linux"),
},
];
function getVersionRegex(minMinorVersionCount, maxMinorVersionCount) {
return `\\d+(?:[._][0-9a-z]+){${minMinorVersionCount},${maxMinorVersionCount}}`;
}
function detect(ua, dataset) {
for (const {
name,
mustContain,
mustNotContain,
minMinorVersionCount,
} of dataset) {
const result = mustContain.exec(ua);
if (!result) {
continue;
}
if (mustNotContain && mustNotContain.test(ua)) {
continue;
}
let version = null;
if (result && result.length === 2) {
// Remove most minor version if that expresses 0.
let parts = result[1].match(/([0-9a-z]+)/g);
parts = parts.reverse();
const validVersionIndex = parts.findIndex(
part => parseInt(part, 10) !== 0
);
if (validVersionIndex !== -1) {
parts = parts.splice(validVersionIndex);
for (let i = 0; i < minMinorVersionCount + 1 - parts.length; i++) {
parts.unshift(0);
}
}
version = parts.reverse().join(".");
}
return { name, version };
}
return null;
}
function parseUserAgent(ua) {
return {
browser: detect(ua, BROWSERS),
os: detect(ua, OSES),
};
}
module.exports = { parseUserAgent };