diff --git a/ng-device-detector.js b/ng-device-detector.js index 04984e9..5500d1f 100644 --- a/ng-device-detector.js +++ b/ng-device-detector.js @@ -24,8 +24,8 @@ PS4: "ps4", VITA: "vita", CHROMECAST: "chromecast", - APPLE_TV:"apple-tv", - GOOGLE_TV:"google-tv", + APPLE_TV: "apple-tv", + GOOGLE_TV: "google-tv", UNKNOWN: "unknown" }) .constant("OS", { @@ -92,336 +92,349 @@ }; } ]) - .factory("deviceDetector", ["$window", "DEVICES", "BROWSERS", "OS", "OS_VERSIONS", "reTree", - function ($window, DEVICES, BROWSERS, OS, OS_VERSIONS, reTree) { - /* ES5 polyfills Start*/ + .provider("deviceDetector", function () { + var customDetectors = []; + this.addCustom = function (customDetectorName, customDetectorRE) { + customDetectors.push({name: customDetectorName, re: customDetectorRE}); + }; + this.$get = ["$window", "DEVICES", "BROWSERS", "OS", "OS_VERSIONS", "reTree", function ($window, DEVICES, BROWSERS, OS, OS_VERSIONS, reTree) { + /* ES5 polyfills Start*/ - // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys - if (!Object.keys) { - Object.keys = (function () { - 'use strict'; - var hasOwnProperty = Object.prototype.hasOwnProperty, - hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), - dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ], - dontEnumsLength = dontEnums.length; + // From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys + if (!Object.keys) { + Object.keys = (function () { + 'use strict'; + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; - return function (obj) { - if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { - throw new TypeError('Object.keys called on non-object'); - } + return function (obj) { + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } - var result = [], prop, i; + var result = [], prop, i; - for (prop in obj) { - if (hasOwnProperty.call(obj, prop)) { - result.push(prop); + for (prop in obj) { + if (hasOwnProperty.call(obj, prop)) { + result.push(prop); + } } - } - if (hasDontEnumBug) { - for (i = 0; i < dontEnumsLength; i++) { - if (hasOwnProperty.call(obj, dontEnums[i])) { - result.push(dontEnums[i]); + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } } } - } - return result; - }; - }()); - } + return result; + }; + }()); + } - // Production steps of ECMA-262, Edition 5, 15.4.4.21 - // Reference: http://es5.github.io/#x15.4.4.21 - if (!Array.prototype.reduce) { - Array.prototype.reduce = function (callback /*, initialValue*/) { - 'use strict'; - if (this == null) { - throw new TypeError('Array.prototype.reduce called on null or undefined'); - } - if (typeof callback !== 'function') { - throw new TypeError(callback + ' is not a function'); - } - var t = Object(this), len = t.length >>> 0, k = 0, value; - if (arguments.length == 2) { - value = arguments[1]; - } else { - while (k < len && !(k in t)) { - k++; + // Production steps of ECMA-262, Edition 5, 15.4.4.21 + // Reference: http://es5.github.io/#x15.4.4.21 + if (!Array.prototype.reduce) { + Array.prototype.reduce = function (callback /*, initialValue*/) { + 'use strict'; + if (this == null) { + throw new TypeError('Array.prototype.reduce called on null or undefined'); } - if (k >= len) { - throw new TypeError('Reduce of empty array with no initial value'); + if (typeof callback !== 'function') { + throw new TypeError(callback + ' is not a function'); } - value = t[k++]; - } - for (; k < len; k++) { - if (k in t) { - value = callback(value, t[k], k, t); + var t = Object(this), len = t.length >>> 0, k = 0, value; + if (arguments.length == 2) { + value = arguments[1]; + } else { + while (k < len && !(k in t)) { + k++; + } + if (k >= len) { + throw new TypeError('Reduce of empty array with no initial value'); + } + value = t[k++]; } - } - return value; - }; - } - /* ES5 polyfills End*/ - - var OS_RE = { - WINDOWS: {and: [{or: [/\bWindows|(Win\d\d)\b/, /\bWin 9x\b/]}, {not: /\bWindows Phone\b/}]}, - MAC: {and: [/\bMac OS\b/, {not: /Windows Phone/}]}, - IOS: {and: [{or: [/\biPad\b/, /\biPhone\b/, /\biPod\b/]}, {not: /Windows Phone/}]}, - ANDROID: {and: [/\bAndroid\b/, {not: /Windows Phone/}]}, - LINUX: /\bLinux\b/, - UNIX: /\bUNIX\b/, - FIREFOX_OS: {and: [/\bFirefox\b/, /Mobile\b/]}, - CHROME_OS: /\bCrOS\b/, - WINDOWS_PHONE: {or: [/\bIEMobile\b/, /\bWindows Phone\b/]}, - PS4: /\bMozilla\/5.0 \(PlayStation 4\b/, - VITA: /\bMozilla\/5.0 \(Play(S|s)tation Vita\b/ - }; - - var BROWSERS_RE = { - CHROME: {and: [{or: [/\bChrome\b/, /\bCriOS\b/]}, {not: {or: [/\bOPR\b/, /\bEdge\b/]}}]}, - FIREFOX: {or:[/\bFirefox\b/,/\bFxiOS\b/]}, - SAFARI: {and: [/^((?!CriOS).)*\Safari\b.*$/, {not: {or: [/\bOPR\b/, /\bEdge\b/, /Windows Phone/,/\bCordova\b/]}}]}, - OPERA: {or: [/Opera\b/, /\bOPR\b/]}, - IE: {or: [/\bMSIE\b/, /\bTrident\b/,/^Mozilla\/5\.0 \(Windows NT 10\.0; Win64; x64\)$/]}, - MS_EDGE: {or: [/\bEdge\b/]}, - PS4: /\bMozilla\/5.0 \(PlayStation 4\b/, - VITA: /\bMozilla\/5.0 \(Play(S|s)tation Vita\b/, - CORDOVA: /\bCordova\b/, - FB_MESSENGER: /\bFBAN\/MessengerForiOS\b/ - }; - - var DEVICES_RE = { - ANDROID: {and: [/\bAndroid\b/, {not: /Windows Phone/}]}, - I_PAD: /\biPad\b/, - IPHONE: {and: [/\biPhone\b/, {not: /Windows Phone/}]}, - I_POD: /\biPod\b/, - BLACKBERRY: /\bblackberry\b/, - FIREFOX_OS: {and: [/\bFirefox\b/, /\bMobile\b/]}, - CHROME_BOOK: /\bCrOS\b/, - WINDOWS_PHONE: {or: [/\bIEMobile\b/, /\bWindows Phone\b/]}, - PS4: /\bMozilla\/5.0 \(PlayStation 4\b/, - CHROMECAST: /\bCrKey\b/, - APPLE_TV:/^iTunes-AppleTV\/4.1$/, - GOOGLE_TV:/\bGoogleTV\b/, - VITA: /\bMozilla\/5.0 \(Play(S|s)tation Vita\b/ - }; + for (; k < len; k++) { + if (k in t) { + value = callback(value, t[k], k, t); + } + } + return value; + }; + } + /* ES5 polyfills End*/ - var OS_VERSIONS_RE = { - WINDOWS_3_11: /Win16/, - WINDOWS_95: /(Windows 95|Win95|Windows_95)/, - WINDOWS_ME: /(Win 9x 4.90|Windows ME)/, - WINDOWS_98: /(Windows 98|Win98)/, - WINDOWS_CE: /Windows CE/, - WINDOWS_2000: /(Windows NT 5.0|Windows 2000)/, - WINDOWS_XP: /(Windows NT 5.1|Windows XP)/, - WINDOWS_SERVER_2003: /Windows NT 5.2/, - WINDOWS_VISTA: /Windows NT 6.0/, - WINDOWS_7: /(Windows 7|Windows NT 6.1)/, - WINDOWS_8_1: /(Windows 8.1|Windows NT 6.3)/, - WINDOWS_8: /(Windows 8|Windows NT 6.2)/, - WINDOWS_10: /(Windows NT 10.0)/, - WINDOWS_PHONE_7_5: /(Windows Phone OS 7.5)/, - WINDOWS_PHONE_8_1: /(Windows Phone 8.1)/, - WINDOWS_PHONE_10: /(Windows Phone 10)/, - WINDOWS_NT_4_0: {and: [/(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/, {not: /Windows NT 10.0/}]}, - MACOSX: /(MAC OS X\s*[^ 0-9])/, - MACOSX_3: /(Darwin 10.3|Mac OS X 10.3)/, - MACOSX_4: /(Darwin 10.4|Mac OS X 10.4)/, - MACOSX_5: /(Mac OS X 10.5)/, - MACOSX_6: /(Mac OS X 10.6)/, - MACOSX_7: /(Mac OS X 10.7)/, - MACOSX_8: /(Mac OS X 10.8)/, - MACOSX_9: /(Mac OS X 10.9)/, - MACOSX_10: /(Mac OS X 10.10)/, - MACOSX_11: /(Mac OS X 10.11)/, - MACOSX_12: /(Mac OS X 10.12)/, - MACOSX_13: /(Mac OS X 10.13)/, - MACOSX_14: /(Mac OS X 10.14)/, - MACOSX_15: /(Mac OS X 10.15)/ - }; + var OS_RE = { + WINDOWS: {and: [{or: [/\bWindows|(Win\d\d)\b/, /\bWin 9x\b/]}, {not: /\bWindows Phone\b/}]}, + MAC: {and: [/\bMac OS\b/, {not: /Windows Phone/}]}, + IOS: {and: [{or: [/\biPad\b/, /\biPhone\b/, /\biPod\b/]}, {not: /Windows Phone/}]}, + ANDROID: {and: [/\bAndroid\b/, {not: /Windows Phone/}]}, + LINUX: /\bLinux\b/, + UNIX: /\bUNIX\b/, + FIREFOX_OS: {and: [/\bFirefox\b/, /Mobile\b/]}, + CHROME_OS: /\bCrOS\b/, + WINDOWS_PHONE: {or: [/\bIEMobile\b/, /\bWindows Phone\b/]}, + PS4: /\bMozilla\/5.0 \(PlayStation 4\b/, + VITA: /\bMozilla\/5.0 \(Play(S|s)tation Vita\b/ + }; - var BROWSER_VERSIONS_RE_MAP = { - CHROME: [/\bChrome\/([\d\.]+)\b/, /\bCriOS\/([\d\.]+)\b/], - FIREFOX: [/\bFirefox\/([\d\.]+)\b/,/\bFxiOS\/([\d\.]+)\b/], - SAFARI: /\bVersion\/([\d\.]+)\b/, - OPERA: [/\bVersion\/([\d\.]+)\b/, /\bOPR\/([\d\.]+)\b/], - IE: [/\bMSIE ([\d\.]+\w?)\b/, /\brv:([\d\.]+\w?)\b/], - CORDOVA: /\bCordova\/([\d\.]+)\b/, - MS_EDGE: /\bEdge\/([\d\.]+)\b/ - }; + var BROWSERS_RE = { + CHROME: {and: [{or: [/\bChrome\b/, /\bCriOS\b/]}, {not: {or: [/\bOPR\b/, /\bEdge\b/]}}]}, + FIREFOX: {or: [/\bFirefox\b/, /\bFxiOS\b/]}, + SAFARI: {and: [/^((?!CriOS).)*\Safari\b.*$/, {not: {or: [/\bOPR\b/, /\bEdge\b/, /Windows Phone/, /\bCordova\b/]}}]}, + OPERA: {or: [/Opera\b/, /\bOPR\b/]}, + IE: {or: [/\bMSIE\b/, /\bTrident\b/, /^Mozilla\/5\.0 \(Windows NT 10\.0; Win64; x64\)$/]}, + MS_EDGE: {or: [/\bEdge\b/]}, + PS4: /\bMozilla\/5.0 \(PlayStation 4\b/, + VITA: /\bMozilla\/5.0 \(Play(S|s)tation Vita\b/, + CORDOVA: /\bCordova\b/, + FB_MESSENGER: /\bFBAN\/MessengerForiOS\b/ + }; - var BROWSER_VERSIONS_RE = Object.keys(BROWSER_VERSIONS_RE_MAP).reduce(function (obj, key) { - obj[BROWSERS[key]] = BROWSER_VERSIONS_RE_MAP[key]; - return obj; - }, {}); + var DEVICES_RE = { + ANDROID: {and: [/\bAndroid\b/, {not: /Windows Phone/}]}, + I_PAD: /\biPad\b/, + IPHONE: {and: [/\biPhone\b/, {not: /Windows Phone/}]}, + I_POD: /\biPod\b/, + BLACKBERRY: /\bblackberry\b/, + FIREFOX_OS: {and: [/\bFirefox\b/, /\bMobile\b/]}, + CHROME_BOOK: /\bCrOS\b/, + WINDOWS_PHONE: {or: [/\bIEMobile\b/, /\bWindows Phone\b/]}, + PS4: /\bMozilla\/5.0 \(PlayStation 4\b/, + CHROMECAST: /\bCrKey\b/, + APPLE_TV: /^iTunes-AppleTV\/4.1$/, + GOOGLE_TV: /\bGoogleTV\b/, + VITA: /\bMozilla\/5.0 \(Play(S|s)tation Vita\b/ + }; - var ua = $window.navigator.userAgent; + var OS_VERSIONS_RE = { + WINDOWS_3_11: /Win16/, + WINDOWS_95: /(Windows 95|Win95|Windows_95)/, + WINDOWS_ME: /(Win 9x 4.90|Windows ME)/, + WINDOWS_98: /(Windows 98|Win98)/, + WINDOWS_CE: /Windows CE/, + WINDOWS_2000: /(Windows NT 5.0|Windows 2000)/, + WINDOWS_XP: /(Windows NT 5.1|Windows XP)/, + WINDOWS_SERVER_2003: /Windows NT 5.2/, + WINDOWS_VISTA: /Windows NT 6.0/, + WINDOWS_7: /(Windows 7|Windows NT 6.1)/, + WINDOWS_8_1: /(Windows 8.1|Windows NT 6.3)/, + WINDOWS_8: /(Windows 8|Windows NT 6.2)/, + WINDOWS_10: /(Windows NT 10.0)/, + WINDOWS_PHONE_7_5: /(Windows Phone OS 7.5)/, + WINDOWS_PHONE_8_1: /(Windows Phone 8.1)/, + WINDOWS_PHONE_10: /(Windows Phone 10)/, + WINDOWS_NT_4_0: {and: [/(Windows NT 4.0|WinNT4.0|WinNT|Windows NT)/, {not: /Windows NT 10.0/}]}, + MACOSX: /(MAC OS X\s*[^ 0-9])/, + MACOSX_3: /(Darwin 10.3|Mac OS X 10.3)/, + MACOSX_4: /(Darwin 10.4|Mac OS X 10.4)/, + MACOSX_5: /(Mac OS X 10.5)/, + MACOSX_6: /(Mac OS X 10.6)/, + MACOSX_7: /(Mac OS X 10.7)/, + MACOSX_8: /(Mac OS X 10.8)/, + MACOSX_9: /(Mac OS X 10.9)/, + MACOSX_10: /(Mac OS X 10.10)/, + MACOSX_11: /(Mac OS X 10.11)/, + MACOSX_12: /(Mac OS X 10.12)/, + MACOSX_13: /(Mac OS X 10.13)/, + MACOSX_14: /(Mac OS X 10.14)/, + MACOSX_15: /(Mac OS X 10.15)/ + }; - var deviceInfo = { - raw: { - userAgent: ua, - os: {}, - browser: {}, - device: {} - } - }; + var BROWSER_VERSIONS_RE_MAP = { + CHROME: [/\bChrome\/([\d\.]+)\b/, /\bCriOS\/([\d\.]+)\b/], + FIREFOX: [/\bFirefox\/([\d\.]+)\b/, /\bFxiOS\/([\d\.]+)\b/], + SAFARI: /\bVersion\/([\d\.]+)\b/, + OPERA: [/\bVersion\/([\d\.]+)\b/, /\bOPR\/([\d\.]+)\b/], + IE: [/\bMSIE ([\d\.]+\w?)\b/, /\brv:([\d\.]+\w?)\b/], + CORDOVA: /\bCordova\/([\d\.]+)\b/, + MS_EDGE: /\bEdge\/([\d\.]+)\b/ + }; - deviceInfo.raw.os = Object.keys(OS).reduce(function (obj, item) { - obj[OS[item]] = reTree.test(ua, OS_RE[item]); - return obj; - }, {}); + var BROWSER_VERSIONS_RE = Object.keys(BROWSER_VERSIONS_RE_MAP).reduce(function (obj, key) { + obj[BROWSERS[key]] = BROWSER_VERSIONS_RE_MAP[key]; + return obj; + }, {}); - deviceInfo.raw.browser = Object.keys(BROWSERS).reduce(function (obj, item) { - obj[BROWSERS[item]] = reTree.test(ua, BROWSERS_RE[item]); - return obj; - }, {}); + var ua = $window.navigator.userAgent; - deviceInfo.raw.device = Object.keys(DEVICES).reduce(function (obj, item) { - obj[DEVICES[item]] = reTree.test(ua, DEVICES_RE[item]); - return obj; - }, {}); + var deviceInfo = { + raw: { + userAgent: ua, + os: {}, + browser: {}, + device: {} + } + }; - deviceInfo.raw.os_version = Object.keys(OS_VERSIONS).reduce(function (obj, item) { - obj[OS_VERSIONS[item]] = reTree.test(ua, OS_VERSIONS_RE[item]); - return obj; - }, {}); + deviceInfo.raw.os = Object.keys(OS).reduce(function (obj, item) { + obj[OS[item]] = reTree.test(ua, OS_RE[item]); + return obj; + }, {}); - deviceInfo.os = [ - OS.WINDOWS, - OS.IOS, - OS.MAC, - OS.ANDROID, - OS.LINUX, - OS.UNIX, - OS.FIREFOX_OS, - OS.CHROME_OS, - OS.WINDOWS_PHONE - ].reduce(function (previousValue, currentValue) { - return (previousValue === OS.UNKNOWN && deviceInfo.raw.os[currentValue]) ? currentValue : previousValue; - }, OS.UNKNOWN); + deviceInfo.raw.browser = Object.keys(BROWSERS).reduce(function (obj, item) { + obj[BROWSERS[item]] = reTree.test(ua, BROWSERS_RE[item]); + return obj; + }, {}); - deviceInfo.browser = [ - BROWSERS.CHROME, - BROWSERS.FIREFOX, - BROWSERS.SAFARI, - BROWSERS.OPERA, - BROWSERS.IE, - BROWSERS.MS_EDGE, - BROWSERS.CORDOVA, - BROWSERS.FB_MESSENGER - ].reduce(function (previousValue, currentValue) { - return (previousValue === BROWSERS.UNKNOWN && deviceInfo.raw.browser[currentValue]) ? currentValue : previousValue; - }, BROWSERS.UNKNOWN); + deviceInfo.raw.device = Object.keys(DEVICES).reduce(function (obj, item) { + obj[DEVICES[item]] = reTree.test(ua, DEVICES_RE[item]); + return obj; + }, {}); - deviceInfo.device = [ - DEVICES.ANDROID, - DEVICES.I_PAD, - DEVICES.IPHONE, - DEVICES.I_POD, - DEVICES.BLACKBERRY, - DEVICES.FIREFOX_OS, - DEVICES.CHROME_BOOK, - DEVICES.WINDOWS_PHONE, - DEVICES.PS4, - DEVICES.CHROMECAST, - DEVICES.APPLE_TV, - DEVICES.GOOGLE_TV, - DEVICES.VITA - ].reduce(function (previousValue, currentValue) { - return (previousValue === DEVICES.UNKNOWN && deviceInfo.raw.device[currentValue]) ? currentValue : previousValue; - }, DEVICES.UNKNOWN); + deviceInfo.raw.os_version = Object.keys(OS_VERSIONS).reduce(function (obj, item) { + obj[OS_VERSIONS[item]] = reTree.test(ua, OS_VERSIONS_RE[item]); + return obj; + }, {}); - deviceInfo.os_version = [ - OS_VERSIONS.WINDOWS_3_11, - OS_VERSIONS.WINDOWS_95, - OS_VERSIONS.WINDOWS_ME, - OS_VERSIONS.WINDOWS_98, - OS_VERSIONS.WINDOWS_CE, - OS_VERSIONS.WINDOWS_2000, - OS_VERSIONS.WINDOWS_XP, - OS_VERSIONS.WINDOWS_SERVER_2003, - OS_VERSIONS.WINDOWS_VISTA, - OS_VERSIONS.WINDOWS_7, - OS_VERSIONS.WINDOWS_8_1, - OS_VERSIONS.WINDOWS_8, - OS_VERSIONS.WINDOWS_10, - OS_VERSIONS.WINDOWS_PHONE_7_5, - OS_VERSIONS.WINDOWS_PHONE_8_1, - OS_VERSIONS.WINDOWS_PHONE_10, - OS_VERSIONS.WINDOWS_NT_4_0, - OS_VERSIONS.MACOSX, - OS_VERSIONS.MACOSX_3, - OS_VERSIONS.MACOSX_4, - OS_VERSIONS.MACOSX_5, - OS_VERSIONS.MACOSX_6, - OS_VERSIONS.MACOSX_7, - OS_VERSIONS.MACOSX_8, - OS_VERSIONS.MACOSX_9, - OS_VERSIONS.MACOSX_10, - OS_VERSIONS.MACOSX_11, - OS_VERSIONS.MACOSX_12, - OS_VERSIONS.MACOSX_13, - OS_VERSIONS.MACOSX_14, - OS_VERSIONS.MACOSX_15 - ].reduce(function (previousValue, currentValue) { - return (previousValue === OS_VERSIONS.UNKNOWN && deviceInfo.raw.os_version[currentValue]) ? currentValue : previousValue; - }, OS_VERSIONS.UNKNOWN); + deviceInfo.os = [ + OS.WINDOWS, + OS.IOS, + OS.MAC, + OS.ANDROID, + OS.LINUX, + OS.UNIX, + OS.FIREFOX_OS, + OS.CHROME_OS, + OS.WINDOWS_PHONE + ].reduce(function (previousValue, currentValue) { + return (previousValue === OS.UNKNOWN && deviceInfo.raw.os[currentValue]) ? currentValue : previousValue; + }, OS.UNKNOWN); - deviceInfo.browser_version = "0"; - if (deviceInfo.browser !== BROWSERS.UNKNOWN) { - var re = BROWSER_VERSIONS_RE[deviceInfo.browser]; - var res = reTree.exec(ua, re); - if (!!res) { - deviceInfo.browser_version = res[1]; - } - } + deviceInfo.browser = [ + BROWSERS.CHROME, + BROWSERS.FIREFOX, + BROWSERS.SAFARI, + BROWSERS.OPERA, + BROWSERS.IE, + BROWSERS.MS_EDGE, + BROWSERS.CORDOVA, + BROWSERS.FB_MESSENGER + ].reduce(function (previousValue, currentValue) { + return (previousValue === BROWSERS.UNKNOWN && deviceInfo.raw.browser[currentValue]) ? currentValue : previousValue; + }, BROWSERS.UNKNOWN); - deviceInfo.isMobile = function () { - return [ + deviceInfo.device = [ DEVICES.ANDROID, DEVICES.I_PAD, DEVICES.IPHONE, DEVICES.I_POD, DEVICES.BLACKBERRY, DEVICES.FIREFOX_OS, + DEVICES.CHROME_BOOK, DEVICES.WINDOWS_PHONE, + DEVICES.PS4, + DEVICES.CHROMECAST, + DEVICES.APPLE_TV, + DEVICES.GOOGLE_TV, DEVICES.VITA - ].some(function (item) { - return deviceInfo.device == item; - }); - }; + ].reduce(function (previousValue, currentValue) { + return (previousValue === DEVICES.UNKNOWN && deviceInfo.raw.device[currentValue]) ? currentValue : previousValue; + }, DEVICES.UNKNOWN); - deviceInfo.isTablet = function () { - return [ - DEVICES.I_PAD, - DEVICES.FIREFOX_OS - ].some(function (item) { - return deviceInfo.device == item; - }); - }; + deviceInfo.os_version = [ + OS_VERSIONS.WINDOWS_3_11, + OS_VERSIONS.WINDOWS_95, + OS_VERSIONS.WINDOWS_ME, + OS_VERSIONS.WINDOWS_98, + OS_VERSIONS.WINDOWS_CE, + OS_VERSIONS.WINDOWS_2000, + OS_VERSIONS.WINDOWS_XP, + OS_VERSIONS.WINDOWS_SERVER_2003, + OS_VERSIONS.WINDOWS_VISTA, + OS_VERSIONS.WINDOWS_7, + OS_VERSIONS.WINDOWS_8_1, + OS_VERSIONS.WINDOWS_8, + OS_VERSIONS.WINDOWS_10, + OS_VERSIONS.WINDOWS_PHONE_7_5, + OS_VERSIONS.WINDOWS_PHONE_8_1, + OS_VERSIONS.WINDOWS_PHONE_10, + OS_VERSIONS.WINDOWS_NT_4_0, + OS_VERSIONS.MACOSX, + OS_VERSIONS.MACOSX_3, + OS_VERSIONS.MACOSX_4, + OS_VERSIONS.MACOSX_5, + OS_VERSIONS.MACOSX_6, + OS_VERSIONS.MACOSX_7, + OS_VERSIONS.MACOSX_8, + OS_VERSIONS.MACOSX_9, + OS_VERSIONS.MACOSX_10, + OS_VERSIONS.MACOSX_11, + OS_VERSIONS.MACOSX_12, + OS_VERSIONS.MACOSX_13, + OS_VERSIONS.MACOSX_14, + OS_VERSIONS.MACOSX_15 + ].reduce(function (previousValue, currentValue) { + return (previousValue === OS_VERSIONS.UNKNOWN && deviceInfo.raw.os_version[currentValue]) ? currentValue : previousValue; + }, OS_VERSIONS.UNKNOWN); - deviceInfo.isDesktop = function () { - return [ - DEVICES.PS4, - DEVICES.CHROME_BOOK, - DEVICES.UNKNOWN - ].some(function (item) { - return deviceInfo.device == item; - }); - }; + deviceInfo.browser_version = "0"; + if (deviceInfo.browser !== BROWSERS.UNKNOWN) { + var re = BROWSER_VERSIONS_RE[deviceInfo.browser]; + var res = reTree.exec(ua, re); + if (!!res) { + deviceInfo.browser_version = res[1]; + } + } + + deviceInfo.isMobile = function () { + return [ + DEVICES.ANDROID, + DEVICES.I_PAD, + DEVICES.IPHONE, + DEVICES.I_POD, + DEVICES.BLACKBERRY, + DEVICES.FIREFOX_OS, + DEVICES.WINDOWS_PHONE, + DEVICES.VITA + ].some(function (item) { + return deviceInfo.device == item; + }); + }; - return deviceInfo; + deviceInfo.isTablet = function () { + return [ + DEVICES.I_PAD, + DEVICES.FIREFOX_OS + ].some(function (item) { + return deviceInfo.device == item; + }); + }; + + deviceInfo.isDesktop = function () { + return [ + DEVICES.PS4, + DEVICES.CHROME_BOOK, + DEVICES.UNKNOWN + ].some(function (item) { + return deviceInfo.device == item; + }); + }; + + deviceInfo.custom = customDetectors.reduce(function (custom, customDetector) { + custom[customDetector.name] = reTree.test(ua, customDetector.re); + return custom; + }, {}); + return deviceInfo; + }]; } - ]) + ) .directive('deviceDetector', ["deviceDetector", function (deviceDetector) { + function customClassName(name) { + return 'is-'+name.toLowerCase().replace(/[^0-9a-z]+/g,'-'); + } + return { restrict: "A", link: function (scope, elm/*, attrs*/) { @@ -431,6 +444,9 @@ elm.toggleClass('is-mobile', deviceDetector.isMobile()); elm.toggleClass('is-tablet', deviceDetector.isTablet()); elm.toggleClass('is-desktop', deviceDetector.isDesktop()); + Object.keys(deviceDetector.custom).forEach(function (customKey) { + elm.toggleClass(customClassName(customKey), deviceDetector.custom[customKey]); + }); } }; }]); diff --git a/ng-device-detector.min.js b/ng-device-detector.min.js index 2f86a29..9e652c2 100644 --- a/ng-device-detector.min.js +++ b/ng-device-detector.min.js @@ -1 +1 @@ -!function(o){"use strict";o.module("ng.deviceDetector",["reTree"]).constant("BROWSERS",{CHROME:"chrome",FIREFOX:"firefox",SAFARI:"safari",OPERA:"opera",IE:"ie",MS_EDGE:"ms-edge",FB_MESSANGER:"fb-messanger",CORDOVA:"cordova",UNKNOWN:"unknown"}).constant("DEVICES",{ANDROID:"android",I_PAD:"ipad",IPHONE:"iphone",I_POD:"ipod",BLACKBERRY:"blackberry",FIREFOX_OS:"firefox-os",CHROME_BOOK:"chrome-book",WINDOWS_PHONE:"windows-phone",PS4:"ps4",VITA:"vita",CHROMECAST:"chromecast",APPLE_TV:"apple-tv",GOOGLE_TV:"google-tv",UNKNOWN:"unknown"}).constant("OS",{WINDOWS:"windows",MAC:"mac",IOS:"ios",ANDROID:"android",LINUX:"linux",UNIX:"unix",FIREFOX_OS:"firefox-os",CHROME_OS:"chrome-os",WINDOWS_PHONE:"windows-phone",UNKNOWN:"unknown"}).constant("OS_VERSIONS",{WINDOWS_3_11:"windows-3-11",WINDOWS_95:"windows-95",WINDOWS_ME:"windows-me",WINDOWS_98:"windows-98",WINDOWS_CE:"windows-ce",WINDOWS_2000:"windows-2000",WINDOWS_XP:"windows-xp",WINDOWS_SERVER_2003:"windows-server-2003",WINDOWS_VISTA:"windows-vista",WINDOWS_7:"windows-7",WINDOWS_8_1:"windows-8-1",WINDOWS_8:"windows-8",WINDOWS_10:"windows-10",WINDOWS_PHONE_7_5:"windows-phone-7-5",WINDOWS_PHONE_8_1:"windows-phone-8-1",WINDOWS_PHONE_10:"windows-phone-10",WINDOWS_NT_4_0:"windows-nt-4-0",MACOSX_15:"mac-os-x-15",MACOSX_14:"mac-os-x-14",MACOSX_13:"mac-os-x-13",MACOSX_12:"mac-os-x-12",MACOSX_11:"mac-os-x-11",MACOSX_10:"mac-os-x-10",MACOSX_9:"mac-os-x-9",MACOSX_8:"mac-os-x-8",MACOSX_7:"mac-os-x-7",MACOSX_6:"mac-os-x-6",MACOSX_5:"mac-os-x-5",MACOSX_4:"mac-os-x-4",MACOSX_3:"mac-os-x-3",MACOSX_2:"mac-os-x-2",MACOSX:"mac-os-x",UNKNOWN:"unknown"}).service("detectUtils",["deviceDetector","DEVICES","BROWSERS","OS",function(o,n,O,e){var i=o;this.isMobile=function(){return"unknown"!==i.device},this.isAndroid=function(){return i.device===n.ANDROID||i.OS===e.ANDROID},this.isIOS=function(){return i.os===e.IOS||i.device===n.I_POD||i.device===n.IPHONE}}]).factory("deviceDetector",["$window","DEVICES","BROWSERS","OS","OS_VERSIONS","reTree",function(o,n,O,e,i,r){Object.keys||(Object.keys=function(){var o=Object.prototype.hasOwnProperty,n=!{toString:null}.propertyIsEnumerable("toString"),O=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],e=O.length;return function(i){if("object"!=typeof i&&("function"!=typeof i||null===i))throw new TypeError("Object.keys called on non-object");var r,S,t=[];for(r in i)o.call(i,r)&&t.push(r);if(n)for(S=0;S>>0,i=0;if(2==arguments.length)n=arguments[1];else{for(;i=e)throw new TypeError("Reduce of empty array with no initial value");n=O[i++]}for(;i>>0,i=0;if(2==arguments.length)e=arguments[1];else{for(;i=O)throw new TypeError("Reduce of empty array with no initial value");e=n[i++]}for(;i` * To use directly add `"deviceDetector"` service to your injectors... +### Setup + +You can set custom detectors at the provider object. +The +```javascript +angular.module(..., ["ng.deviceDetector"]) + .config(['deviceDetectorProvider', function(deviceDetectorProvider) { + deviceDetectorProvider.addCustom("Custom_UA_Entry",{and:["\\bCustom_UA_Entry\\b",{not:"\\bChrome\\b"}]}); + }]) + .controller(..., ["$scope", "deviceDetector", function($scope, deviceDetector) { + $scope.customUAEntry = deviceDetector.custom["Custom_UA_Entry"]; // true / false + }]); +``` + +Custom detectors will also be added as CSS classes with 'is-' prefix and encoded into css class name casing. + ### deviceDetector service Holds the following properties: * raw : object : contains the raw values... for internal use mostly. diff --git a/test/unit.js b/test/unit.js index c5c2187..12c9104 100644 --- a/test/unit.js +++ b/test/unit.js @@ -1,8 +1,14 @@ +"use strict"; + describe("ng-device-detector", function () { var deviceDetector; - function loadDetector(userAgent) { - module("ng.deviceDetector"); + function loadDetector(userAgent, setup) { + module("ng.deviceDetector", function (deviceDetectorProvider) { + if (!!setup) { + setup.apply(null, [deviceDetectorProvider]); + } + }); inject(["$window", function ($window) { var __originalNavigator = $window.navigator; $window.navigator = {}; @@ -78,7 +84,7 @@ describe("ng-device-detector", function () { }); } -// Chrome + // Chrome describeUserAgent("Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36", "windows", "windows-8-1", "chrome", "37.0.2049.0", "unknown", false, false, true); describeUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1664.3 Safari/537.36", @@ -86,7 +92,7 @@ describe("ng-device-detector", function () { describeUserAgent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.21 (KHTML, like Gecko) Chrome/19.0.1042.0 Safari/535.21", "linux", "unknown", "chrome", "19.0.1042.0", "unknown", false, false, true); -// Firefox + // Firefox describeUserAgent("Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0", "windows", "windows-xp", "firefox", "31.0", "unknown", false, false, true); describeUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:25.0) Gecko/20100101 Firefox/25.0", @@ -94,7 +100,7 @@ describe("ng-device-detector", function () { describeUserAgent("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:24.0) Gecko/20100101 Firefox/24.0", "linux", "unknown", "firefox", "24.0", "unknown", false, false, true); -// Safari + // Safari describeUserAgent("Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25", "ios", "unknown", "safari", "6.0", "ipad", true, true, false); describeUserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/537.13+ (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2", @@ -102,21 +108,21 @@ describe("ng-device-detector", function () { describeUserAgent("Mozilla/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/531.2+", "linux", "unknown", "safari", "5.0", "unknown", false, false, true); -// Issue #10 + // Issue #10 describeUserAgent("Mozilla/5.0 (iPad; CPU OS 8_0_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) CriOS/38.0.2125.59 Mobile/12A405 Safari/600.1.4 (000767)", "ios", "unknown", "chrome", "38.0.2125.59", "ipad", true, true, false); -// Issue #14 + // Issue #14 describeUserAgent("Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0)", "windows-phone", "windows-phone-7-5", "ie", "9.0", "windows-phone", true, false, false); -// Issue #15 + // Issue #15 describeUserAgent("Mozilla/5.0 (PlayStation 4 1.52) AppleWebKit/536.26 (KHTML, like Gecko)", "unknown", "unknown", "unknown", "0", "ps4", false, false, true); describeUserAgent("Mozilla/5.0 (Playstation Vita 1.61) AppleWebKit/531.22.8 (KHTML, like Gecko) Silk/3.2", "unknown", "unknown", "unknown", "0", "vita", true, false, false); -// Issue #18 + // Issue #18 describeUserAgent("Mozilla/5.0 (Win16; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36", "windows", "windows-3-11", "chrome", "37.0.2049.0", "unknown", false, false, true); describeUserAgent("Mozilla/5.0 (Windows 95; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36", @@ -179,35 +185,35 @@ describe("ng-device-detector", function () { describeUserAgent("Mozilla/4.0 (compatible; MSIE 11; Windows NT 4.0)", "windows", "windows-nt-4-0", "ie", "11", "unknown", false, false, true); -// Issue 21 + // Issue 21 describeUserAgent("Mozilla/5.0 (Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C; rv:11.0) like Gecko", "windows", "windows-8-1", "ie", "11.0", "unknown", false, false, true); describeUserAgent("Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; rv:11.0) like Gecko", "windows", "windows-7", "ie", "11.0", "unknown", false, false, true); -// Issue 24 + // Issue 24 describeUserAgent("Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36 OPR/29.0.1795.47", "windows", "windows-8-1", "opera", "29.0.1795.47", "unknown", false, false, true); -// Issue 27 + // Issue 27 describeUserAgent("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0", "windows", "windows-10", "ms-edge", "12.0", "unknown", false, false, true); describeUserAgent("Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; DEVICE INFO) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Mobile Safari/537.36 Edge/12.0", "windows-phone", "windows-phone-10", "ms-edge", "12.0", "windows-phone", true, false, false); -// Issue 29 + // Issue 29 describeUserAgent("Mozilla/5.0 (X11; CrOS x86_64 4731.85.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36", "chrome-os", "unknown", "chrome", "31.0.1650.63", "chrome-book", false, false, true); -// Issue 32 + // Issue 32 describeUserAgent("Mozilla/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident/7.0; Touch; rv:11.0; IEMobile/11.0; NOKIA; Lumia 930) like iPhone OS 7_0_3 Mac OS X AppleWebKit/537 (KHTML, like Gecko) Mobile Safari/537", "windows-phone", "windows-phone-8-1", "ie", "11.0", "windows-phone", true, false, false); -// Issue 42 + // Issue 42 describeUserAgent("Mozilla/5.0 (iPhone; CPU iPhone OS 9_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/47.0.2526.107 Mobile/13C75 Safari/601.1.46", "ios", "unknown", "chrome", "47.0.2526.107", "iphone", true, false, false); -// Issue 43 + // Issue 43 describe("ES5 support", function () { beforeEach(function () { Object.keys = undefined; @@ -220,11 +226,11 @@ describe("ng-device-detector", function () { }); }); -// Issue 39 + // Issue 39 describeUserAgent("Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Mobile/13B143 [FBAN/MessengerForiOS;FBAV/48.0.0.20.47;FBBV/17291106;FBDV/iPad2,5;FBMD/iPad;FBSN/iPhone OS;FBSV/9.1;FBSS/1; FBCR/;FBID/tablet;FBLC/cs_CZ;FBOP/1]", "ios", "unknown", "fb-messenger", "0", "ipad", true, true, false); -// Issue 40 + // Issue 40 describeUserAgent("Mozilla/5.0 (CrKey armv7l 1.4.15250) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.0 Safari/537.36", "unknown", "unknown", "chrome", "31.0.1650.0", "chromecast", false, false, false); describeUserAgent("iTunes-AppleTV/4.1", @@ -232,24 +238,61 @@ describe("ng-device-detector", function () { describeUserAgent("Mozilla/5.0 (X11; U; Linux i686; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.127 Large Screen Safari/533.4 GoogleTV/ 162671", "linux", "unknown", "chrome", "5.0.375.127", "google-tv", false, false, false); -// Issue 44 + // Issue 44 describeUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "windows", "windows-10", "ie", "0", "unknown", false, false, true); -// Issue 45 + // Issue 45 describeUserAgent("Mozilla/5.0 (iPad; CPU OS 9_2_1 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/49.0.2623.73 Mobile/13D15 Safari/601.1.46", "ios", "unknown", "chrome", "49.0.2623.73", "ipad", true, true, false); -// Issue 53 + // Issue 53 describeUserAgent("Mozilla/5.0 (iPad; CPU OS 10_2 like Mac OS X) AppleWebKit/602.3.12 (KHTML, like Gecko) FxiOS/6.0 Mobile/14C92 Safari/602.3.12", "ios", "unknown", "firefox", "6.0", "ipad", true, true, false); describeUserAgent("Mozilla/5.0 (iPad; CPU OS 10_2 like Mac OS X) AppleWebKit/602.3.12 (KHTML, like Gecko) Version/10.0 Mobile/14C92 Safari/602.1", "ios", "unknown", "safari", "10.0", "ipad", true, true, false); -// Issue 59 + // Issue 59 describeUserAgent("Mozilla/5.0 (Linux; U; Android 4.0.4; es-ve; LT22i Build/ 6.1.1.B.1.54) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/ 534.30", "android", "unknown", "safari", "4.0", "android", true, false, false); describeUserAgent("Mozilla/5.0 (Linux; U; Android 4.0.4; es-ve; LT22i Build/ 6.1.1.B.1.54) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/ 534.30 Cordova/6.2.0", "android", "unknown", "cordova", "6.2.0", "android", true, false, false); }); + + describe("with custom detection", function () { + it("should detect custom entry is missing", function () { + loadDetector("Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36", function (deviceDetectorProvider) { + deviceDetectorProvider.addCustom("Custom_UA_Entry",/\bCustom_UA_Entry\b/); + }); + expect(deviceDetector.custom["Custom_UA_Entry"]).toBe(false); + }); + + it("should detect custom entry", function () { + loadDetector("Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36 Custom_UA_Entry/1.1", function (deviceDetectorProvider) { + deviceDetectorProvider.addCustom("Custom_UA_Entry",/\bCustom_UA_Entry\b/); + }); + expect(deviceDetector.custom["Custom_UA_Entry"]).toBe(true); + }); + + it("should detect custom entry as string", function () { + loadDetector("Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36 Custom_UA_Entry/1.1", function (deviceDetectorProvider) { + deviceDetectorProvider.addCustom("Custom_UA_Entry","\\bCustom_UA_Entry\\b"); + }); + expect(deviceDetector.custom["Custom_UA_Entry"]).toBe(true); + }); + + it("should detect custom entry as reTree", function () { + loadDetector("Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36 Custom_UA_Entry/1.1", function (deviceDetectorProvider) { + deviceDetectorProvider.addCustom("Custom_UA_Entry",{or:["\\bCustom_UA_Entry\\b"]}); + }); + expect(deviceDetector.custom["Custom_UA_Entry"]).toBe(true); + }); + + it("should detect custom entry as complex reTree", function () { + loadDetector("Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36 Custom_UA_Entry/1.1", function (deviceDetectorProvider) { + deviceDetectorProvider.addCustom("Custom_UA_Entry",{and:["\\bCustom_UA_Entry\\b",{not:"\\bChrome\\b"}]}); + }); + expect(deviceDetector.custom["Custom_UA_Entry"]).toBe(false); + }); + }); });