/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"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 };