Socket
Socket
Sign inDemoInstall

useragent

Package Overview
Dependencies
0
Maintainers
1
Versions
39
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.0 to 2.0.0

CHANGELOG.md

6

bin/testfiles.js

@@ -8,5 +8,5 @@ #!/usr/bin/env node

var files = {
'pgts.yaml': 'http://ua-parser.googlecode.com/svn/trunk/test/resources/pgts_browser_list.yaml'
, 'testcases.yaml': 'http://ua-parser.googlecode.com/svn/trunk/test/resources/test_user_agent_parser.yaml'
, 'firefoxes.yaml': 'http://ua-parser.googlecode.com/svn/trunk/test/resources/firefox_user_agent_strings.yaml'
'pgts.yaml': 'https://raw.github.com/tobie/ua-parser/master/test_resources/pgts_browser_list.yaml'
, 'testcases.yaml': 'https://raw.github.com/tobie/ua-parser/master/test_resources/test_user_agent_parser.yaml'
, 'firefoxes.yaml': 'https://raw.github.com/tobie/ua-parser/master/test_resources/firefox_user_agent_strings.yaml'
};

@@ -13,0 +13,0 @@

#!/usr/bin/env node
var updater = require('../lib/update');
updater(function updating (err, data) {
"use strict";
'use strict';
if (err) return console.error('Update unsuccessfull due to errors', err.message);
/**
* Update our definition file.
*/
require('../lib/update').update(function updating(err, data) {
if (err) {
console.error('Update unsuccessfull due to reasons');
console.log(err.message);
console.log(err.stack);
// yay no issues
return;
}
console.log('Successfully fetched and generated new parsers from the internets.');
});

@@ -6,17 +6,6 @@ "use strict";

*/
var Agent = require('../lib/useragent').Agent
var Agent = require('../').Agent
, semver = require('semver');
/**
* Fetches the features of the browser from the browserscope knowledge base.
*
* @api public
*/
Agent.prototype.__defineGetter__('features', function () {
});
/**
* Checks if the user agent's version can be satisfied agents the give

@@ -29,5 +18,4 @@ * ranged argument. This uses the semver libraries range construction.

*/
Agent.prototype.satisfies = function satisfies (range) {
return semver.satisfies(this.major + '.' + this.minor + '.' + this.patch, range);
};

@@ -1,1 +0,583 @@

module.exports = require('./lib/useragent');
'use strict';
/**
* This is where all the magic comes from, specially crafted for `useragent`.
*/
var regexps = require('./lib/regexps');
/**
* Reduce references by storing the lookups.
*/
// OperatingSystem parsers:
var osparsers = regexps.os
, osparserslength = osparsers.length;
// UserAgent parsers:
var agentparsers = regexps.browser
, agentparserslength = agentparsers.length;
// Device parsers:
var deviceparsers = regexps.device
, deviceparserslength = deviceparsers.length;
/**
* The representation of a parsed user agent.
*
* @constructor
* @param {String} family The name of the browser
* @param {String} major Major version of the browser
* @param {String} minor Minor version of the browser
* @param {String} patch Patch version of the browser
* @param {String} source The actual user agent string
* @api public
*/
function Agent(family, major, minor, patch, source) {
this.family = family || 'Other';
this.major = major || '0';
this.minor = minor || '0';
this.patch = patch || '0';
this.source = source || '';
}
/**
* OnDemand parsing of the Operating System.
*
* @type {OperatingSystem}
* @api public
*/
Object.defineProperty(Agent.prototype, 'os', {
get: function lazyparse() {
var userAgent = this.source
, length = osparserslength
, parsers = osparsers
, i = 0
, parser
, res;
for (; i < length; i++) {
if (res = parsers[i][0].exec(userAgent)) {
parser = parsers[i];
if (parser[1]) res[1] = parser[1].replace('$1', res[1]);
break;
}
}
return Object.defineProperty(this, 'os', {
value: !parser || !res
? new OperatingSystem()
: new OperatingSystem(
res[1]
, parser[2] || res[2]
, parser[3] || res[3]
, parser[4] || res[4]
)
}).os;
},
/**
* Bypass the OnDemand parsing and set an OperatingSystem instance.
*
* @param {OperatingSystem} os
* @api public
*/
set: function set(os) {
if (!(os instanceof OperatingSystem)) return false;
return Object.defineProperty(this, 'os', {
value: os
}).os;
}
});
/**
* OnDemand parsing of the Device type.
*
* @type {Device}
* @api public
*/
Object.defineProperty(Agent.prototype, 'device', {
get: function lazyparse() {
var userAgent = this.source
, length = deviceparserslength
, parsers = deviceparsers
, i = 0
, parser
, res;
for (; i < length; i++) {
if (res = parsers[i][0].exec(userAgent)) {
parser = parsers[i];
if (parser[1]) res[1] = parser[1].replace('$1', res[1]);
break;
}
}
return Object.defineProperty(this, 'device', {
value: !parser || !res
? new Device()
: new Device(
res[1]
, parser[2] || res[2]
, parser[3] || res[3]
, parser[4] || res[4]
)
}).device;
},
/**
* Bypass the OnDemand parsing and set an Device instance.
*
* @param {Device} device
* @api public
*/
set: function set(device) {
if (!(device instanceof Device)) return false;
return Object.defineProperty(this, 'device', {
value: device
}).device;
}
});
/*** Generates a string output of the parsed user agent.
*
* @returns {String}
* @api public
*/
Agent.prototype.toAgent = function toAgent() {
var output = this.family
, version = this.toVersion();
if (version) output += ' '+ version;
return output;
};
/**
* Generates a string output of the parser user agent and operating system.
*
* @returns {String} "UserAgent 0.0.0 / OS"
* @api public
*/
Agent.prototype.toString = function toString() {
var agent = this.toAgent()
, os = this.os !== 'Other' ? this.os : false;
return agent + (os ? ' / ' + os : '');
};
/**
* Outputs a compiled veersion number of the user agent.
*
* @returns {String}
* @api public
*/
Agent.prototype.toVersion = function toVersion() {
var version = '';
if (this.major) {
version += this.major;
if (this.minor) {
version += '.' + this.minor;
// Special case here, the patch can also be Alpha, Beta etc so we need
// to check if it's a string or not.
if (this.patch) {
version += (isNaN(+this.patch) ? ' ' : '.') + this.patch;
}
}
}
return version;
};
/**
* Outputs a JSON string of the Agent.
*
* @returns {String}
* @api public
*/
Agent.prototype.toJSON = function toJSON() {
return {
family: this.family
, major: this.major
, minor: this.minor
, patch: this.patch
, device: this.device
, os: this.os
};
};
/**
* The representation of a parsed Operating System.
*
* @constructor
* @param {String} family The name of the os
* @param {String} major Major version of the os
* @param {String} minor Minor version of the os
* @param {String} patch Patch version of the os
* @api public
*/
function OperatingSystem(family, major, minor, patch) {
this.family = family || 'Other';
this.major = major || '';
this.minor = minor || '';
this.patch = patch || '';
}
/**
* Generates a stringified version of the Operating System.
*
* @returns {String} "Operating System 0.0.0"
* @api public
*/
OperatingSystem.prototype.toString = function toString() {
var output = this.family
, version = this.toVersion();
if (version) output += ' '+ version;
return output;
};
/**
* Generates the version of the Operating System.
*
* @returns {String}
* @api public
*/
OperatingSystem.prototype.toVersion = function toVersion() {
var version = '';
if (this.major) {
version += this.major;
if (this.minor) {
version += '.' + this.minor;
// Special case here, the patch can also be Alpha, Beta etc so we need
// to check if it's a string or not.
if (this.patch) {
version += (isNaN(+this.patch) ? ' ' : '.') + this.patch;
}
}
}
return version;
};
/**
* Outputs a JSON string of the OS, values are defaulted to undefined so they
* are not outputed in the stringify.
*
* @returns {String}
* @api public
*/
OperatingSystem.prototype.toJSON = function toJSON(){
return {
family: this.family
, major: this.major || undefined
, minor: this.minor || undefined
, patch: this.patch || undefined
};
};
/**
* The representation of a parsed Device.
*
* @constructor
* @param {String} family The name of the os
* @api public
*/
function Device(family, major, minor, patch) {
this.family = family || 'Other';
this.major = major || '';
this.minor = minor || '';
this.patch = patch || '';
}
/**
* Generates a stringified version of the Device.
*
* @returns {String} "Device 0.0.0"
* @api public
*/
Device.prototype.toString = function toString() {
var output = this.family
, version = this.toVersion();
if (version) output += ' '+ version;
return output;
};
/**
* Generates the version of the Device.
*
* @returns {String}
* @api public
*/
Device.prototype.toVersion = function toVersion() {
var version = '';
if (this.major) {
version += this.major;
if (this.minor) {
version += '.' + this.minor;
// Special case here, the patch can also be Alpha, Beta etc so we need
// to check if it's a string or not.
if (this.patch) {
version += (isNaN(+this.patch) ? ' ' : '.') + this.patch;
}
}
}
return version;
};
/**
* Get string representation.
*
* @returns {String}
* @api public
*/
Device.prototype.toString = function toString() {
var output = this.family
, version = this.toVersion();
if (version) output += ' '+ version;
return output;
};
/**
* Outputs a JSON string of the Device, values are defaulted to undefined so they
* are not outputed in the stringify.
*
* @returns {String}
* @api public
*/
Device.prototype.toJSON = function toJSON() {
return {
family: this.family
, major: this.major || undefined
, minor: this.minor || undefined
, patch: this.patch || undefined
};
};
/**
* Small nifty thick that allows us to download a fresh set regexs from t3h
* Int3rNetz when we want to. We will be using the compiled version by default
* but users can opt-in for updates.
*
* @param {Boolean} refresh Refresh the dataset from the remote
* @api public
*/
module.exports = function updater() {
require('./lib/update')(function updating(err, results) {
if (err) return; // fail silently
regexps = results;
// OperatingSystem parsers:
osparsers = regexps.os;
osparserslength = osparsers.length;
// UserAgent parsers:
agentparsers = regexps.browser;
agentparserslength = agentparsers.length;
// Device parsers:
deviceparsers = regexps.device;
deviceparserslength = deviceparsers.length;
});
};
// Override the exports with our newly set module.exports
exports = module.exports;
/**
* Nao that we have setup all the different classes and configured it we can
* actually start assembling and exposing everything.
*/
exports.Device = Device;
exports.OperatingSystem = OperatingSystem;
exports.Agent = Agent;
/**
* Parses the user agent string with the generated parsers from the
* ua-parser project on google code.
*
* @param {String} userAgent The user agent string
* @param {String} jsAgent Optional UA from js to detect chrome frame
* @returns {Agent}
* @api public
*/
exports.parse = function parse(userAgent, jsAgent) {
if (!userAgent) return new Agent();
var length = agentparserslength
, parsers = agentparsers
, i = 0
, parser
, res;
for (; i < length; i++) {
if (res = parsers[i][0].exec(userAgent)) {
parser = parsers[i];
if (parser[1]) res[1] = parser[1].replace('$1', res[1]);
if (!jsAgent) return new Agent(
res[1]
, parser[2] || res[2]
, parser[3] || res[3]
, parser[4] || res[4]
, userAgent
);
break;
}
}
// Return early if we didn't find an match, but might still be able to parse
// the os and device, so make sure we supply it with the source
if (!parser || !res) return new Agent('', '', '', '', userAgent);
// Detect Chrome Frame, but make sure it's enabled! So we need to check for
// the Chrome/ so we know that it's actually using Chrome under the hood.
if (jsAgent && ~jsAgent.indexOf('Chrome/') && ~userAgent.indexOf('chromeframe')) {
res[1] = 'Chrome Frame (IE '+ res[1] +'.'+ res[2] +')';
// Run the JavaScripted userAgent string through the parser again so we can
// update the version numbers;
parser = parse(jsAgent);
parser[2] = parser.major;
parser[3] = parser.minor;
parser[4] = parser.patch;
}
return new Agent(
res[1]
, parser[2] || res[2]
, parser[3] || res[3]
, parser[4] || res[4]
, userAgent
);
};
/**
* If you are doing a lot of lookups you might want to cache the results of the
* parsed user agent string instead, in memory.
*
* @TODO We probably want to create 2 dictionary's here 1 for the Agent
* instances and one for the userAgent instance mapping so we can re-use simular
* Agent instance and lower our memory consumption.
*
* @param {String} userAgent The user agent string
* @param {String} jsAgent Optional UA from js to detect chrome frame
* @api public
*/
var dictionary = {};
exports.lookup = function lookup(userAgent, jsAgent) {
var key = (userAgent || '')+(jsAgent || '');
return dictionary[key] || (dictionary[key] = exports.parse(userAgent, jsAgent));
};
/**
* Does a more inaccurate but more common check for useragents identification.
* The version detection is from the jQuery.com library and is licensed under
* MIT.
*
* @param {String} useragent The user agent
* @returns {Object} matches
* @api public
*/
exports.is = function is(useragent) {
var ua = (useragent || '').toLowerCase()
, details = {
chrome: false
, firefox: false
, ie: false
, mobile_safari: false
, mozilla: false
, opera: false
, safari: false
, webkit: false
, version: (ua.match(exports.is.versionRE) || [0, "0"])[1]
};
if (~ua.indexOf('webkit')) {
details.webkit = true;
if (~ua.indexOf('chrome')) {
details.chrome = true;
} else if (~ua.indexOf('safari')) {
details.safari = true;
if (~ua.indexOf('mobile') && ~ua.indexOf('apple')) {
details.mobile_safari = true;
}
}
} else if (~ua.indexOf('opera')) {
details.opera = true;
} else if (~ua.indexOf('mozilla') && !~ua.indexOf('compatible')) {
details.mozilla = true;
if (~ua.indexOf('firefox')) details.firefox = true;
} else if (~ua.indexOf('msie')) {
details.ie = true;
}
return details;
};
/**
* Parses out the version numbers.
*
* @type {RegExp}
* @api private
*/
exports.is.versionRE = /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/;
/**
* Transform a JSON object back to a valid userAgent string
*
* @param {Object} details
* @returns {Agent}
*/
exports.fromJSON = function fromJSON(details) {
if (typeof details === 'string') details = JSON.parse(details);
var agent = new Agent(details.family, details.major, details.minor, details.patch)
, os = details.os;
// The device family was added in v2.0
if ('device' in details) {
agent.device = new Device(details.device.family);
} else {
agent.device = new Device();
}
if ('os' in details && os) {
// In v1.1.0 we only parsed out the Operating System name, not the full
// version which we added in v2.0. To provide backwards compatible we should
// we should set the details.os as family
if (typeof os === 'string') {
agent.os = new OperatingSystem(os);
} else {
agent.os = new OperatingSystem(os.family, os.major, os.minor, os.patch);
}
}
return agent;
};
/**
* Library version.
*
* @type {String}
* @api public
*/
exports.version = require('./package.json').version;

@@ -1,6 +0,7 @@

"use strict";
'use strict';
var request = require('request')
, yaml = require('yamlparser')
, path = require('path')
/**
* Build in Native modules.
*/
var path = require('path')
, fs = require('fs')

@@ -10,129 +11,149 @@ , vm = require('vm');

/**
* Fetches a new version of the user agent yaml
*
* @param {Function} fn Callback function
* @api public
* Third party modules.
*/
var request = require('request')
, yaml = require('yamlparser');
module.exports = function update (fn) {
var ua_parsers = []
, os_parsers = [];
exports.update = function update(callback) {
// Fetch the remote resource as that is frequently updated
request(exports.remote, function downloading(err, res, remote) {
if (err) return callback(err);
if (res.statusCode !== 200) return callback(new Error('Invalid statusCode returned'));
/**
* Parses the yaml file and adds to our ua / os parser array
*
* @param {String} yamlstring The string that needs to be extracted
* @returns {Boolean} Success
* @api private
*/
// Also get some local additions that are missing from the source
fs.readFile(exports.local, 'utf8', function reading(err, local) {
if (err) return callback(err);
function convert (yamlstring) {
// try to see if we can parse the yaml
var parsed_yaml;
// Parse the contents
exports.parse([ remote, local ], function parsing(err, results, source) {
callback(err, results);
try { parsed_yaml = yaml.eval(yamlstring); }
catch (e) { return false; }
if (!err && source) {
fs.writeFile(exports.output, source, function idk(err) {
if (err) {
console.error('Failed to save the generated file due to reasons', err);
}
});
}
});
});
});
};
// make sure we have a correct structure because we are reading this from
// a remote resource, they might change the structure of the JSON
if (
!('user_agent_parsers' in parsed_yaml)
|| !parsed_yaml.user_agent_parsers.length
|| !parsed_yaml.user_agent_parsers[0].regex
|| !('os_parsers' in parsed_yaml)
|| !parsed_yaml.os_parsers.length
|| !parsed_yaml.os_parsers[0].regex
) return false;
exports.parse = function parse(sources, callback) {
var results = {};
// \o/ working, now to create pre-compiled version of the regexps
// now we can try to generate a smaller file so it will take less memory
// once we load thing in memory
var agents = parsed_yaml.user_agent_parsers
, os = parsed_yaml.os_parsers
, parser, i, l;
sources.forEach(function parser(data) {
// Try to repair some of the odd structures that are in the yaml files
// before parsing it so we generate a uniform structure:
// small note about the odd syntax here, we need to JSON.stingify the
// regexp so that we are storing it as valid escaped string
// Normalize the Operating system versions:
data = data.replace(/os_v([1-3])_replacement/gim, function replace(match, version) {
return 'v'+ version +'_replacement';
});
// generate array of user agent parsers
for (i = 0, l = agents.length; i < l; i++) {
parser = 'r:new RegExp('+ JSON.stringify(agents[i].regex) + ')';
// Make sure that we are able to parse the yaml string
try { data = yaml.eval(data); }
catch (e) {
callback(e);
return callback = null;
}
// check for potential family replacements
if (agents[i].family_replacement)
parser += ', family:"' + agents[i].family_replacement + '"';
[
{ resource: 'user_agent_parsers', replacement: 'family_replacement' }
, { resource: 'device_parsers', replacement: 'device_replacement' }
, { resource: 'os_parsers', replacement: 'os_replacement' }
].forEach(function parsing(details) {
results[details.resource] = results[details.resource] || [];
if (agents[i].major_version_replacement)
parser += ', major: "' + agents[i].major_version_replacement + '"';
var resources = data[details.resource]
, resource
, parser;
ua_parsers.push('{' + parser + '}');
}
for (var i = 0, l = resources.length; i < l; i++) {
resource = resources[i];
// generate array of os parsers
for (i = 0, l = os.length; i < l; i++) {
parser = 'r:new RegExp('+ JSON.stringify(os[i].regex) + ')';
// We need to JSON stringify the data to properly add slashes escape other
// kinds of crap in the RegularExpression. If we don't do thing we get
// some illegal token warnings.
parser = 'new RegExp('+ JSON.stringify(resource.regex) + ')';
// check for potential os replacements
if (os[i].os_replacement)
parser += ', os:"' + os[i].os_replacement + '"';
// Check if we have replacement for the parsed family name
if (resource[details.replacement]) {
parser += ', "'+ resource[details.replacement].replace('"', '\\"') +'"';
} else {
parser += ', 0';
}
os_parsers.push('{' + parser + '}');
}
if (resource.v1_replacement) {
parser += ', "'+ resource.v1_replacement.replace('"', '\\"') +'"';
} else {
parser += ', 0';
}
agents.length = os.length = parsed_yaml = 0;
return true;
}
if (resource.v2_replacement) {
parser += ', "'+ resource.v2_replacement.replace('"', '\\"') +'"';
} else {
parser += ', 0';
}
/**
* The conversion of the files has been done, output the file and write it
*
* @api private
*/
if (resource.v3_replacement) {
parser += ', "'+ resource.v3_replacement.replace('"', '\\"') +'"';
} else {
parser += ', 0';
}
function output () {
// now that we have generated arrays of parsers we are gonna build up the
// file and make it happen.
var agentjs = 'exports.browser = ['+ ua_parsers.join(',') + '];';
agentjs += 'exports.os = [' + os_parsers.join(',') + '];';
results[details.resource].push('[ ' + parser + ' ]');
}
});
});
// parse down to javascript, we don't want to eval it because someone might
// have been naught with our internet and injected a require() statment in
// the codez, paranoid ftw
var sandbox = { exports: {} };
try { vm.runInNewContext(agentjs, sandbox, 'agentjs.vm'); }
catch (e) { return fn(e); }
// Generate a correct format
exports.generate(results, callback);
};
fn(null, sandbox.exports);
exports.generate = function generate(results, callback) {
var regexps = [
'exports.browser = [\n '+ results.user_agent_parsers.join('\n , ') +'\n];'
, 'exports.device = [\n '+ results.device_parsers.join('\n , ') +'\n];'
, 'exports.os = [\n '+ results.os_parsers.join('\n , ') +'\n];'
].join('\n\n');
// awesome, we just compiled our own library, time to write to file but do
// note that this should be last, as there couldd be compile errors
fs.writeFile(exports.output, agentjs, function (err) {
// we really don't care if we could write or not..
});
}
// Now that we have generated the structure for the RegExps export file we
// need to validate that we created a JavaScript compatible file, if we would
// write the file without checking it's content we could be breaking the
// module.
var sandbox = {
exports: {} // Emulate a module context, so everything is attached here
};
// fetch the remote database from the ua-parser google code project
request(exports.remote, function remoteResponse (err, res, data) {
if (err) return fn(err);
if (res.statusCode !== 200) return fn(new Error('incorrect status code'));
if (!convert(data)) return fn(new Error('unable to parse remote useragent db'));
// Crossing our fingers that it worked
try { vm.runInNewContext(regexps, sandbox, 'validating.vm'); }
catch (e) { return callback(e); }
// now that we parsed the remote file, do the same for the our custom local
// useragent database
fs.readFile(exports.local, 'utf8', function (err, data) {
if (err) return fn(err);
if (!convert(data)) return fn(new Error('unable to parse local useragent db'));
output();
});
});
callback(undefined, sandbox.exports, regexps);
};
/**
* Constants
* The location of the ua-parser regexes yaml file.
*
* @type {String}
* @api private
*/
exports.remote = 'https://raw.github.com/tobie/ua-parser/master/regexes.yaml';
exports.version = '0.0.1';
exports.remote = 'http://ua-parser.googlecode.com/svn/trunk/resources/user_agent_parser.yaml';
exports.local = path.join(__dirname, '..', 'static', 'user_agent.after.yaml');
exports.output = path.join(__dirname, '..', 'lib', 'agents.js');
/**
* The location of our local regexes yaml file.
*
* @type {String}
* @api private
*/
exports.local = path.resolve(__dirname, '..', 'static', 'user_agent.after.yaml');
/**
* The the output location for the generated regexps file
*
* @type {String}
* @api private
*/
exports.output = path.resolve(__dirname, '..', 'lib', 'regexps.js');
{
"name": "useragent"
, "version": "1.1.0"
, "description": "User-Agent string parser based on Browserscope.org algorithms for more browser reporting"
, "version": "2.0.0"
, "description": "Fastest, most accurate & effecient user agent string parser, uses browserscope's research for parsing"
, "author": "Arnout Kazemier"
, "main": "./index.js"
, "keywords":[ "user-agent", "useragent", "browserscope", "ua", "parser", "agent", "user agent"]
, "keywords":[
"agent"
, "browser"
, "browserscope"
, "os"
, "parse"
, "parser"
, "ua"
, "ua-parse"
, "ua-parser"
, "user agent"
, "user"
, "user-agent"
, "useragent"
, "version"
]
, "maintainers": [{

@@ -13,12 +28,6 @@ "name":"Arnout Kazemier"

}]
, "licenses": [
{
"type": "Apache-2.0"
, "url": "https://github.com/3rd-Eden/useragent/blob/master/LICENSE"
}
, {
"type": "MIT"
, "url": "https://github.com/3rd-Eden/useragent/blob/master/LICENSE"
}
]
, "license": {
"type": "MIT"
, "url": "https://github.com/3rd-Eden/useragent/blob/master/LICENSE"
}
, "repository": {

@@ -28,7 +37,2 @@ "type": "git"

}
, "dependencies": {
"yamlparser": "0.0.2"
, "request": "2.9.202"
, "semver": "1.0.14"
}
, "devDependencies": {

@@ -38,7 +42,9 @@ "should": "*"

, "long-stack-traces": "0.1.2"
, "yamlparser": "0.0.2"
, "request": "2.9.203"
, "semver": "1.0.14"
}
, "scripts": {
"update": "node ./bin/update.js"
, "test": "make test"
"test": "make test"
}
}

@@ -1,72 +0,64 @@

# useragent
# useragent - high performance user agent parser for Node.js
Current build status:[![BuildStatus](https://secure.travis-ci.org/3rd-Eden/useragent.png)](http://travis-ci.org/3rd-Eden/useragent)
Useragent originated as port of [browserscope.org][browserscope]'s user agent
parser project also known as ua-parser. Useragent allows you to parse user agent
string with high accuracy by using hand tuned dedicated regular expressions for
browser matching. This database is needed to ensure that every browser is
correctly parsed as every browser vendor implements it's own user agent schema.
This is why regular user agent parsers have major issues because they will
most likely parse out the wrong browser name or confuse the render engine version
with the actual version of the browser.
Useragent is a port of browserscope.org's user agent parser project which
allows you to parse user agent strings with high accuracy by using hand tuned
dedicated regular expressions for browser matching. Regular user agent parser
have major issues because they usually parse out the version number of the
render engine instead of the version number of the browser.
---
Because user agent parsing will always be like shooting a moving target because
browser vendors keep making subtle changes to them it's important to keep your
the regular expressions database up to date. When you install useragent it will
automatically download the latest regexp database from the ua-parser project
and transform it in to a dedicated node.js require statement. This way you will
always be up to date.
### Build status [![BuildStatus](https://secure.travis-ci.org/3rd-Eden/useragent.png?branch=master)](http://travis-ci.org/3rd-Eden/useragent)
But there few more tricks, so keep reading on until you hit the API section.
---
### Performance
### High performance
The 1.0 release of the useragent parser spots a major performance improvement
of the old parser, it also has a reduced memory level because we recompiled the
regex list to make a smaller impact.
The module has been developed with a benchmark driven approach. It has a
pre-compiled library that contains all the Regular Expressions and uses deferred
or on demand parsing for Operating System and device information. All this
engineering effort has been worth it as [this benchmark shows][benchmark]:
Some stats from the benchmark:
```
Starting the benchmark, parsing 62 useragent strings per run
> ```
> Starting the benchmark, parsing 47 useragent strings per run
> Executed benchmark (useragent2)
> Count (86), Cycles (96), Elapsed (5.028), Hz (1640.9779913574882)
>
> Executed benchmark (useragent1)
> Count (56), Cycles (96), Elapsed (5.03), Hz (1063.7910527195145)
> ```
Executed benchmark against node module: "useragent"
Count (49), Cycles (4), Elapsed (5.531), Hz (946.8647887795141)
As you can see that useragent 2 (1.0) is much faster then original parser. The
test above was executed on a iMac 2010 and Node.js v0.4.12. The code has been
optimized to take full advantage of the upcoming V8 crankshaft support in
node.js and you will another performance boost from it. Free performance boost,
so that is pretty bad ass.
Executed benchmark against node module: "useragent_parser"
Count (25), Cycles (2), Elapsed (5.359), Hz (463.63241573609633)
Executed benchmark against node module: "useragent-parser"
Count (13), Cycles (4), Elapsed (5.468), Hz (246.70547120951275)
Executed benchmark against node module: "ua-parser"
Count (13), Cycles (3), Elapsed (5.481), Hz (243.57388988788998)
Module: "useragent" is the user agent fastest parser.
```
---
### Installation
Installation is done using the Node Package Manager (npm). If you don't have
npm installed on your system you can download it from
[npmjs.org](http://npmjs.org)
Installation is done using the Node Package Manager (NPM). If you don't have
NPM installed on your system you can download it from
[npmjs.org][npm]
```
npm install useragent
npm install useragent --save
```
### Migrating from 0.1.2 to 1.0.0
The `--save` flag tells NPM to automatically add it to your `package.json` file.
If you are upgrading from 0.1.2 to 1.0.0 these changes will affect your:
---
* `useragent.browser(ua)` has been renamed to `useragent.is(ua)`.
* `useragent.parser(ua, jsua)` has been renamed to `useragent.parse(ua, jsua)`.
* `result.pretty()` has been renamed to `result.toAgent()`.
* `result.V1` has been renamed to `result.major`.
* `result.V2` has been renamed to `result.minor`.
* `result.V3` has been renamed to `result.patch`.
* `result.prettyOS()` has been removed.
* `result.match` has been removed.
### API
For more detailed information, check out the API below.
### API / Working the codez
Include the `useragent` parser in you node.js application:
Include the useragent parser in you node.js application:
```js

@@ -76,6 +68,6 @@ var useragent = require('useragent');

The useragent library allows you do use the automatically installed regex
The `useragent` library allows you do use the automatically installed RegExp
library or you can fetch it live from the remote servers. So if you are
paranoid and always want your regex library to be up to date to match with
agent the widest range of useragent strings you can do:
paranoid and always want your RegExp library to be up to date to match with
agent the widest range of `useragent` strings you can do:

@@ -89,38 +81,4 @@ ```js

JavaScript supported format. If it fails to compile or load it from the remote
location it will just fallback silently to the shipped version.
location it will just fall back silently to the shipped version.
But there are more ways to update your regex library, when the useragent is
installed on your system we automatically start a update process to see if the
shipped version is out of date or not, if we can fetch one we will store that
one instead so you have latest version available for you when you install.
In addition to this, doing a `npm update useragent` should also re-fetch the
library for you. So many ways to stay up to date ;).
#### useragent.is(useragent string).browsername;
This api provides you with a quick and dirty browser lookup. The underlying
code is usually found on client side scripts so it's not the same quality as
our parse method but it does the job.
`useragent.is` returns a object with potential matched browser names
```js
useragent.is(req.headers['user-agent']).firefox // true
useragent.is(req.headers['user-agent']).safari // false
var ua = useragent.is(req.headers['user-agent'])
// the object
{
version: '3'
webkit: false
opera: false
ie: false
chrome: false
safari: false
mobile_safari: false
firefox: true
}
```
#### useragent.parse(useragent string[, js useragent]);

@@ -130,7 +88,7 @@

The function accepts 2 arguments, both should be a `string`. The first argument
should the useragent string that is known on the server from the
should the user agent string that is known on the server from the
`req.headers.useragent` header. The other argument is optional and should be
the useragent string that you see in the browser, this can be send from the
the user agent string that you see in the browser, this can be send from the
browser using a xhr request or something like this. This allows you detect if
the user is browsing the web using the `Chrome Frame` plugin.
the user is browsing the web using the `Chrome Frame` extension.

@@ -148,7 +106,11 @@ The parser returns a Agent instance, this allows you to output user agent

The parse method returns a `Agent` instance which contains all details about the
user agent. See the Agent section of the API documentation for the available
methods.
#### useragent.lookup(useragent string[, js useragent]);
This provides the same functionality as above, but it caches the useragent
This provides the same functionality as above, but it caches the user agent
string and it's parsed result in memory to provide faster lookups in the
future. This can be handly if you expect to parse a lot of user agent strings.
future. This can be handy if you expect to parse a lot of user agent strings.

@@ -158,9 +120,24 @@ It uses the same arguments as the `useragent.parse` method and returns exactly

#### useragent.fromString(agent.toString());
```js
var agent = useragent.lookup(req.headers['user-agent']);
```
Parses the result of `agent.toString()` back to a new Agent instance.
And this is a serious performance improvement as shown in this benchmark:
```
Executed benchmark against method: "useragent.parse"
Count (49), Cycles (3), Elapsed (5.534), Hz (947.6844321931629)
Executed benchmark against method: "useragent.lookup"
Count (11758), Cycles (3), Elapsed (5.395), Hz (229352.03831239208)
```
#### useragent.fromJSON(obj);
Transforms the JSON representation of a `Agent` instance back in to a working
`Agent` instance
```js
var agent = useragent.parse(req.headers['user-agent'])
, another = useragent.fromString(agent.toString());
, another = useragent.fromJSON(JSON.stringify(agent));

@@ -170,34 +147,58 @@ console.log(agent == another);

#### useragent.fromJSON(agent.toJSON());
#### useragent.is(useragent string).browsername;
Parses the result of `agent.toJSON()` back to a new Agent instance.
This api provides you with a quick and dirty browser lookup. The underlying
code is usually found on client side scripts so it's not the same quality as
our `useragent.parse` method but it might be needed for legacy reasons.
`useragent.is` returns a object with potential matched browser names
```js
var agent = useragent.parse(req.headers['user-agent'])
, another = useragent.fromJSON(agent.toJSON());
useragent.is(req.headers['user-agent']).firefox // true
useragent.is(req.headers['user-agent']).safari // false
var ua = useragent.is(req.headers['user-agent'])
console.log(agent == another);
// the object
{
version: '3'
webkit: false
opera: false
ie: false
chrome: false
safari: false
mobile_safari: false
firefox: true
}
```
### Agents
---
Most of the methods mentioned above return a Agent instance. This Agent exposes
### Agents, OperatingSystem and Device instances
Most of the methods mentioned above return a Agent instance. The Agent exposes
the parsed out information from the user agent strings. This allows us to
extend the agent with more methods that do not nessesarly need to be in the
extend the agent with more methods that do not necessarily need to be in the
core agent instance, allowing us to expose a plugin interface for third party
developers.
developers and at the same time create a uniform interface for all versioning.
You can read out the following properties:
The Agent has the following property
* `family` The browser family, or browser name, it defaults to Other.
* `major` The major version number of the family, it defaults to 0.
* `minor` The minor version number of the family, it defaults to 0.
* `patch` The patch version number of the family, it defaults to 0.
* `os` The operating system of the user, it defaults to Other.
- `family` The browser family, or browser name, it defaults to Other.
- `major` The major version number of the family, it defaults to 0.
- `minor` The minor version number of the family, it defaults to 0.
- `patch` The patch version number of the family, it defaults to 0.
While most version number information is a string, I have chosen to present
them all as string because a browser can also be identified as beta or alpha.
When the family name or os can not be determined we will default to the string
Other.
In addition to the properties mentioned above, it also has 2 special properties,
which are:
- `os` OperatingSystem instance
- `device` Device instance
When you access those 2 properties the agent will do on demand parsing of the
Operating System or/and Device information.
The OperatingSystem has the same properties as the Agent, for the Device we
don't have any versioning information available, so only the `family` property is
set there. If we cannot find the family, they will default to `Other`.
The following methods are available:

@@ -222,9 +223,8 @@

var agent = useragent.parse(req.headers['user-agent']);
agent.toString(); // 'Chrome 15.0.874 / Mac OS X'
agent.toString(); // 'Chrome 15.0.874 / Mac OS X 10.8.1'
// as it's a to string method you can also concat it with another string
'your useragent is ' + agent; // 'your useragent is Chrome 15.0.874 / Mac OS X'
'your useragent is ' + agent;
// 'your useragent is Chrome 15.0.874 / Mac OS X 10.8.1'
```
#### Agent.toVersion();

@@ -241,17 +241,82 @@

Generates a stringified JSON output of the agent, this can later be used again
to create a new Agent instance using the `useragent.fromJSON` method.
Generates a JSON representation of the Agent. By using the `toJSON` method we
automatically allow it to be stringified when supplying as to the
`JSON.stringify` method.
```js
var agent = useragent.parse(req.headers['user-agent']);
agent.toJSON(); //'{"family":"Chrome","major":"15","minor":"0","patch":"874","os":"Mac OS X"}'
agent.toJSON(); // returns an object
JSON.stringify(agent);
```
### Adding more features to the Agent
#### OperatingSystem.toString();
As I wanted to keep the core of the useragent parser as clean and fast as
possible I decided to move some of the initally planned features to a new
Generates a stringified version of operating system;
```js
var agent = useragent.parse(req.headers['user-agent']);
agent.os.toString(); // 'Mac OSX 10.8.1'
```
#### OperatingSystem.toVersion();
Generates a stringified version of operating system's version;
```js
var agent = useragent.parse(req.headers['user-agent']);
agent.os.toVersion(); // '10.8.1'
```
#### OperatingSystem.toJSON();
Generates a JSON representation of the OperatingSystem. By using the `toJSON`
method we automatically allow it to be stringified when supplying as to the
`JSON.stringify` method.
```js
var agent = useragent.parse(req.headers['user-agent']);
agent.os.toJSON(); // returns an object
JSON.stringify(agent.os);
```
#### Device.toString();
Generates a stringified version of device;
```js
var agent = useragent.parse(req.headers['user-agent']);
agent.device.toString(); // 'Asus A100'
```
#### Device.toVersion();
Generates a stringified version of device's version;
```js
var agent = useragent.parse(req.headers['user-agent']);
agent.device.toVersion(); // '' , no version found but could also be '0.0.0'
```
#### Device.toJSON();
Generates a JSON representation of the Device. By using the `toJSON` method we
automatically allow it to be stringified when supplying as to the
`JSON.stringify` method.
```js
var agent = useragent.parse(req.headers['user-agent']);
agent.device.toJSON(); // returns an object
JSON.stringify(agent.device);
```
### Adding more features to the useragent
As I wanted to keep the core of the user agent parser as clean and fast as
possible I decided to move some of the initially planned features to a new
`plugin` file.
These extenstions to the Agent prototype can be loaded by requiring the
These extensions to the Agent prototype can be loaded by requiring the
`useragent/features` file:

@@ -264,3 +329,3 @@

The inital release introduces 1 new method, satisfies, which allows you to see
The initial release introduces 1 new method, satisfies, which allows you to see
if the version number of the browser satisfies a certain range. It uses the

@@ -279,2 +344,9 @@ semver library to do all the range calculations but here is a small summary of

As it requires the `semver` module to function you need to install it
seperately:
```
npm install semver --save
```
#### Agent.satisfies('range style here');

@@ -289,1 +361,36 @@

```
---
### Migrations
For small changes between version please review the [changelog][changelog].
#### Upgrading from 1.10 to 2.0.0
- `useragent.fromAgent` has been removed.
- `agent.toJSON` now returns an Object, use `JSON.stringify(agent)` for the old
behaviour.
- `agent.os` is now an `OperatingSystem` instance with version numbers. If you
still a string only representation do `agent.os.toString()`.
#### Upgrading from 0.1.2 to 1.0.0
- `useragent.browser(ua)` has been renamed to `useragent.is(ua)`.
- `useragent.parser(ua, jsua)` has been renamed to `useragent.parse(ua, jsua)`.
- `result.pretty()` has been renamed to `result.toAgent()`.
- `result.V1` has been renamed to `result.major`.
- `result.V2` has been renamed to `result.minor`.
- `result.V3` has been renamed to `result.patch`.
- `result.prettyOS()` has been removed.
- `result.match` has been removed.
---
### License
MIT
[browserscope]: http://www.browserscope.org/
[benchmark]: /3rd-Eden/useragent/blob/master/benchmark/run.js
[changelog]: /3rd-Eden/useragent/blob/master/CHANGELOG.md
[npm]: http://npmjs.org

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc