UNPKG

detect.js

Version:

JS Library to detect browser, os and device based on the UserAgent String, forked by John Carmichael for ease of use.

372 lines (314 loc) 10.4 kB
/** * Detect.js: User-Agent Parser * https://github.com/darcyclarke/Detect.js * Dual licensed under the MIT and GPL licenses. * * @version * @author Darcy Clarke * @url http://darcyclarke.me * @createdat * * Based on UA-Parser (https://github.com/tobie/ua-parser) by Tobie Langel * * Example Usage: * var agentInfo = detect.parse(navigator.userAgent); * console.log(agentInfo.browser.family); // Chrome * */ (function(root, undefined){ // Shim Array.prototype.map if necessary // Production steps of ECMA-262, Edition 5, 15.4.4.19 // Reference: http://es5.github.com/#x15.4.4.19 if (!Array.prototype.map) { Array.prototype.map = function(callback, thisArg) { var T, A, k; if (this == null) { throw new TypeError(" this is null or not defined"); } // 1. Let O be the result of calling ToObject passing the |this| value as the argument. var O = Object(this); // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". // 3. Let len be ToUint32(lenValue). var len = O.length >>> 0; // 4. If IsCallable(callback) is false, throw a TypeError exception. // See: http://es5.github.com/#x9.11 if (typeof callback !== "function") { throw new TypeError(callback + " is not a function"); } // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. if (thisArg) { T = thisArg; } // 6. Let A be a new array created as if by the expression new Array(len) where Array is // the standard built-in constructor with that name and len is the value of len. A = new Array(len); // 7. Let k be 0 k = 0; // 8. Repeat, while k < len while(k < len) { var kValue, mappedValue; // a. Let Pk be ToString(k). // This is implicit for LHS operands of the in operator // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. // This step can be combined with c // c. If kPresent is true, then if (k in O) { // i. Let kValue be the result of calling the Get internal method of O with argument Pk. kValue = O[ k ]; // ii. Let mappedValue be the result of calling the Call internal method of callback // with T as the this value and argument list containing kValue, k, and O. mappedValue = callback.call(T, kValue, k, O); // iii. Call the DefineOwnProperty internal method of A with arguments // Pk, Property Descriptor {Value: mappedValue, : true, Enumerable: true, Configurable: true}, // and false. // In browsers that support Object.defineProperty, use the following: // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true }); // For best browser support, use the following: A[ k ] = mappedValue; } // d. Increase k by 1. k++; } // 9. return A return A; }; } // Detect var detect = root.detect = (function(){ // Context var _this = function(){}; // Regexes var regexes = {}; // Parsers _this.parsers = [ 'device_parsers', 'browser_parsers', 'os_parsers', 'mobile_os_families', 'mobile_browser_families' ]; // Types _this.types = ['browser', 'os', 'device']; // Regular Expressions _this.regexes = regexes || (function(){ var results = {}; _this.parsers.map(function(parser){ results[parser] = []; }); return results; })(); // Families _this.families = (function(){ var results = {}; _this.types.map(function(type){ results[type] = []; }); return results; })(); // Utility Variables var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype, nativeForEach = ArrayProto.forEach, nativeIndexOf = ArrayProto.indexOf; // Find Utility var find = function(ua, obj){ var ret = {}; for(var i=0; i < obj.length; i++){ ret = obj[i](ua); if(ret){ break; } } return ret; }; // Remove Utility var remove = function(arr, props){ each(arr, function(obj){ each(props, function(prop){ delete obj[prop]; }); }); }; // Contains Utility var contains = function(obj, target) { var found = false; if (obj == null) return found; if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; found = any(obj, function(value) { return value === target; }); return found; }; // Each Utility var each = forEach = function(obj, iterator, context) { if (obj == null) return; if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if (obj.length === +obj.length) { for (var i = 0, l = obj.length; i < l; i++) { iterator.call(context, obj[i], i, obj); } } else { for (var key in obj) { if (_.has(obj, key)) { iterator.call(context, obj[key], key, obj); } } } }; // Extend Utiltiy var extend = function(obj) { each(slice.call(arguments, 1), function(source) { for (var prop in source) { obj[prop] = source[prop]; } }); return obj; }; // Check String Utility var check = function(str){ return !!(str && typeof str != 'undefined' && str != null); } // To Version String Utility var toVersionString = function(obj){ var output = ''; obj = obj || {}; if(check(obj)){ if(check(obj.major)){ output += obj.major; if(check(obj.minor)){ output += '.' + obj.minor; if(check(obj.patch)){ output += '.' + obj.patch; } } } } return output; }; // To String Utility var toString = function(obj){ obj = obj || {}; var suffix = toVersionString(obj); if(suffix) suffix = ' ' + suffix; return (obj && check(obj.family)) ? obj.family + suffix : ''; }; // Parse User-Agent String _this.parse = function(ua){ // Parsers Utility var parsers = function(type){ return _this.regexes[type + '_parsers'].map(function(obj){ var regexp = new RegExp(obj.regex), rep = obj[( type === 'browser' ? 'family' : type ) + '_replacement'], major_rep = obj.major_version_replacement; function parser(ua){ var m = ua.match(regexp); if(!m) return null; var ret = {}; ret.family = (rep ? rep.replace('$1', m[1]) : m[1]) || 'other'; ret.major = parseInt(major_rep ? major_rep : m[2]) || null; ret.minor = m[3] ? parseInt(m[3]) : null; ret.patch = m[4] ? parseInt(m[4]) : null; ret.tablet = (obj.tablet); ret.man = obj.manufacturer || null; return ret; } return parser; }); }; // User Agent var UserAgent = function(){}; // Browsers Parsed var browser_parsers = parsers('browser'); // Operating Systems Parsed var os_parsers = parsers('os'); // Devices Parsed var device_parsers = parsers('device'); // Set Agent var a = new UserAgent(); // Remember the original user agent string a.source = ua; // Set Browser a.browser = find(ua, browser_parsers); if(check(a.browser)){ a.browser.name = toString(a.browser); a.browser.version = toVersionString(a.browser); } else { a.browser = {}; } // Set OS a.os = find(ua, os_parsers); if(check(a.os)){ a.os.name = toString(a.os); a.os.version = toVersionString(a.os); } else { a.os = {}; } // Set Device a.device = find(ua, device_parsers); if(check(a.device)){ a.device.name = toString(a.device); a.device.version = toVersionString(a.device); } else { a.device = { tablet: false, family: 'Other' }; } // Determine Device Type var mobile_agents = {}; var mobile_browser_families = _this.regexes.mobile_browser_families.map(function(str) { mobile_agents[str] = true; }); var mobile_os_families = _this.regexes.mobile_os_families.map(function(str) { mobile_agents[str] = true; }); // Is Spider if(a.browser.family === 'Spider'){ a.device.type = 'Spider'; // Is Tablet } else if(a.browser.tablet || a.os.tablet || a.device.tablet){ a.device.type = 'Tablet'; // Is Mobile } else if(mobile_agents.hasOwnProperty(a.browser.family)){ a.device.type = 'Mobile'; // Is Desktop } else { a.device.type = 'Desktop'; } // Determine Device Manufacturer a.device.manufacturer = a.browser.man || a.os.man || a.device.man || null; // Cleanup Objects remove([a.browser, a.os, a.device], ['tablet', 'man']); // Return Agent return a; } // Return context return _this; })(); // Export the Underscore object for **Node.js** and **"CommonJS"**, // backwards-compatibility for the old `require()` API. If we're not // CommonJS, add `_` to the global object via a string identifier // the Closure Compiler "advanced" mode. Registration as an AMD // via define() happens at the end of this file if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = detect; } exports.detect = detect; } else { root['detect'] = detect; } // AMD define happens at the end for compatibility with AMD // that don't enforce next-turn semantics on modules if (typeof define === 'function' && define.amd) { define(function(require) { return detect; }); } })(window);