UNPKG

outdated-browser-rework

Version:

Detects outdated browsers and asks users to upgrade to a new version. Handles mobile devices!

1,259 lines (1,069 loc) 167 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.outdatedBrowserRework = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ var DEFAULTS = { Chrome: 57, // Includes Chrome for mobile devices Edge: 39, Safari: 10, "Mobile Safari": 10, Opera: 50, Firefox: 50, Vivaldi: 1, IE: false } var EDGEHTML_VS_EDGE_VERSIONS = { 12: 0.1, 13: 21, 14: 31, 15: 39, 16: 41, 17: 42, 18: 44 } var updateDefaults = function (defaults, updatedValues) { for (var key in updatedValues) { defaults[key] = updatedValues[key] } return defaults } module.exports = function (parsedUserAgent, options) { // Set default options var browserSupport = options.browserSupport ? updateDefaults(DEFAULTS, options.browserSupport) : DEFAULTS var requiredCssProperty = options.requiredCssProperty || false var browserName = parsedUserAgent.browser.name; var isAndroidButNotChrome if (options.requireChromeOnAndroid) { isAndroidButNotChrome = parsedUserAgent.os.name === "Android" && parsedUserAgent.browser.name !== "Chrome" } var parseMinorVersion = function (version) { return version.replace(/[^\d.]/g, '').split(".")[1]; } var isBrowserUnsupported = function () { var isUnsupported = false if (!(browserName in browserSupport)) { if (!options.isUnknownBrowserOK) { isUnsupported = true } } else if (!browserSupport[browserName]) { isUnsupported = true } return isUnsupported; } var isBrowserUnsupportedResult = isBrowserUnsupported(); var isBrowserOutOfDate = function () { var browserVersion = parsedUserAgent.browser.version; var browserMajorVersion = parsedUserAgent.browser.major; var osName = parsedUserAgent.os.name; var osVersion = parsedUserAgent.os.version; // Edge legacy needed a version mapping, Edge on Chromium doesn't if (browserName === "Edge" && browserMajorVersion <= 18) { browserMajorVersion = EDGEHTML_VS_EDGE_VERSIONS[browserMajorVersion]; } // Firefox Mobile on iOS is essentially Mobile Safari so needs to be handled that way // See: https://github.com/mikemaccana/outdated-browser-rework/issues/98#issuecomment-597721173 if (browserName === 'Firefox' && osName === 'iOS') { browserName = 'Mobile Safari'; browserVersion = osVersion; browserMajorVersion = osVersion.substring(0, osVersion.indexOf('.')); } var isOutOfDate = false if (isBrowserUnsupportedResult) { isOutOfDate = true; } else if (browserName in browserSupport) { var minVersion = browserSupport[browserName] if (typeof minVersion == 'object') { var minMajorVersion = minVersion.major var minMinorVersion = minVersion.minor if (browserMajorVersion < minMajorVersion) { isOutOfDate = true } else if (browserMajorVersion == minMajorVersion) { var browserMinorVersion = parseMinorVersion(browserVersion) if (browserMinorVersion < minMinorVersion) { isOutOfDate = true } } } else if (browserMajorVersion < minVersion) { isOutOfDate = true } } return isOutOfDate } // Returns true if a browser supports a css3 property var isPropertySupported = function (property) { if (!property) { return true } var div = document.createElement("div") var vendorPrefixes = ["khtml", "ms", "o", "moz", "webkit"] var count = vendorPrefixes.length // Note: HTMLElement.style.hasOwnProperty seems broken in Edge if (property in div.style) { return true } property = property.replace(/^[a-z]/, function (val) { return val.toUpperCase() }) while (count--) { var prefixedProperty = vendorPrefixes[count] + property // See comment re: HTMLElement.style.hasOwnProperty above if (prefixedProperty in div.style) { return true } } return false } // Return results return { isAndroidButNotChrome: isAndroidButNotChrome, isBrowserOutOfDate: isBrowserOutOfDate(), isBrowserUnsupported: isBrowserUnsupportedResult, isPropertySupported: isPropertySupported(requiredCssProperty) }; } },{}],2:[function(require,module,exports){ /* Highly dumbed down version of https://github.com/unclechu/node-deep-extend */ /** * Extening object that entered in first argument. * * Returns extended object or false if have no target object or incorrect type. * * If you wish to clone source object (without modify it), just use empty new * object as first argument, like this: * deepExtend({}, yourObj_1, [yourObj_N]); */ module.exports = function deepExtend(/*obj_1, [obj_2], [obj_N]*/) { if (arguments.length < 1 || typeof arguments[0] !== "object") { return false } if (arguments.length < 2) { return arguments[0] } var target = arguments[0] for (var i = 1; i < arguments.length; i++) { var obj = arguments[i] for (var key in obj) { var src = target[key] var val = obj[key] if (typeof val !== "object" || val === null) { target[key] = val // just clone arrays (and recursive clone objects inside) } else if (typeof src !== "object" || src === null) { target[key] = deepExtend({}, val) // source value and new value is objects both, extending... } else { target[key] = deepExtend(src, val) } } } return target } },{}],3:[function(require,module,exports){ var evaluateBrowser = require("./evaluateBrowser") var languageMessages = require("./languages.json") var deepExtend = require("./extend") var UserAgentParser = require("ua-parser-js") var COLORS = { salmon: "#f25648", white: "white" } module.exports = function(options) { var main = function() { // Despite the docs, UA needs to be provided to constructor explicitly: // https://github.com/faisalman/ua-parser-js/issues/90 var parsedUserAgent = new UserAgentParser(navigator.userAgent).getResult() // Variable definition (before ajax) var outdatedUI = document.getElementById("outdated") // Set default options options = options || {} var browserLocale = window.navigator.language || window.navigator.userLanguage // Everyone else, IE // CSS property to check for. You may also like 'borderSpacing', 'boxShadow', 'transform', 'borderImage'; var backgroundColor = options.backgroundColor || COLORS.salmon var textColor = options.textColor || COLORS.white var fullscreen = options.fullscreen || false var language = options.language || browserLocale.slice(0, 2) // Language code var updateSource = "web" // Other possible values are 'googlePlay' or 'appStore'. Determines where we tell users to go for upgrades. // Chrome mobile is still Chrome (unlike Safari which is 'Mobile Safari') var isAndroid = parsedUserAgent.os.name === "Android" if (isAndroid) { updateSource = "googlePlay" } else if (parsedUserAgent.os.name === "iOS") { updateSource = "appStore" } var isBrowserUnsupported = false // set later after browser evaluation var done = true var changeOpacity = function (opacityValue) { outdatedUI.style.opacity = opacityValue / 100 outdatedUI.style.filter = "alpha(opacity=" + opacityValue + ")" } var fadeIn = function (opacityValue) { changeOpacity(opacityValue) if (opacityValue === 1) { outdatedUI.style.display = "table" } if (opacityValue === 100) { done = true } } var makeFadeInFunction = function (opacityValue) { return function () { fadeIn(opacityValue) } } // Style element explicitly - TODO: investigate and delete if not needed var startStylesAndEvents = function () { var buttonClose = document.getElementById("buttonCloseUpdateBrowser") var buttonUpdate = document.getElementById("buttonUpdateBrowser") //check settings attributes outdatedUI.style.backgroundColor = backgroundColor //way too hard to put !important on IE6 outdatedUI.style.color = textColor outdatedUI.children[0].children[0].style.color = textColor outdatedUI.children[0].children[1].style.color = textColor // Update button is desktop only if (buttonUpdate) { buttonUpdate.style.color = textColor if (buttonUpdate.style.borderColor) { buttonUpdate.style.borderColor = textColor } // Override the update button color to match the background color buttonUpdate.onmouseover = function () { this.style.color = backgroundColor this.style.backgroundColor = textColor } buttonUpdate.onmouseout = function () { this.style.color = textColor this.style.backgroundColor = backgroundColor } } buttonClose.style.color = textColor buttonClose.onmousedown = function () { outdatedUI.style.display = "none" return false } } var getMessage = function (lang) { var defaultMessages = languageMessages[lang] || languageMessages.en var customMessages = options.messages && options.messages[lang] var messages = deepExtend({}, defaultMessages, customMessages) var updateMessages = { web: "<p>" + messages.update.web + (messages.url ? ( '<a id="buttonUpdateBrowser" rel="nofollow" href="' + messages.url + '">' + messages.callToAction + "</a>" ) : '') + "</p>", googlePlay: "<p>" + messages.update.googlePlay + '<a id="buttonUpdateBrowser" rel="nofollow" href="https://play.google.com/store/apps/details?id=com.android.chrome">' + messages.callToAction + "</a></p>", appStore: "<p>" + messages.update[updateSource] + "</p>" } var updateMessage = updateMessages[updateSource] var browserSupportMessage = messages.outOfDate; if (isBrowserUnsupported && messages.unsupported) { browserSupportMessage = messages.unsupported; } return ( '<div class="vertical-center"><h6>' + browserSupportMessage + "</h6>" + updateMessage + '<p class="last"><a href="#" id="buttonCloseUpdateBrowser" title="' + messages.close + '">&times;</a></p></div>' ) } var result = evaluateBrowser(parsedUserAgent, options); if (result.isAndroidButNotChrome || result.isBrowserOutOfDate || !result.isPropertySupported) { // This is an outdated browser and the banner needs to show // Set this flag with the result for `getMessage` isBrowserUnsupported = result.isBrowserUnsupported if (done && outdatedUI.style.opacity !== "1") { done = false for (var opacity = 1; opacity <= 100; opacity++) { setTimeout(makeFadeInFunction(opacity), opacity * 8) } } var insertContentHere = document.getElementById("outdated") if (fullscreen) { insertContentHere.classList.add("fullscreen") } insertContentHere.innerHTML = getMessage(language) startStylesAndEvents() } } // Load main when DOM ready. var oldOnload = window.onload if (typeof window.onload !== "function") { window.onload = main } else { window.onload = function() { if (oldOnload) { oldOnload() } main() } } } },{"./evaluateBrowser":1,"./extend":2,"./languages.json":4,"ua-parser-js":5}],4:[function(require,module,exports){ module.exports={ "ko": { "outOfDate": "최신 브라우저가 아닙니다!", "update": { "web": "웹사이트를 제대로 보려면 브라우저를 업데이트하세요.", "googlePlay": "Google Play에서 Chrome을 설치하세요", "appStore": "설정 앱에서 iOS를 업데이트하세요" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "지금 브라우저 업데이트하기", "close": "닫기" }, "ja": { "outOfDate": "古いブラウザをお使いのようです。", "update": { "web": "ウェブサイトを正しく表示できるように、ブラウザをアップデートしてください。", "googlePlay": "Google PlayからChromeをインストールしてください", "appStore": "設定からiOSをアップデートしてください" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "今すぐブラウザをアップデートする", "close": "閉じる" }, "br": { "outOfDate": "O seu navegador est&aacute; desatualizado!", "update": { "web": "Atualize o seu navegador para ter uma melhor experi&ecirc;ncia e visualiza&ccedil;&atilde;o deste site. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Atualize o seu navegador agora", "close": "Fechar" }, "ca": { "outOfDate": "El vostre navegador no està actualitzat!", "update": { "web": "Actualitzeu el vostre navegador per veure correctament aquest lloc web. ", "googlePlay": "Instal·leu Chrome des de Google Play", "appStore": "Actualitzeu iOS des de l'aplicació Configuració" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Actualitzar el meu navegador ara", "close": "Tancar" }, "zh": { "outOfDate": "您的浏览器已过时", "update": { "web": "要正常浏览本网站请升级您的浏览器。", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "现在升级", "close": "关闭" }, "cz": { "outOfDate": "Váš prohlížeč je zastaralý!", "update": { "web": "Pro správné zobrazení těchto stránek aktualizujte svůj prohlížeč. ", "googlePlay": "Nainstalujte si Chrome z Google Play", "appStore": "Aktualizujte si systém iOS" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Aktualizovat nyní svůj prohlížeč", "close": "Zavřít" }, "da": { "outOfDate": "Din browser er forældet!", "update": { "web": "Opdatér din browser for at få vist denne hjemmeside korrekt. ", "googlePlay": "Installér venligst Chrome fra Google Play", "appStore": "Opdatér venligst iOS" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Opdatér din browser nu", "close": "Luk" }, "de": { "outOfDate": "Ihr Browser ist veraltet!", "update": { "web": "Bitte aktualisieren Sie Ihren Browser, um diese Website korrekt darzustellen. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Den Browser jetzt aktualisieren ", "close": "Schließen" }, "ee": { "outOfDate": "Sinu veebilehitseja on vananenud!", "update": { "web": "Palun uuenda oma veebilehitsejat, et näha lehekülge korrektselt. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Uuenda oma veebilehitsejat kohe", "close": "Sulge" }, "en": { "outOfDate": "Your browser is out-of-date!", "update": { "web": "Update your browser to view this website correctly. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Update my browser now", "close": "Close" }, "es": { "outOfDate": "¡Tu navegador está anticuado!", "update": { "web": "Actualiza tu navegador para ver esta página correctamente. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Actualizar mi navegador ahora", "close": "Cerrar" }, "fa": { "rightToLeft": true, "outOfDate": "مرورگر شما منسوخ شده است!", "update": { "web": "جهت مشاهده صحیح این وبسایت، مرورگرتان را بروز رسانی نمایید. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "همین حالا مرورگرم را بروز کن", "close": "Close" }, "fi": { "outOfDate": "Selaimesi on vanhentunut!", "update": { "web": "Lataa ajantasainen selain n&auml;hd&auml;ksesi t&auml;m&auml;n sivun oikein. ", "googlePlay": "Asenna uusin Chrome Google Play -kaupasta", "appStore": "Päivitä iOS puhelimesi asetuksista" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "P&auml;ivit&auml; selaimeni nyt ", "close": "Sulje" }, "fr": { "outOfDate": "Votre navigateur n'est plus compatible !", "update": { "web": "Mettez à jour votre navigateur pour afficher correctement ce site Web. ", "googlePlay": "Merci d'installer Chrome depuis le Google Play Store", "appStore": "Merci de mettre à jour iOS depuis l'application Réglages" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Mettre à jour maintenant ", "close": "Fermer" }, "hu": { "outOfDate": "A böngészője elavult!", "update": { "web": "Firssítse vagy cserélje le a böngészőjét. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "A böngészőm frissítése ", "close": "Close" }, "id": { "outOfDate": "Browser yang Anda gunakan sudah ketinggalan zaman!", "update": { "web": "Perbaharuilah browser Anda agar bisa menjelajahi website ini dengan nyaman. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Perbaharui browser sekarang ", "close": "Close" }, "it": { "outOfDate": "Il tuo browser non &egrave; aggiornato!", "update": { "web": "Aggiornalo per vedere questo sito correttamente. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Aggiorna ora", "close": "Chiudi" }, "lt": { "outOfDate": "Jūsų naršyklės versija yra pasenusi!", "update": { "web": "Atnaujinkite savo naršyklę, kad galėtumėte peržiūrėti šią svetainę tinkamai. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Atnaujinti naršyklę ", "close": "Close" }, "nl": { "outOfDate": "Je gebruikt een oude browser!", "update": { "web": "Update je browser om deze website correct te bekijken. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Update mijn browser nu ", "close": "Sluiten" }, "pl": { "outOfDate": "Twoja przeglądarka jest przestarzała!", "update": { "web": "Zaktualizuj swoją przeglądarkę, aby poprawnie wyświetlić tę stronę. ", "googlePlay": "Proszę zainstalować przeglądarkę Chrome ze sklepu Google Play", "appStore": "Proszę zaktualizować iOS z Ustawień" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Zaktualizuj przeglądarkę już teraz", "close": "Zamknij" }, "pt": { "outOfDate": "O seu browser est&aacute; desatualizado!", "update": { "web": "Atualize o seu browser para ter uma melhor experi&ecirc;ncia e visualiza&ccedil;&atilde;o deste site. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Atualize o seu browser agora", "close": "Fechar" }, "ro": { "outOfDate": "Browserul este învechit!", "update": { "web": "Actualizați browserul pentru a vizualiza corect acest site. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Actualizați browserul acum!", "close": "Close" }, "ru": { "outOfDate": "Ваш браузер устарел!", "update": { "web": "Обновите ваш браузер для правильного отображения этого сайта. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Обновить мой браузер ", "close": "Закрыть" }, "si": { "outOfDate": "Vaš brskalnik je zastarel!", "update": { "web": "Za pravilen prikaz spletne strani posodobite vaš brskalnik. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Posodobi brskalnik ", "close": "Zapri" }, "sv": { "outOfDate": "Din webbläsare stödjs ej längre!", "update": { "web": "Uppdatera din webbläsare för att webbplatsen ska visas korrekt. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Uppdatera min webbläsare nu", "close": "Stäng" }, "ua": { "outOfDate": "Ваш браузер застарів!", "update": { "web": "Оновіть ваш браузер для правильного відображення цього сайта. ", "googlePlay": "Please install Chrome from Google Play", "appStore": "Please update iOS from the Settings App" }, "url": "https://browser-update.org/update-browser.html", "callToAction": "Оновити мій браузер ", "close": "Закрити" } } },{}],5:[function(require,module,exports){ /*! * UAParser.js v0.7.22 * Lightweight JavaScript-based User-Agent string parser * https://github.com/faisalman/ua-parser-js * * Copyright © 2012-2019 Faisal Salman <f@faisalman.com> * Licensed under MIT License */ (function (window, undefined) { 'use strict'; ////////////// // Constants ///////////// var LIBVERSION = '0.7.22', EMPTY = '', UNKNOWN = '?', FUNC_TYPE = 'function', UNDEF_TYPE = 'undefined', OBJ_TYPE = 'object', STR_TYPE = 'string', MAJOR = 'major', // deprecated MODEL = 'model', NAME = 'name', TYPE = 'type', VENDOR = 'vendor', VERSION = 'version', ARCHITECTURE= 'architecture', CONSOLE = 'console', MOBILE = 'mobile', TABLET = 'tablet', SMARTTV = 'smarttv', WEARABLE = 'wearable', EMBEDDED = 'embedded'; /////////// // Helper ////////// var util = { extend : function (regexes, extensions) { var mergedRegexes = {}; for (var i in regexes) { if (extensions[i] && extensions[i].length % 2 === 0) { mergedRegexes[i] = extensions[i].concat(regexes[i]); } else { mergedRegexes[i] = regexes[i]; } } return mergedRegexes; }, has : function (str1, str2) { if (typeof str1 === "string") { return str2.toLowerCase().indexOf(str1.toLowerCase()) !== -1; } else { return false; } }, lowerize : function (str) { return str.toLowerCase(); }, major : function (version) { return typeof(version) === STR_TYPE ? version.replace(/[^\d\.]/g,'').split(".")[0] : undefined; }, trim : function (str) { return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); } }; /////////////// // Map helper ////////////// var mapper = { rgx : 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) { 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; } }, str : function (str, map) { for (var i in map) { // check if array if (typeof map[i] === OBJ_TYPE && map[i].length > 0) { for (var j = 0; j < map[i].length; j++) { if (util.has(map[i][j], str)) { return (i === UNKNOWN) ? undefined : i; } } } else if (util.has(map[i], str)) { return (i === UNKNOWN) ? undefined : i; } } return str; } }; /////////////// // String map ////////////// var maps = { browser : { oldsafari : { version : { '1.0' : '/8', '1.2' : '/1', '1.3' : '/3', '2.0' : '/412', '2.0.2' : '/416', '2.0.3' : '/417', '2.0.4' : '/419', '?' : '/' } } }, device : { amazon : { model : { 'Fire Phone' : ['SD', 'KF'] } }, sprint : { model : { 'Evo Shift 4G' : '7373KT' }, vendor : { 'HTC' : 'APA', 'Sprint' : 'Sprint' } } }, os : { windows : { version : { '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 : [[ // Presto based /(opera\smini)\/([\w\.-]+)/i, // Opera Mini /(opera\s[mobiletab]+).+version\/([\w\.-]+)/i, // Opera Mobi/Tablet /(opera).+version\/([\w\.]+)/i, // Opera > 9.80 /(opera)[\/\s]+([\w\.]+)/i // Opera < 9.80 ], [NAME, VERSION], [ /(opios)[\/\s]+([\w\.]+)/i // Opera mini on iphone >= 8.0 ], [[NAME, 'Opera Mini'], VERSION], [ /\s(opr)\/([\w\.]+)/i // Opera Webkit ], [[NAME, 'Opera'], VERSION], [ // Mixed /(kindle)\/([\w\.]+)/i, // Kindle /(lunascape|maxthon|netfront|jasmine|blazer)[\/\s]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer // Trident based /(avant\s|iemobile|slim)(?:browser)?[\/\s]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser /(bidubrowser|baidubrowser)[\/\s]?([\w\.]+)/i, // Baidu Browser /(?:ms|\()(ie)\s([\w\.]+)/i, // Internet Explorer // Webkit/KHTML based /(rekonq)\/([\w\.]*)/i, // Rekonq /(chromium|flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon)\/([\w\.-]+)/i // Chromium/Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon ], [NAME, VERSION], [ /(konqueror)\/([\w\.]+)/i // Konqueror ], [[NAME, 'Konqueror'], VERSION], [ /(trident).+rv[:\s]([\w\.]+).+like\sgecko/i // IE11 ], [[NAME, 'IE'], VERSION], [ /(edge|edgios|edga|edg)\/((\d+)?[\w\.]+)/i // Microsoft Edge ], [[NAME, 'Edge'], VERSION], [ /(yabrowser)\/([\w\.]+)/i // Yandex ], [[NAME, 'Yandex'], VERSION], [ /(Avast)\/([\w\.]+)/i // Avast Secure Browser ], [[NAME, 'Avast Secure Browser'], VERSION], [ /(AVG)\/([\w\.]+)/i // AVG Secure Browser ], [[NAME, 'AVG Secure Browser'], VERSION], [ /(puffin)\/([\w\.]+)/i // Puffin ], [[NAME, 'Puffin'], VERSION], [ /(focus)\/([\w\.]+)/i // Firefox Focus ], [[NAME, 'Firefox Focus'], VERSION], [ /(opt)\/([\w\.]+)/i // Opera Touch ], [[NAME, 'Opera Touch'], VERSION], [ /((?:[\s\/])uc?\s?browser|(?:juc.+)ucweb)[\/\s]?([\w\.]+)/i // UCBrowser ], [[NAME, 'UCBrowser'], VERSION], [ /(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon ], [[NAME, /_/g, ' '], VERSION], [ /(windowswechat qbcore)\/([\w\.]+)/i // WeChat Desktop for Windows Built-in Browser ], [[NAME, 'WeChat(Win) Desktop'], VERSION], [ /(micromessenger)\/([\w\.]+)/i // WeChat ], [[NAME, 'WeChat'], VERSION], [ /(brave)\/([\w\.]+)/i // Brave browser ], [[NAME, 'Brave'], VERSION], [ /(qqbrowserlite)\/([\w\.]+)/i // QQBrowserLite ], [NAME, VERSION], [ /(QQ)\/([\d\.]+)/i // QQ, aka ShouQ ], [NAME, VERSION], [ /m?(qqbrowser)[\/\s]?([\w\.]+)/i // QQBrowser ], [NAME, VERSION], [ /(baiduboxapp)[\/\s]?([\w\.]+)/i // Baidu App ], [NAME, VERSION], [ /(2345Explorer)[\/\s]?([\w\.]+)/i // 2345 Browser ], [NAME, VERSION], [ /(MetaSr)[\/\s]?([\w\.]+)/i // SouGouBrowser ], [NAME], [ /(LBBROWSER)/i // LieBao Browser ], [NAME], [ /xiaomi\/miuibrowser\/([\w\.]+)/i // MIUI Browser ], [VERSION, [NAME, 'MIUI Browser']], [ /;fbav\/([\w\.]+);/i // Facebook App for iOS & Android ], [VERSION, [NAME, 'Facebook']], [ /safari\s(line)\/([\w\.]+)/i, // Line App for iOS /android.+(line)\/([\w\.]+)\/iab/i // Line App for Android ], [NAME, VERSION], [ /headlesschrome(?:\/([\w\.]+)|\s)/i // Chrome Headless ], [VERSION, [NAME, 'Chrome Headless']], [ /\swv\).+(chrome)\/([\w\.]+)/i // Chrome WebView ], [[NAME, /(.+)/, '$1 WebView'], VERSION], [ /((?:oculus|samsung)browser)\/([\w\.]+)/i ], [[NAME, /(.+(?:g|us))(.+)/, '$1 $2'], VERSION], [ // Oculus / Samsung Browser /android.+version\/([\w\.]+)\s+(?:mobile\s?safari|safari)*/i // Android Browser ], [VERSION, [NAME, 'Android Browser']], [ /(sailfishbrowser)\/([\w\.]+)/i // Sailfish Browser ], [[NAME, 'Sailfish Browser'], VERSION], [ /(chrome|omniweb|arora|[tizenoka]{5}\s?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia ], [NAME, VERSION], [ /(dolfin)\/([\w\.]+)/i // Dolphin ], [[NAME, 'Dolphin'], VERSION], [ /(qihu|qhbrowser|qihoobrowser|360browser)/i // 360 ], [[NAME, '360 Browser']], [ /((?:android.+)crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS ], [[NAME, 'Chrome'], VERSION], [ /(coast)\/([\w\.]+)/i // Opera Coast ], [[NAME, 'Opera Coast'], VERSION], [ /fxios\/([\w\.-]+)/i // Firefox for iOS ], [VERSION, [NAME, 'Firefox']], [ /version\/([\w\.]+).+?mobile\/\w+\s(safari)/i // Mobile Safari ], [VERSION, [NAME, 'Mobile Safari']], [ /version\/([\w\.]+).+?(mobile\s?safari|safari)/i // Safari & Safari Mobile ], [VERSION, NAME], [ /webkit.+?(gsa)\/([\w\.]+).+?(mobile\s?safari|safari)(\/[\w\.]+)/i // Google Search Appliance on iOS ], [[NAME, 'GSA'], VERSION], [ /webkit.+?(mobile\s?safari|safari)(\/[\w\.]+)/i // Safari < 3.0 ], [NAME, [VERSION, mapper.str, maps.browser.oldsafari.version]], [ /(webkit|khtml)\/([\w\.]+)/i ], [NAME, VERSION], [ // Gecko based /(navigator|netscape)\/([\w\.-]+)/i // Netscape ], [[NAME, 'Netscape'], VERSION], [ /(swiftfox)/i, // Swiftfox /(icedragon|iceweasel|camino|chimera|fennec|maemo\sbrowser|minimo|conkeror)[\/\s]?([\w\.\+]+)/i, // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror /(firefox|seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([\w\.-]+)$/i, // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix /(mozilla)\/([\w\.]+).+rv\:.+gecko\/\d+/i, // Mozilla // Other /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir)[\/\s]?([\w\.]+)/i, // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir /(links)\s\(([\w\.]+)/i, // Links /(gobrowser)\/?([\w\.]*)/i, // GoBrowser /(ice\s?browser)\/v?([\w\._]+)/i, // ICE Browser /(mosaic)[\/\s]([\w\.]+)/i // Mosaic ], [NAME, VERSION] ], cpu : [[ /(?:(amd|x(?:(?:86|64)[_-])?|wow|win)64)[;\)]/i // AMD64 ], [[ARCHITECTURE, 'amd64']], [ /(ia32(?=;))/i // IA32 (quicktime) ], [[ARCHITECTURE, util.lowerize]], [ /((?:i[346]|x)86)[;\)]/i // IA32 ], [[ARCHITECTURE, 'ia32']], [ // PocketPC mistakenly identified as PowerPC /windows\s(ce|mobile);\sppc;/i ], [[ARCHITECTURE, 'arm']], [ /((?:ppc|powerpc)(?:64)?)(?:\smac|;|\))/i // PowerPC ], [[ARCHITECTURE, /ower/, '', util.lowerize]], [ /(sun4\w)[;\)]/i // SPARC ], [[ARCHITECTURE, 'sparc']], [ /((?:avr32|ia64(?=;))|68k(?=\))|arm(?:64|(?=v\d+[;l]))|(?=atmel\s)avr|(?:irix|mips|sparc)(?:64)?(?=;)|pa-risc)/i // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC ], [[ARCHITECTURE, util.lowerize]] ], device : [[ /\((ipad|playbook);[\w\s\),;-]+(rim|apple)/i // iPad/PlayBook ], [MODEL, VENDOR, [TYPE, TABLET]], [ /applecoremedia\/[\w\.]+ \((ipad)/ // iPad ], [MODEL, [VENDOR, 'Apple'], [TYPE, TABLET]], [ /(apple\s{0,1}tv)/i // Apple TV ], [[MODEL, 'Apple TV'], [VENDOR, 'Apple'], [TYPE, SMARTTV]], [ /(archos)\s(gamepad2?)/i, // Archos /(hp).+(touchpad)/i, // HP TouchPad /(hp).+(tablet)/i, // HP Tablet /(kindle)\/([\w\.]+)/i, // Kindle /\s(nook)[\w\s]+build\/(\w+)/i, // Nook /(dell)\s(strea[kpr\s\d]*[\dko])/i // Dell Streak ], [VENDOR, MODEL, [TYPE, TABLET]], [ /(kf[A-z]+)\sbuild\/.+silk\//i // Kindle Fire HD ], [MODEL, [VENDOR, 'Amazon'], [TYPE, TABLET]], [ /(sd|kf)[0349hijorstuw]+\sbuild\/.+silk\//i // Fire Phone ], [[MODEL, mapper.str, maps.device.amazon.model], [VENDOR, 'Amazon'], [TYPE, MOBILE]], [ /android.+aft([bms])\sbuild/i // Fire TV ], [MODEL, [VENDOR, 'Amazon'], [TYPE, SMARTTV]], [ /\((ip[honed|\s\w*]+);.+(apple)/i // iPod/iPhone ], [MODEL, VENDOR, [TYPE, MOBILE]], [ /\((ip[honed|\s\w*]+);/i // iPod/iPhone ], [MODEL, [VENDOR, 'Apple'], [TYPE, MOBILE]], [ /(blackberry)[\s-]?(\w+)/i, // BlackBerry /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron)[\s_-]?([\w-]*)/i, // BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron /(hp)\s([\w\s]+\w)/i, // HP iPAQ /(asus)-?(\w+)/i // Asus ], [VENDOR, MODEL, [TYPE, MOBILE]], [ /\(bb10;\s(\w+)/i // BlackBerry 10 ], [MODEL, [VENDOR, 'BlackBerry'], [TYPE, MOBILE]], [ // Asus Tablets /android.+(transfo[prime\s]{4,10}\s\w+|eeepc|slider\s\w+|nexus 7|padfone|p00c)/i ], [MODEL, [VENDOR, 'Asus'], [TYPE, TABLET]], [ /(sony)\s(tablet\s[ps])\sbuild\//i, // Sony /(sony)?(?:sgp.+)\sbuild\//i ], [[VENDOR, 'Sony'], [MODEL, 'Xperia Tablet'], [TYPE, TABLET]], [ /android.+\s([c-g]\d{4}|so[-l]\w+)(?=\sbuild\/|\).+chrome\/(?![1-6]{0,1}\d\.))/i ], [MODEL, [VENDOR, 'Sony'], [TYPE, MOBILE]], [ /\s(ouya)\s/i, // Ouya /(nintendo)\s([wids3u]+)/i // Nintendo ], [VENDOR, MODEL, [TYPE, CONSOLE]], [ /android.+;\s(shield)\sbuild/i // Nvidia ], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [ /(playstation\s[34portablevi]+)/i // Playstation ], [MODEL, [VENDOR, 'Sony'], [TYPE, CONSOLE]], [ /(sprint\s(\w+))/i // Sprint Phones ], [[VENDOR, mapper.str, maps.device.sprint.vendor], [MODEL, mapper.str, maps.device.sprint.model], [TYPE, MOBILE]], [ /(htc)[;_\s-]+([\w\s]+(?=\)|\sbuild)|\w+)/i, // HTC /(zte)-(\w*)/i, // ZTE /(alcatel|geeksphone|nexian|panasonic|(?=;\s)sony)[_\s-]?([\w-]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony ], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [ /(nexus\s9)/i // HTC Nexus 9 ], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [ /d\/huawei([\w\s-]+)[;\)]/i, /(nexus\s6p|vog-l29|ane-lx1|eml-l29|ele-l29)/i // Huawei ], [MODEL, [VENDOR, 'Huawei'], [TYPE, MOBILE]], [ /android.+(bah2?-a?[lw]\d{2})/i // Huawei MediaPad ], [MODEL, [VENDOR, 'Huawei'], [TYPE, TABLET]], [ /(microsoft);\s(lumia[\s\w]+)/i // Microsoft Lumia ], [VENDOR, MODEL, [TYPE, MOBILE]], [ /[\s\(;](xbox(?:\sone)?)[\s\);]/i // Microsoft Xbox ], [MODEL, [VENDOR, 'Microsoft'], [TYPE, CONSOLE]], [ /(kin\.[onetw]{3})/i // Microsoft Kin ], [[MODEL, /\./g, ' '], [VENDOR, 'Microsoft'], [TYPE, MOBILE]], [ // Motorola /\s(milestone|droid(?:[2-4x]|\s(?:bionic|x2|pro|razr))?:?(\s4g)?)[\w\s]+build\//i, /mot[\s-]?(\w*)/i, /(XT\d{3,4}) build\//i, /(nexus\s6)/i ], [MODEL, [VENDOR, 'Motorola'], [TYPE, MOBILE]], [ /android.+\s(mz60\d|xoom[\s2]{0,2})\sbuild\//i ], [MODEL, [VENDOR, 'Motorola'], [TYPE, TABLET]], [ /hbbtv\/\d+\.\d+\.\d+\s+\([\w\s]*;\s*(\w[^;]*);([^;]*)/i // HbbTV devices ], [[VENDOR, util.trim], [MODEL, util.trim], [TYPE, SMARTTV]], [ /hbbtv.+maple;(\d+)/i ], [[MODEL, /^/, 'SmartTV'], [VENDOR, 'Samsung'], [TYPE, SMARTTV]], [ /\(dtv[\);].+(aquos)/i // Sharp ], [MODEL, [VENDOR, 'Sharp'], [TYPE, SMARTTV]], [ /android.+((sch-i[89]0\d|shw-m380s|gt-p\d{4}|gt-n\d+|sgh-t8[56]9|nexus 10))/i, /((SM-T\w+))/i ], [[VENDOR, 'Samsung'], MODEL, [TYPE, TABLET]], [ // Samsung /smart-tv.+(samsung)/i ], [VENDOR, [TYPE, SMARTTV], MODEL], [ /((s[cgp]h-\w+|gt-\w+|galaxy\snexus|sm-\w[\w\d]+))/i, /(sam[sung]*)[\s-]*(\w+-?[\w-]*)/i, /sec-((sgh\w+))/i ], [[VENDOR, 'Samsung'], MODEL, [TYPE, MOBILE]], [ /sie-(\w*)/i // Siemens ], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [ /(maemo|nokia).*(n900|lumia\s\d+)/i, // Nokia /(nokia)[\s_-]?([\w-]*)/i ], [[VENDOR, 'Nokia'], MODEL, [TYPE, MOBILE]], [ /android[x\d\.\s;]+\s([ab][1-7]\-?[0178a]\d\d?)/i // Acer ], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [ /android.+([vl]k\-?\d{3})\s+build/i // LG Tablet ], [MODEL, [VENDOR, 'LG'], [TYPE, TABLET]], [ /android\s3\.[\s\w;-]{10}(lg?)-([06cv9]{3,4})/i // LG Tablet ], [[VENDOR, 'LG'], MODEL, [TYPE, TABLET]], [ /(lg) netcast\.tv/i // LG SmartTV ], [VENDOR, MODEL, [TYPE, SMARTTV]], [ /(nexus\s[45])/i, // LG /lg[e;\s\/-]+(\w*)/i, /android.+lg(\-?[\d\w]+)\s+build/i ], [MODEL, [VENDOR, 'LG'], [TYPE, MOBILE]], [ /(lenovo)\s?(s(?:5000|6000)(?:[\w-]+)|tab(?:[\s\w]+))/i // Lenovo tablets ], [VENDOR, MODEL, [TYPE, TABLET]], [ /android.+(ideatab[a-z0-9\-\s]+)/i // Lenovo ], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [ /(lenovo)[_\s-]?([\w-]+)/i ], [VENDOR, MODEL, [TYPE, MOBILE]], [ /linux;.+((jolla));/i // Jolla ], [VENDOR, MODEL, [TYPE, MOBILE]], [ /((pebble))app\/[\d\.]+\s/i // Pebble ], [VENDOR, MODEL, [TYPE, WEARABLE]], [ /android.+;\s(oppo)\s?([\w\s]+)\sbuild/i // OPPO ], [VENDOR, MODEL, [TYPE, MOBILE]], [ /crkey/i // Google Chromecast ], [[MODEL, 'Chromecast'], [VENDOR, 'Google'], [TYPE, SMARTTV]], [ /android.+;\s(glass)\s\d/i // Google Glass ], [MODEL, [VENDOR, 'Google'], [TYPE, WEARABLE]], [ /android.+;\s(pixel c)[\s)]/i // Google Pixel C ], [MODEL, [VENDOR, 'Google'], [TYPE, TABLET]], [ /android.+;\s(pixel( [23])?( xl)?)[\s)]/i // Google Pixel ], [MODEL, [VENDOR, 'Google'], [TYPE, MOBILE]], [ /android.+;\s(\w+)\s+build\/hm\1/i, // Xiaomi Hongmi 'numeric' models /android.+(hm[\s\-_]*note?[\s_]*(?:\d\w)?)\s+build/i, // Xiaomi Hongmi /android.+(mi[\s\-_]*(?:a\d|one|one[\s_]plus|note lte)?[\s_]*(?:\d?\w?)[\s_]*(?:plus)?)\s+build/i, // Xiaomi Mi /android.+(redmi[\s\-_]*(?:note)?(?:[\s_]?[\w\s]+))\s+build/i // Redmi Phones ], [[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, MOBILE]], [ /android.+(mi[\s\-_]*(?:pad)(?:[\s_]?[\w\s]+))\s+build/i // Mi Pad tablets ],[[MODEL, /_/g, ' '], [VENDOR, 'Xiaomi'], [TYPE, TABLET]], [ /android.+;\s(m[1-5]\snote)\sbuild/i // Meizu ], [MODEL, [VENDOR, 'M