Last active
February 28, 2023 15:57
-
-
Save mxdvl/563b5fb0389502e4d746ac1eb08f552d to your computer and use it in GitHub Desktop.
Resolve User Agent
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///////////////////////////////////////////////////////////////////////////////// | |
/* UAParser.js v1.1.34 | |
Copyright © 2012-2023 Faisal Salman <[email protected]> | |
MIT License */ /* | |
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data. | |
Supports browser & node.js environment. | |
Demo : https://faisalman.github.io/ua-parser-js | |
Source : https://github.com/faisalman/ua-parser-js */ | |
///////////////////////////////////////////////////////////////////////////////// | |
////////////// | |
// Constants | |
///////////// | |
var LIBVERSION = '1.1.34', | |
EMPTY = '', | |
UNKNOWN = '?', | |
FUNC_TYPE = 'function', | |
UNDEF_TYPE = 'undefined', | |
OBJ_TYPE = 'object', | |
STR_TYPE = 'string', | |
MAJOR = 'major', | |
MODEL = 'model', | |
NAME = 'name', | |
TYPE = 'type', | |
VENDOR = 'vendor', | |
VERSION = 'version', | |
ARCHITECTURE = 'architecture', | |
CONSOLE = 'console', | |
MOBILE = 'mobile', | |
TABLET = 'tablet', | |
SMARTTV = 'smarttv', | |
WEARABLE = 'wearable', | |
EMBEDDED = 'embedded', | |
USER_AGENT = 'user-agent', | |
UA_MAX_LENGTH = 350; | |
var AMAZON = 'Amazon', | |
APPLE = 'Apple', | |
ASUS = 'ASUS', | |
BLACKBERRY = 'BlackBerry', | |
BROWSER = 'Browser', | |
CHROME = 'Chrome', | |
EDGE = 'Edge', | |
FIREFOX = 'Firefox', | |
GOOGLE = 'Google', | |
HUAWEI = 'Huawei', | |
LG = 'LG', | |
MICROSOFT = 'Microsoft', | |
MOTOROLA = 'Motorola', | |
OPERA = 'Opera', | |
SAMSUNG = 'Samsung', | |
SHARP = 'Sharp', | |
SONY = 'Sony', | |
SWISS = 'Swiss', | |
XIAOMI = 'Xiaomi', | |
ZEBRA = 'Zebra', | |
ZTE = 'ZTE', | |
FACEBOOK = 'Facebook', | |
CHROMIUM_OS = 'Chromium OS', | |
MAC_OS = 'Mac OS'; | |
/////////// | |
// Helper | |
////////// | |
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]; | |
} | |
return mergedRegexes; | |
}, | |
enumerize = function (arr) { | |
var enums = {}; | |
for (var i = 0; i < arr.length; i++) { | |
enums[arr[i].toUpperCase()] = arr[i]; | |
} | |
return enums; | |
}, | |
has = function (str1, str2) { | |
return typeof str1 === STR_TYPE | |
? lowerize(str2).indexOf(lowerize(str1)) !== -1 | |
: false; | |
}, | |
isExtensions = function (obj) { | |
for (var prop in obj) { | |
return /^(browser|cpu|device|engine|os)$/.test(prop); | |
} | |
}, | |
lowerize = function (str, rgx) { | |
return typeof str === STR_TYPE | |
? str.toLowerCase().replace(rgx ? new RegExp(rgx, 'i') : EMPTY, EMPTY) | |
: str; | |
}, | |
majorize = function (version) { | |
return typeof version === STR_TYPE | |
? version.replace(/[^\d\.]/g, EMPTY).split('.')[0] | |
: undefined; | |
}, | |
trim = function (str, len) { | |
if (typeof str === STR_TYPE) { | |
str = str.replace(/^\s\s*/, EMPTY); | |
return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH); | |
} | |
}; | |
/////////////// | |
// Map helper | |
////////////// | |
var rgxMapper = function (ua, arrays) { | |
var i = 0, | |
j, | |
k, | |
p, | |
q, | |
matches, | |
match; | |
// loop through all regexes maps | |
while (i < arrays.length && !matches) { | |
var regex = arrays[i], // even sequence (0,2,4,..) | |
props = arrays[i + 1]; // odd sequence (1,3,5,..) | |
j = k = 0; | |
// try matching uastring with regexes | |
while (j < regex.length && !matches) { | |
if (!regex[j]) { | |
break; | |
} | |
matches = regex[j++].exec(ua); | |
if (!!matches) { | |
for (p = 0; p < props.length; p++) { | |
match = matches[++k]; | |
q = props[p]; | |
// check if given property is actually array | |
if (typeof q === OBJ_TYPE && q.length > 0) { | |
if (q.length === 2) { | |
if (typeof q[1] == FUNC_TYPE) { | |
// assign modified match | |
this[q[0]] = q[1].call(this, match); | |
} else { | |
// assign given value, ignore regex match | |
this[q[0]] = q[1]; | |
} | |
} else if (q.length === 3) { | |
// check whether function or regex | |
if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) { | |
// call function (usually string mapper) | |
this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined; | |
} else { | |
// sanitize match using given regex | |
this[q[0]] = match ? match.replace(q[1], q[2]) : undefined; | |
} | |
} else if (q.length === 4) { | |
this[q[0]] = match | |
? q[3].call(this, match.replace(q[1], q[2])) | |
: undefined; | |
} | |
} else { | |
this[q] = match ? match : undefined; | |
} | |
} | |
} | |
} | |
i += 2; | |
} | |
}, | |
strMapper = function (str, map) { | |
for (var i in map) { | |
// check if current value is array | |
if (typeof map[i] === OBJ_TYPE && map[i].length > 0) { | |
for (var j = 0; j < map[i].length; j++) { | |
if (has(map[i][j], str)) { | |
return i === UNKNOWN ? undefined : i; | |
} | |
} | |
} else if (has(map[i], str)) { | |
return i === UNKNOWN ? undefined : i; | |
} | |
} | |
return str; | |
}; | |
/////////////// | |
// 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 = { | |
ME: '4.90', | |
'NT 3.11': 'NT3.51', | |
'NT 4.0': 'NT4.0', | |
2000: 'NT 5.0', | |
XP: ['NT 5.1', 'NT 5.2'], | |
Vista: 'NT 6.0', | |
7: 'NT 6.1', | |
8: 'NT 6.2', | |
8.1: 'NT 6.3', | |
10: ['NT 6.4', 'NT 10.0'], | |
RT: 'ARM', | |
}; | |
////////////// | |
// Regex map | |
///////////// | |
var regexes = { | |
browser: [ | |
[ | |
/\b(?:crmo|crios)\/([\w\.]+)/i, // Chrome for Android/iOS | |
], | |
[VERSION, [NAME, 'Chrome']], | |
[ | |
/edg(?:e|ios|a)?\/([\w\.]+)/i, // Microsoft Edge | |
], | |
[VERSION, [NAME, 'Edge']], | |
[ | |
// Presto based | |
/(opera mini)\/([-\w\.]+)/i, // Opera Mini | |
/(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i, // Opera Mobi/Tablet | |
/(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i, // Opera | |
], | |
[NAME, VERSION], | |
[ | |
/opios[\/ ]+([\w\.]+)/i, // Opera mini on iphone >= 8.0 | |
], | |
[VERSION, [NAME, OPERA + ' Mini']], | |
[ | |
/\bopr\/([\w\.]+)/i, // Opera Webkit | |
], | |
[VERSION, [NAME, OPERA]], | |
[ | |
// Mixed | |
/(kindle)\/([\w\.]+)/i, // Kindle | |
/(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer | |
// Trident based | |
/(avant |iemobile|slim)(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser | |
/(ba?idubrowser)[\/ ]?([\w\.]+)/i, // Baidu Browser | |
/(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer | |
// Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon | |
/(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i, | |
// Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ, aka ShouQ | |
/(weibo)__([\d\.]+)/i, // Weibo | |
], | |
[NAME, VERSION], | |
[ | |
/(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i, // UCBrowser | |
], | |
[VERSION, [NAME, 'UC' + BROWSER]], | |
[ | |
/microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser | |
/\bqbcore\/([\w\.]+).+microm/i, | |
], | |
[VERSION, [NAME, 'WeChat(Win) Desktop']], | |
[ | |
/micromessenger\/([\w\.]+)/i, // WeChat | |
], | |
[VERSION, [NAME, 'WeChat']], | |
[ | |
/konqueror\/([\w\.]+)/i, // Konqueror | |
], | |
[VERSION, [NAME, 'Konqueror']], | |
[ | |
/trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i, // IE11 | |
], | |
[VERSION, [NAME, 'IE']], | |
[ | |
/yabrowser\/([\w\.]+)/i, // Yandex | |
], | |
[VERSION, [NAME, 'Yandex']], | |
[ | |
/(avast|avg)\/([\w\.]+)/i, // Avast/AVG Secure Browser | |
], | |
[[NAME, /(.+)/, '$1 Secure ' + BROWSER], VERSION], | |
[ | |
/\bfocus\/([\w\.]+)/i, // Firefox Focus | |
], | |
[VERSION, [NAME, FIREFOX + ' Focus']], | |
[ | |
/\bopt\/([\w\.]+)/i, // Opera Touch | |
], | |
[VERSION, [NAME, OPERA + ' Touch']], | |
[ | |
/coc_coc\w+\/([\w\.]+)/i, // Coc Coc Browser | |
], | |
[VERSION, [NAME, 'Coc Coc']], | |
[ | |
/dolfin\/([\w\.]+)/i, // Dolphin | |
], | |
[VERSION, [NAME, 'Dolphin']], | |
[ | |
/coast\/([\w\.]+)/i, // Opera Coast | |
], | |
[VERSION, [NAME, OPERA + ' Coast']], | |
[ | |
/miuibrowser\/([\w\.]+)/i, // MIUI Browser | |
], | |
[VERSION, [NAME, 'MIUI ' + BROWSER]], | |
[ | |
/fxios\/([-\w\.]+)/i, // Firefox for iOS | |
], | |
[VERSION, [NAME, FIREFOX]], | |
[ | |
/\bqihu|(qi?ho?o?|360)browser/i, // 360 | |
], | |
[[NAME, '360 ' + BROWSER]], | |
[/(oculus|samsung|sailfish|huawei)browser\/([\w\.]+)/i], | |
[[NAME, /(.+)/, '$1 ' + BROWSER], VERSION], | |
[ | |
// Oculus/Samsung/Sailfish/Huawei Browser | |
/(comodo_dragon)\/([\w\.]+)/i, // Comodo Dragon | |
], | |
[[NAME, /_/g, ' '], VERSION], | |
[ | |
/(electron)\/([\w\.]+) safari/i, // Electron-based App | |
/(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla | |
/m?(qqbrowser|baiduboxapp|2345Explorer)[\/ ]?([\w\.]+)/i, // QQBrowser/Baidu App/2345 Browser | |
], | |
[NAME, VERSION], | |
[ | |
/(metasr)[\/ ]?([\w\.]+)/i, // SouGouBrowser | |
/(lbbrowser)/i, // LieBao Browser | |
/\[(linkedin)app\]/i, // LinkedIn App for iOS & Android | |
], | |
[NAME], | |
[ | |
// WebView | |
/((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i, // Facebook App for iOS & Android | |
], | |
[[NAME, FACEBOOK], VERSION], | |
[ | |
/(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App | |
/(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 | |
], | |
[NAME, VERSION], | |
[ | |
/\bgsa\/([\w\.]+) .*safari\//i, // Google Search Appliance on iOS | |
], | |
[VERSION, [NAME, 'GSA']], | |
[ | |
/headlesschrome(?:\/([\w\.]+)| )/i, // Chrome Headless | |
], | |
[VERSION, [NAME, CHROME + ' Headless']], | |
[ | |
/ wv\).+(chrome)\/([\w\.]+)/i, // Chrome WebView | |
], | |
[[NAME, CHROME + ' WebView'], VERSION], | |
[ | |
/droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i, // Android Browser | |
], | |
[VERSION, [NAME, 'Android ' + BROWSER]], | |
[ | |
/(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i, // Chrome/OmniWeb/Arora/Tizen/Nokia | |
], | |
[NAME, VERSION], | |
[ | |
/version\/([\w\.\,]+) .*mobile\/\w+ (safari)/i, // Mobile Safari | |
], | |
[VERSION, [NAME, 'Mobile Safari']], | |
[ | |
/version\/([\w(\.|\,)]+) .*(mobile ?safari|safari)/i, // Safari & Safari Mobile | |
], | |
[VERSION, NAME], | |
[ | |
/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i, // Safari < 3.0 | |
], | |
[NAME, [VERSION, strMapper, oldSafariMap]], | |
[/(webkit|khtml)\/([\w\.]+)/i], | |
[NAME, VERSION], | |
[ | |
// Gecko based | |
/(navigator|netscape\d?)\/([-\w\.]+)/i, // Netscape | |
], | |
[[NAME, 'Netscape'], VERSION], | |
[ | |
/mobile vr; rv:([\w\.]+)\).+firefox/i, // Firefox Reality | |
], | |
[VERSION, [NAME, FIREFOX + ' Reality']], | |
[ | |
/ekiohf.+(flow)\/([\w\.]+)/i, // Flow | |
/(swiftfox)/i, // Swiftfox | |
/(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i, | |
// IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar | |
/(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i, | |
// Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix | |
/(firefox)\/([\w\.]+)/i, // Other Firefox-based | |
/(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla | |
// Other | |
/(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i, | |
// Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser | |
/(links) \(([\w\.]+)/i, // Links | |
/panasonic;(viera)/i, // Panasonic Viera | |
], | |
[NAME, VERSION], | |
[ | |
/(cobalt)\/([\w\.]+)/i, // Cobalt | |
], | |
[NAME, [VERSION, /[^\d\.]+./, EMPTY]], | |
], | |
cpu: [ | |
[ | |
/(?:(amd|x(?:(?:86|64)[-_])?|wow|win)64)[;\)]/i, // AMD64 (x64) | |
], | |
[[ARCHITECTURE, 'amd64']], | |
[ | |
/(ia32(?=;))/i, // IA32 (quicktime) | |
], | |
[[ARCHITECTURE, lowerize]], | |
[ | |
/((?:i[346]|x)86)[;\)]/i, // IA32 (x86) | |
], | |
[[ARCHITECTURE, 'ia32']], | |
[ | |
/\b(aarch64|arm(v?8e?l?|_?64))\b/i, // ARM64 | |
], | |
[[ARCHITECTURE, 'arm64']], | |
[ | |
/\b(arm(?:v[67])?ht?n?[fl]p?)\b/i, // ARMHF | |
], | |
[[ARCHITECTURE, 'armhf']], | |
[ | |
// PocketPC mistakenly identified as PowerPC | |
/windows (ce|mobile); ppc;/i, | |
], | |
[[ARCHITECTURE, 'arm']], | |
[ | |
/((?:ppc|powerpc)(?:64)?)(?: mac|;|\))/i, // PowerPC | |
], | |
[[ARCHITECTURE, /ower/, EMPTY, lowerize]], | |
[ | |
/(sun4\w)[;\)]/i, // SPARC | |
], | |
[[ARCHITECTURE, 'sparc']], | |
[ | |
/((?:avr32|ia64(?=;))|68k(?=\))|\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i, | |
// IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC | |
], | |
[[ARCHITECTURE, lowerize]], | |
], | |
device: [ | |
[ | |
////////////////////////// | |
// MOBILES & TABLETS | |
///////////////////////// | |
// Samsung | |
/\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i, | |
], | |
[MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], | |
[ | |
/\b((?:s[cgp]h|gt|sm)-\w+|sc[g-]?[\d]+a?|galaxy nexus)/i, | |
/samsung[- ]([-\w]+)/i, | |
/sec-(sgh\w+)/i, | |
], | |
[MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], | |
[ | |
// Apple | |
/\((ip(?:hone|od)[\w ]*);/i, // iPod/iPhone | |
], | |
[MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], | |
[ | |
/\((ipad);[-\w\),; ]+apple/i, // iPad | |
/applecoremedia\/[\w\.]+ \((ipad)/i, | |
/\b(ipad)\d\d?,\d\d?[;\]].+ios/i, | |
], | |
[MODEL, [VENDOR, APPLE], [TYPE, TABLET]], | |
[/(macintosh);/i], | |
[MODEL, [VENDOR, APPLE]], | |
[ | |
// Sharp | |
/\b(sh-?[altvz]?\d\d[a-ekm]?)/i, | |
], | |
[MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], | |
[ | |
// Huawei | |
/\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i, | |
], | |
[MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], | |
[ | |
/(?:huawei|honor)([-\w ]+)[;\)]/i, | |
/\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i, | |
], | |
[MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], | |
[ | |
// Xiaomi | |
/\b(poco[\w ]+)(?: 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 | |
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i, // Xiaomi Mi | |
], | |
[ | |
[MODEL, /_/g, ' '], | |
[VENDOR, XIAOMI], | |
[TYPE, MOBILE], | |
], | |
[ | |
/\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i, // Mi Pad tablets | |
], | |
[ | |
[MODEL, /_/g, ' '], | |
[VENDOR, XIAOMI], | |
[TYPE, TABLET], | |
], | |
[ | |
// OPPO | |
/; (\w+) bui.+ oppo/i, | |
/\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i, | |
], | |
[MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], | |
[ | |
// Vivo | |
/vivo (\w+)(?: bui|\))/i, | |
/\b(v[12]\d{3}\w?[at])(?: bui|;)/i, | |
], | |
[MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], | |
[ | |
// Realme | |
/\b(rmx[12]\d{3})(?: bui|;|\))/i, | |
], | |
[MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], | |
[ | |
// Motorola | |
/\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i, | |
/\bmot(?:orola)?[- ](\w*)/i, | |
/((?:moto[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i, | |
], | |
[MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], | |
[/\b(mz60\d|xoom[2 ]{0,2}) build\//i], | |
[MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], | |
[ | |
// LG | |
/((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i, | |
], | |
[MODEL, [VENDOR, LG], [TYPE, TABLET]], | |
[ | |
/(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i, | |
/\blg[-e;\/ ]+((?!browser|netcast|android tv)\w+)/i, | |
/\blg-?([\d\w]+) bui/i, | |
], | |
[MODEL, [VENDOR, LG], [TYPE, MOBILE]], | |
[ | |
// Lenovo | |
/(ideatab[-\w ]+)/i, | |
/lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i, | |
], | |
[MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], | |
[ | |
// Nokia | |
/(?:maemo|nokia).*(n900|lumia \d+)/i, | |
/nokia[-_ ]?([-\w\.]*)/i, | |
], | |
[ | |
[MODEL, /_/g, ' '], | |
[VENDOR, 'Nokia'], | |
[TYPE, MOBILE], | |
], | |
[ | |
/(pixel c)\b/i, // Google Pixel C | |
], | |
[MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], | |
[ | |
/droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i, // Google Pixel | |
], | |
[MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], | |
[ | |
// Sony | |
/droid.+ (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i, | |
], | |
[MODEL, [VENDOR, SONY], [TYPE, MOBILE]], | |
[/sony tablet [ps]/i, /\b(?:sony)?sgp\w+(?: bui|\))/i], | |
[ | |
[MODEL, 'Xperia Tablet'], | |
[VENDOR, SONY], | |
[TYPE, TABLET], | |
], | |
[ | |
// OnePlus | |
/ (kb2005|in20[12]5|be20[12][59])\b/i, | |
/(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i, | |
], | |
[MODEL, [VENDOR, 'OnePlus'], [TYPE, MOBILE]], | |
[ | |
// Amazon | |
/(alexa)webm/i, | |
/(kf[a-z]{2}wi)( bui|\))/i, // Kindle Fire without Silk | |
/(kf[a-z]+)( bui|\)).+silk\//i, // Kindle Fire HD | |
], | |
[MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], | |
[ | |
/((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i, // Fire Phone | |
], | |
[ | |
[MODEL, /(.+)/g, 'Fire Phone $1'], | |
[VENDOR, AMAZON], | |
[TYPE, MOBILE], | |
], | |
[ | |
// BlackBerry | |
/(playbook);[-\w\),; ]+(rim)/i, // BlackBerry PlayBook | |
], | |
[MODEL, VENDOR, [TYPE, TABLET]], | |
[ | |
/\b((?:bb[a-f]|st[hv])100-\d)/i, | |
/\(bb10; (\w+)/i, // BlackBerry 10 | |
], | |
[MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], | |
[ | |
// Asus | |
/(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i, | |
], | |
[MODEL, [VENDOR, ASUS], [TYPE, TABLET]], | |
[/ (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i], | |
[MODEL, [VENDOR, ASUS], [TYPE, MOBILE]], | |
[ | |
// HTC | |
/(nexus 9)/i, // HTC Nexus 9 | |
], | |
[MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], | |
[ | |
/(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i, // HTC | |
// ZTE | |
/(zte)[- ]([\w ]+?)(?: bui|\/|\))/i, | |
/(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i, // Alcatel/GeeksPhone/Nexian/Panasonic/Sony | |
], | |
[VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], | |
[ | |
// Acer | |
/droid.+; ([ab][1-7]-?[0178a]\d\d?)/i, | |
], | |
[MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], | |
[ | |
// Meizu | |
/droid.+; (m[1-5] note) bui/i, | |
/\bmz-([-\w]{2,})/i, | |
], | |
[MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], | |
[ | |
// MIXED | |
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[-_ ]?([-\w]*)/i, | |
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron | |
/(hp) ([\w ]+\w)/i, // HP iPAQ | |
/(asus)-?(\w+)/i, // Asus | |
/(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia | |
/(lenovo)[-_ ]?([-\w]+)/i, // Lenovo | |
/(jolla)/i, // Jolla | |
/(oppo) ?([\w ]+) bui/i, // OPPO | |
], | |
[VENDOR, MODEL, [TYPE, MOBILE]], | |
[ | |
/(kobo)\s(ereader|touch)/i, // Kobo | |
/(archos) (gamepad2?)/i, // Archos | |
/(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad | |
/(kindle)\/([\w\.]+)/i, // Kindle | |
/(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]], | |
[ | |
/(surface duo)/i, // Surface Duo | |
], | |
[MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], | |
[ | |
/droid [\d\.]+; (fp\du?)(?: b|\))/i, // Fairphone | |
], | |
[MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], | |
[ | |
/(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 | |
], | |
[[VENDOR, 'Dragon Touch'], MODEL, [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]], | |
[ | |
/(shield[\w ]+) b/i, // Nvidia Shield Tablets | |
], | |
[MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], | |
[ | |
/(sprint) (\w+)/i, // Sprint Phones | |
], | |
[VENDOR, MODEL, [TYPE, MOBILE]], | |
[ | |
/(kin\.[onetw]{3})/i, // Microsoft Kin | |
], | |
[ | |
[MODEL, /\./g, ' '], | |
[VENDOR, MICROSOFT], | |
[TYPE, MOBILE], | |
], | |
[ | |
/droid.+; ([c6]+|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i, // Zebra | |
], | |
[MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]], | |
[/droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i], | |
[MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], | |
[ | |
/////////////////// | |
// SMARTTVS | |
/////////////////// | |
/smart-tv.+(samsung)/i, // Samsung | |
], | |
[VENDOR, [TYPE, SMARTTV]], | |
[/hbbtv.+maple;(\d+)/i], | |
[ | |
[MODEL, /^/, 'SmartTV'], | |
[VENDOR, SAMSUNG], | |
[TYPE, SMARTTV], | |
], | |
[ | |
/(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i, // LG SmartTV | |
], | |
[ | |
[VENDOR, LG], | |
[TYPE, SMARTTV], | |
], | |
[ | |
/(apple) ?tv/i, // Apple TV | |
], | |
[VENDOR, [MODEL, APPLE + ' TV'], [TYPE, SMARTTV]], | |
[ | |
/crkey/i, // Google Chromecast | |
], | |
[ | |
[MODEL, CHROME + 'cast'], | |
[VENDOR, GOOGLE], | |
[TYPE, SMARTTV], | |
], | |
[ | |
/droid.+aft(\w)( bui|\))/i, // Fire TV | |
], | |
[MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], | |
[ | |
/\(dtv[\);].+(aquos)/i, | |
/(aquos-tv[\w ]+)\)/i, // Sharp | |
], | |
[MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]], | |
[ | |
/(bravia[\w ]+)( bui|\))/i, // Sony | |
], | |
[MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], | |
[ | |
/(mitv-\w{5}) bui/i, // Xiaomi | |
], | |
[MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], | |
[ | |
/Hbbtv.*(technisat) (.*);/i, // TechniSAT | |
], | |
[VENDOR, MODEL, [TYPE, SMARTTV]], | |
[ | |
/\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku | |
/hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i, // HbbTV devices | |
], | |
[ | |
[VENDOR, trim], | |
[MODEL, trim], | |
[TYPE, SMARTTV], | |
], | |
[ | |
/\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i, // SmartTV from Unidentified Vendors | |
], | |
[[TYPE, SMARTTV]], | |
[ | |
/////////////////// | |
// CONSOLES | |
/////////////////// | |
/(ouya)/i, // Ouya | |
/(nintendo) (\w+)/i, // Nintendo | |
], | |
[VENDOR, MODEL, [TYPE, CONSOLE]], | |
[ | |
/droid.+; (shield) bui/i, // Nvidia | |
], | |
[MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], | |
[ | |
/(playstation \w+)/i, // Playstation | |
], | |
[MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], | |
[ | |
/\b(xbox(?: one)?(?!; xbox))[\); ]/i, // Microsoft Xbox | |
], | |
[MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], | |
[ | |
/////////////////// | |
// WEARABLES | |
/////////////////// | |
/((pebble))app/i, // Pebble | |
], | |
[VENDOR, MODEL, [TYPE, WEARABLE]], | |
[ | |
/(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i, // Apple Watch | |
], | |
[MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], | |
[ | |
/droid.+; (glass) \d/i, // Google Glass | |
], | |
[MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], | |
[/droid.+; (wt63?0{2,3})\)/i], | |
[MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], | |
[ | |
/(quest( 2| pro)?)/i, // Oculus Quest | |
], | |
[MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], | |
[ | |
/////////////////// | |
// EMBEDDED | |
/////////////////// | |
/(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i, // Tesla | |
], | |
[VENDOR, [TYPE, EMBEDDED]], | |
[ | |
//////////////////// | |
// MIXED (GENERIC) | |
/////////////////// | |
/droid .+?; ([^;]+?)(?: bui|\) applew).+? mobile safari/i, // Android Phones from Unidentified Vendors | |
], | |
[MODEL, [TYPE, MOBILE]], | |
[ | |
/droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i, // Android Tablets from Unidentified Vendors | |
], | |
[MODEL, [TYPE, TABLET]], | |
[ | |
/\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i, // Unidentifiable Tablet | |
], | |
[[TYPE, TABLET]], | |
[ | |
/(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i, // Unidentifiable Mobile | |
], | |
[[TYPE, MOBILE]], | |
[ | |
/(android[-\w\. ]{0,9});.+buil/i, // Generic Android Device | |
], | |
[MODEL, [VENDOR, 'Generic']], | |
], | |
engine: [ | |
[ | |
/windows.+ edge\/([\w\.]+)/i, // EdgeHTML | |
], | |
[VERSION, [NAME, EDGE + 'HTML']], | |
[ | |
/webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i, // Blink | |
], | |
[VERSION, [NAME, 'Blink']], | |
[ | |
/(presto)\/([\w\.]+)/i, // Presto | |
/(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna | |
/ekioh(flow)\/([\w\.]+)/i, // Flow | |
/(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links | |
/(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab | |
], | |
[NAME, VERSION], | |
[ | |
/rv\:([\w\.]{1,9})\b.+(gecko)/i, // Gecko | |
], | |
[VERSION, NAME], | |
], | |
os: [ | |
[ | |
// Windows | |
/microsoft (windows) (vista|xp)/i, // Windows (iTunes) | |
], | |
[NAME, VERSION], | |
[ | |
/(windows) nt 6\.2; (arm)/i, // Windows RT | |
/(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i, // Windows Phone | |
/(windows)[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i, | |
], | |
[NAME, [VERSION, strMapper, windowsVersionMap]], | |
[/(win(?=3|9|n)|win 9x )([nt\d\.]+)/i], | |
[ | |
[NAME, 'Windows'], | |
[VERSION, strMapper, windowsVersionMap], | |
], | |
[ | |
// iOS/macOS | |
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS | |
/cfnetwork\/.+darwin/i, | |
], | |
[ | |
[VERSION, /_/g, '.'], | |
[NAME, 'iOS'], | |
], | |
[ | |
/(mac os x) ?([\w\. ]*)/i, | |
/(macintosh|mac_powerpc\b)(?!.+haiku)/i, // Mac OS | |
], | |
[ | |
[NAME, MAC_OS], | |
[VERSION, /_/g, '.'], | |
], | |
[ | |
// Mobile OSes | |
/droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i, // Android-x86/HarmonyOS | |
], | |
[VERSION, NAME], | |
[ | |
// Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS | |
/(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-\/ ]?([\w\.]*)/i, | |
/(blackberry)\w*\/([\w\.]*)/i, // Blackberry | |
/(tizen|kaios)[\/ ]([\w\.]+)/i, // Tizen/KaiOS | |
/\((series40);/i, // Series 40 | |
], | |
[NAME, VERSION], | |
[ | |
/\(bb(10);/i, // BlackBerry 10 | |
], | |
[VERSION, [NAME, BLACKBERRY]], | |
[ | |
/(?:symbian ?os|symbos|s60(?=;)|series60)[-\/ ]?([\w\.]*)/i, // Symbian | |
], | |
[VERSION, [NAME, 'Symbian']], | |
[ | |
/mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i, // Firefox OS | |
], | |
[VERSION, [NAME, FIREFOX + ' OS']], | |
[ | |
/web0s;.+rt(tv)/i, | |
/\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i, // WebOS | |
], | |
[VERSION, [NAME, 'webOS']], | |
[ | |
/watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i, // watchOS | |
], | |
[VERSION, [NAME, 'watchOS']], | |
[ | |
// Google Chromecast | |
/crkey\/([\d\.]+)/i, // Google Chromecast | |
], | |
[VERSION, [NAME, CHROME + 'cast']], | |
[ | |
/(cros) [\w]+(?:\)| ([\w\.]+)\b)/i, // Chromium OS | |
], | |
[[NAME, CHROMIUM_OS], VERSION], | |
[ | |
// Smart TVs | |
/panasonic;(viera)/i, // Panasonic Viera | |
/(netrange)mmh/i, // Netrange | |
/(nettv)\/(\d+\.[\w\.]+)/i, // NetTV | |
// Console | |
/(nintendo|playstation) (\w+)/i, // Nintendo/Playstation | |
/(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S) | |
// Other | |
/\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm | |
/(mint)[\/\(\) ]?(\w*)/i, // Mint | |
/(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux | |
/([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i, | |
// Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire | |
/(hurd|linux) ?([\w\.]*)/i, // Hurd/Linux | |
/(gnu) ?([\w\.]*)/i, // GNU | |
/\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly | |
/(haiku) (\w+)/i, // Haiku | |
], | |
[NAME, VERSION], | |
[ | |
/(sunos) ?([\w\.\d]*)/i, // Solaris | |
], | |
[[NAME, 'Solaris'], VERSION], | |
[ | |
/((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris | |
/(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX | |
/\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX | |
/(unix) ?([\w\.]*)/i, // UNIX | |
], | |
[NAME, VERSION], | |
], | |
}; | |
///////////////// | |
// Constructor | |
//////////////// | |
function UAItem() {} | |
UAItem.prototype.get = function (prop) { | |
if (!prop) { | |
return this.data; | |
} | |
return this.data.hasOwnProperty(prop) ? this.data[prop] : undefined; | |
}; | |
UAItem.prototype.parse = function (ua, rgxmap) { | |
rgxMapper.call(this.data, ua, rgxmap); | |
}; | |
UAItem.prototype.set = function (prop, val) { | |
this.data[prop] = val; | |
}; | |
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() { | |
this.data = UAItem.createUAData({ | |
init_props: [NAME, VERSION, MAJOR], | |
is_ignoreProps: [VERSION, MAJOR], | |
is_ignoreRgx: ' ?browser$', | |
toString_props: [NAME, VERSION], | |
}); | |
} | |
UABrowser.prototype = new UAItem(); | |
function UACPU() { | |
this.data = UAItem.createUAData({ | |
init_props: [ARCHITECTURE], | |
is_ignoreProps: [], | |
toString_props: [ARCHITECTURE], | |
}); | |
} | |
UACPU.prototype = new UAItem(); | |
function UADevice() { | |
this.data = UAItem.createUAData({ | |
init_props: [TYPE, MODEL, VENDOR], | |
is_ignoreProps: [], | |
toString_props: [VENDOR, MODEL], | |
}); | |
} | |
UADevice.prototype = new UAItem(); | |
function UAEngine() { | |
this.data = UAItem.createUAData({ | |
init_props: [NAME, VERSION], | |
is_ignoreProps: [VERSION], | |
toString_props: [NAME, VERSION], | |
}); | |
} | |
UAEngine.prototype = new UAItem(); | |
function UAOS() { | |
this.data = UAItem.createUAData({ | |
init_props: [NAME, VERSION], | |
is_ignoreProps: [VERSION], | |
is_ignoreRgx: ' ?os$', | |
toString_props: [NAME, VERSION], | |
}); | |
} | |
UAOS.prototype = new UAItem(); | |
function UAResult() { | |
this.data = { | |
ua: '', | |
browser: undefined, | |
cpu: undefined, | |
device: undefined, | |
engine: undefined, | |
os: undefined, | |
}; | |
} | |
UAResult.prototype = new UAItem(); | |
function UAParser(ua, extensions, headers) { | |
if (typeof ua === OBJ_TYPE) { | |
if (isExtensions(ua)) { | |
if (typeof extensions === OBJ_TYPE) { | |
headers = extensions; // case UAParser(extensions, headers) | |
} | |
extensions = ua; // case UAParser(extensions) | |
} else { | |
headers = ua; // case UAParser(headers) | |
} | |
ua = undefined; | |
} else if (typeof ua === STR_TYPE && !isExtensions(extensions)) { | |
headers = extensions; // case UAParser(ua, headers) | |
extensions = undefined; | |
} | |
if (!(this instanceof UAParser)) { | |
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 _ua = | |
ua || | |
(_navigator && _navigator.userAgent | |
? _navigator.userAgent | |
: !ua && headers && headers[USER_AGENT] | |
? headers[USER_AGENT] | |
: EMPTY); | |
var _uach = | |
_navigator && _navigator.userAgentData | |
? _navigator.userAgentData | |
: undefined; | |
var _rgxmap = extensions ? extend(regexes, extensions) : regexes; | |
// public methods | |
this.getBrowser = function () { | |
var _browser = new UABrowser(); | |
_browser.parse(_ua, _rgxmap.browser); | |
_browser.set(MAJOR, majorize(_browser.get(VERSION))); | |
// Brave-specific detection | |
if ( | |
_navigator && | |
_navigator.brave && | |
typeof _navigator.brave.isBrave == FUNC_TYPE | |
) { | |
_browser.set(NAME, 'Brave'); | |
} | |
return _browser.get(); | |
}; | |
this.getCPU = function () { | |
var _cpu = new UACPU(); | |
_cpu.parse(_ua, _rgxmap.cpu); | |
return _cpu.get(); | |
}; | |
this.getDevice = function () { | |
var _device = new UADevice(); | |
_device.parse(_ua, _rgxmap.device); | |
if (!_device.get(TYPE) && _uach && _uach.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'); | |
_device.set(TYPE, TABLET); | |
} | |
return _device.get(); | |
}; | |
this.getEngine = function () { | |
var _engine = new UAEngine(); | |
_engine.parse(_ua, _rgxmap.engine); | |
return _engine.get(); | |
}; | |
this.getOS = function () { | |
var _os = new UAOS(); | |
_os.parse(_ua, _rgxmap.os); | |
if (!_os.get(NAME) && _uach && _uach.platform != 'Unknown') { | |
_os.set( | |
NAME, | |
_uach.platform | |
.replace(/chrome os/i, CHROMIUM_OS) | |
.replace(/macos/i, MAC_OS), | |
); // backward compatibility | |
} | |
return _os.get(); | |
}; | |
this.getResult = function () { | |
var _result = new UAResult(); | |
_result.set('ua', _ua); | |
_result.set('browser', this.getBrowser()); | |
_result.set('cpu', this.getCPU()); | |
_result.set('device', this.getDevice()); | |
_result.set('engine', this.getEngine()); | |
_result.set('os', this.getOS()); | |
return _result.get(); | |
}; | |
this.getUA = function () { | |
return _ua; | |
}; | |
this.setUA = function (ua) { | |
_ua = | |
typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH | |
? trim(ua, UA_MAX_LENGTH) | |
: ua; | |
return this; | |
}; | |
this.setUA(_ua); | |
return this; | |
} | |
UAParser.VERSION = LIBVERSION; | |
UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]); | |
UAParser.CPU = enumerize([ARCHITECTURE]); | |
UAParser.DEVICE = enumerize([ | |
MODEL, | |
VENDOR, | |
TYPE, | |
CONSOLE, | |
MOBILE, | |
SMARTTV, | |
TABLET, | |
WEARABLE, | |
EMBEDDED, | |
]); | |
UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]); | |
/** | |
* Convert version to a semver value. | |
* - 2.5 -> 2.5.0 | |
* - 1 -> 1.0.0 | |
* | |
* @type {(version: string | undefined | null) => string | null} */ | |
const coerce = (version) => { | |
if (!(typeof version === 'string')) return null; | |
return version.split('.').concat(0, 0, 0).slice(0, 3).join('.'); | |
}; | |
/** @type {(uaString: string) => { family: string | null; version: string | null }} */ | |
function resolveUserAgent(uaString) { | |
const parsedUA = UAParser(uaString); | |
const parsedBrowserVersion = coerce(parsedUA.browser.version); | |
const parsedOSVersion = coerce(parsedUA.os.version); | |
const parsedEngineVersion = coerce(parsedUA.engine.version); | |
// Case A: For Safari on iOS, the use the browser version | |
if (parsedUA.browser.name === 'Safari' && parsedUA.os.name === 'iOS') { | |
return { | |
family: 'iOS', | |
version: parsedBrowserVersion, | |
}; | |
} | |
// Case B: The browser on iOS didn't report as safari, | |
// so we use the iOS version as a proxy to the browser | |
// version. This is based on the assumption that the | |
// underlying Safari Engine used will be *atleast* equal | |
// to the iOS version it's running on. | |
if (parsedUA.os.name === 'iOS') { | |
return { | |
family: 'iOS', | |
version: parsedOSVersion, | |
}; | |
} | |
if ( | |
(parsedUA.browser.name === 'Opera' && parsedUA.device.type === 'mobile') || | |
parsedUA.browser.name === 'Opera Mobi' | |
) { | |
return { | |
family: 'OperaMobile', | |
version: parsedBrowserVersion, | |
}; | |
} | |
if (parsedUA.browser.name === 'Samsung Browser') { | |
return { | |
family: 'Samsung', | |
version: parsedBrowserVersion, | |
}; | |
} | |
if (parsedUA.browser.name === 'IE') { | |
return { | |
family: 'Explorer', | |
version: parsedBrowserVersion, | |
}; | |
} | |
if (parsedUA.browser.name === 'IEMobile') { | |
return { | |
family: 'ExplorerMobile', | |
version: parsedBrowserVersion, | |
}; | |
} | |
// Use engine version for gecko-based browsers | |
if (parsedUA.engine.name === 'Gecko') { | |
return { | |
family: 'Firefox', | |
version: parsedEngineVersion, | |
}; | |
} | |
// Use engine version for blink-based browsers | |
if (parsedUA.engine.name === 'Blink') { | |
return { | |
family: 'Chrome', | |
version: parsedEngineVersion, | |
}; | |
} | |
// Chrome based browsers pre-blink (WebKit) | |
if ( | |
parsedUA.browser.name && | |
['Chrome', 'Chromium', 'Chrome WebView', 'Chrome Headless'].includes( | |
parsedUA.browser.name, | |
) | |
) { | |
return { | |
family: 'Chrome', | |
version: parsedBrowserVersion, | |
}; | |
} | |
if (parsedUA.browser.name === 'Android Browser') { | |
// Versions prior to Blink were based | |
// on the OS version. Only after this | |
// did android start using system chrome for web-views | |
return { | |
family: 'Android', | |
version: parsedOSVersion, | |
}; | |
} | |
return { | |
family: parsedUA.browser.name || null, | |
version: parsedBrowserVersion, | |
}; | |
} | |
return resolveUserAgent(useragent); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment