diff --git a/README.markdown b/README.markdown index a946a87b..8be950c8 100644 --- a/README.markdown +++ b/README.markdown @@ -12,25 +12,43 @@ Usage :: [node.js][1] --------------------- ```js var uaParser = require('ua-parser'); -var ua = uaParser.parse(navigator.userAgent); +var ua = uaParser.parse("Mozilla/5.0 (iPhone; CPU iPhone OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B206 Safari/7534.48.3"); console.log(ua.tostring()); -// -> "Safari 5.0.1" +// -> "Mobile Safari 5.1" console.log(ua.toVersionString()); -// -> "5.0.1" +// -> "5.1" + +console.log(ua.toFullString()); +// -> "Mobile Safari 5.1/iOS 5.1.1" console.log(ua.family); -// -> "Safari" +// -> "Mobile Safari" console.log(ua.major); -// -> 5 +// -> "5" console.log(ua.minor); -// -> 0 +// -> "1" console.log(ua.patch); +// -> "" + +console.log(ua.majorInt); +// -> 5 + +console.log(ua.minorInt); // -> 1 + +console.log(ua.patchInt); +// -> 0 + +console.log(ua.os); +// -> { family: "iOS", major: "5", minor: "1", patch: "1", majorInt: 5, minorInt: 1, patch: 1 } + +console.log(ua.device); +// -> { isMobile: true, isSpider: false, family: "iPhone" } ``` diff --git a/js/index.js b/js/index.js index 9b85c687..2f920845 100644 --- a/js/index.js +++ b/js/index.js @@ -6,12 +6,26 @@ var file = path.join(__dirname, '..', 'regexes.yaml'); var regexes = fs.readFileSync(file, 'utf8'); regexes = yaml.eval(regexes); +var mobile_agents = {}; + +for(var i in regexes.mobile_user_agent_families) { + mobile_agents[regexes.mobile_user_agent_families[i]] = true; +} +for(var i in regexes.mobile_os_families) { + mobile_agents[regexes.mobile_os_families[i]] = true; +} + var ua_parsers = regexes.user_agent_parsers.map(function(obj) { var regexp = new RegExp(obj.regex), famRep = obj.family_replacement, - majorVersionRep = obj.v1_replacement; + majorVersionRep = obj.v1_replacement, + minorVersionRep = obj.v2_replacement; function parser(ua) { + if(!ua || !ua.length) { + throw new Error('UserAgent not specified'); + } + var m = ua.match(regexp); if (!m) { return null; } @@ -19,9 +33,20 @@ var ua_parsers = regexes.user_agent_parsers.map(function(obj) { var family = famRep ? famRep.replace('$1', m[1]) : m[1]; var obj = new UserAgent(family); - obj.major = parseInt(majorVersionRep ? majorVersionRep : m[2]); - obj.minor = m[3] ? parseInt(m[3]) : null; - obj.patch = m[4] ? parseInt(m[4]) : null; + + obj.major = (majorVersionRep ? majorVersionRep : m[2]) || ''; + obj.minor = (minorVersionRep ? minorVersionRep : m[3]) || ''; + obj.patch = m[4] || ''; + obj.majorInt = ~~parseInt(obj.major); + obj.minorInt = ~~parseInt(obj.minor); + obj.patchInt = ~~parseInt(obj.patch); + + if(mobile_agents.hasOwnProperty(family)) { + obj.device.isMobile = true; + } + if(family == "Spider") { + obj.device.isSpider = true; + } return obj; } @@ -31,14 +56,21 @@ var ua_parsers = regexes.user_agent_parsers.map(function(obj) { var os_parsers = regexes.os_parsers.map(function(obj) { var regexp = new RegExp(obj.regex), - osRep = obj.os_replacement; + osRep = obj.os_replacement, + minorVersionRep = obj.os_v1_replacement, + majorVersionRep = obj.os_v2_replacement; function parser(ua) { var m = ua.match(regexp); if(!m) { return null; } - var os = (osRep ? osRep : m[1]) + (m.length > 2 ? " " + m[2] : "") + (m.length > 3 ? "." + m[3] : ""); + var os = { + family: (osRep ? osRep.replace('$1', m[1]) : m[1]) || '', + major : (majorVersionRep ? majorVersionRep : m[2]) || '', + minor : (minorVersionRep ? minorVersionRep : m[3]) || '', + patch : m[4] || '' + }; return os; } @@ -46,55 +78,115 @@ var os_parsers = regexes.os_parsers.map(function(obj) { return parser; }); +var device_parsers = regexes.device_parsers.map(function(obj) { + var regexp = new RegExp(obj.regex), + deviceRep = obj.device_replacement; + + function parser(ua) { + var m = ua.match(regexp); + + if(!m) { return null; } + + var device = deviceRep ? deviceRep.replace('$1', m[1]) : m[1]; + + return device; + } + + return parser; +}); + exports.parse = parse; function parse(ua) { - var os, i; - for (i=0; i < ua_parsers.length; i++) { - var result = ua_parsers[i](ua); - if (result) { break; } - } + var result, os, device, i; - for (i=0; i < os_parsers.length; i++) { - os = os_parsers[i](ua); - if (os) { break; } - } + ua_parsers.some(function(u) { + return result = u(ua); + }); + + os_parsers.some(function(o) { + return os = o(ua); + }); + + device_parsers.some(function(d) { + return device = d(ua); + }); if(!result) { result = new UserAgent(); } - result.os = os; + if(os) { + result.os.family = os.family; + result.os.major = os.major; + result.os.minor = os.minor; + result.os.patch = os.patch; + result.os.majorInt = ~~parseInt(os.major); + result.os.minorInt = ~~parseInt(os.minor); + result.os.patchInt = ~~parseInt(os.patch); + } + result.device.family = device || "Other"; return result; } -function UserAgent(family) { - this.family = family || 'Other'; -} - -UserAgent.prototype.toVersionString = function() { +function toVersionString() { var output = ''; - if (this.major != null) { + if (this.major.length) { output += this.major; - if (this.minor != null) { + if (this.minor.length) { output += '.' + this.minor; - if (this.patch != null) { + if (this.patch.length) { output += '.' + this.patch; } } } - return output; -}; + return output; +} -UserAgent.prototype.toString = function() { +function toString() { var suffix = this.toVersionString(); if (suffix) { suffix = ' ' + suffix; } return this.family + suffix; -}; +} + +function OS() { + this.family = 'Other'; + this.major = ''; + this.minor = ''; + this.patch = ''; + this.majorInt = 0; + this.minorInt = 0; + this.patchInt = 0; +} + +OS.prototype.toVersionString = toVersionString; + +OS.prototype.toString = toString; + +function UserAgent(family) { + this.os = new OS(); + + this.family = family || 'Other'; + this.major = ''; + this.minor = ''; + this.patch = ''; + this.majorInt = 0; + this.minorInt = 0; + this.patchInt = 0; + this.device = { + isMobile: false, + isSpider: false, + family : 'Other' + } +} + +UserAgent.prototype.toVersionString = toVersionString; + +UserAgent.prototype.toString = toString; UserAgent.prototype.toFullString = function() { - return this.toString() + (this.os ? "/" + this.os : ""); + return this.toString() + (this.os.toString() ? "/" + this.os.toString() : ""); }; if (require.main === module) { var output, input = process.argv[2]; if (!input) { process.exit(1); } process.stdout.write(parse(input).toString()); -} +} \ No newline at end of file diff --git a/regexes.yaml b/regexes.yaml index 14b154a3..fe73977b 100644 --- a/regexes.yaml +++ b/regexes.yaml @@ -88,6 +88,8 @@ user_agent_parsers: family_replacement: 'Swiftfox' # Rekonq + - regex: '(rekonq)/(.*?)[.](.*?) Safari' + family_replacement: 'Rekonq' - regex: 'rekonq' family_replacement: 'Rekonq'