Skip to content

Fix parsing Issue 63 https://github.com/3rd-Eden/useragent/issues/63 #79

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 54 additions & 25 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ var agentparsers = regexps.browser
var deviceparsers = regexps.device
, deviceparserslength = deviceparsers.length;

// Default for unknown version
var UnknownFamily = 'Other';
var UnknownVersion = '0';

/**
* The representation of a parsed user agent.
*
Expand All @@ -32,13 +36,29 @@ var deviceparsers = regexps.device
* @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.family = family || UnknownFamily;
this.major = major || UnknownVersion;
this.minor = minor || UnknownVersion;
this.patch = patch || UnknownVersion;
this.source = source || '';
}

function replaceRegExResult(matches, templates) {
var r = [];
var m = matches.map(function(v){ v = typeof v === 'string' ? v.replace(/\&/g, '&&').replace(/\$/g, '&D') : v; });
for(var t=1; t<=4; t++) { // template[1..4]
var tmpl = templates[t];
r[t] = tmpl[0];
if (typeof r[t] === 'string') {
tmpl[1].forEach(function(i){
r[t] = r[t].replace(new RegExp('\\$'+i, 'g'), matches[i] != undefined ? matches[i] : '');
});
r[t] = r[t].replace(/&D/g, '$').replace(/&&/g, '&');
}
}
return r;
}

/**
* OnDemand parsing of the Operating System.
*
Expand All @@ -58,7 +78,7 @@ Object.defineProperty(Agent.prototype, 'os', {
if (res = parsers[i][0].exec(userAgent)) {
parser = parsers[i];

if (parser[1]) res[1] = parser[1].replace('$1', res[1]);
res = replaceRegExResult(res, parser);
break;
}
}
Expand All @@ -68,9 +88,9 @@ Object.defineProperty(Agent.prototype, 'os', {
? new OperatingSystem()
: new OperatingSystem(
res[1]
, parser[2] || res[2]
, parser[3] || res[3]
, parser[4] || res[4]
, res[2]
, res[3]
, res[4]
)
}).os;
},
Expand Down Expand Up @@ -109,7 +129,7 @@ Object.defineProperty(Agent.prototype, 'device', {
if (res = parsers[i][0].exec(userAgent)) {
parser = parsers[i];

if (parser[1]) res[1] = parser[1].replace('$1', res[1]);
res = replaceRegExResult(res, parser);
break;
}
}
Expand All @@ -119,9 +139,9 @@ Object.defineProperty(Agent.prototype, 'device', {
? new Device()
: new Device(
res[1]
, parser[2] || res[2]
, parser[3] || res[3]
, parser[4] || res[4]
, res[2]
, res[3]
, res[4]
)
}).device;
},
Expand Down Expand Up @@ -161,7 +181,7 @@ Agent.prototype.toAgent = function toAgent() {
*/
Agent.prototype.toString = function toString() {
var agent = this.toAgent()
, os = this.os !== 'Other' ? this.os : false;
, os = this.os !== UnknownFamily ? this.os : false;

return agent + (os ? ' / ' + os : '');
};
Expand Down Expand Up @@ -220,10 +240,10 @@ Agent.prototype.toJSON = function toJSON() {
* @api public
*/
function OperatingSystem(family, major, minor, patch) {
this.family = family || 'Other';
this.major = major || '0';
this.minor = minor || '0';
this.patch = patch || '0';
this.family = family || UnknownFamily;
this.major = major || UnknownVersion;
this.minor = minor || UnknownVersion;
this.patch = patch || UnknownVersion;
}

/**
Expand Down Expand Up @@ -293,10 +313,10 @@ OperatingSystem.prototype.toJSON = function toJSON(){
* @api public
*/
function Device(family, major, minor, patch) {
this.family = family || 'Other';
this.major = major || '0';
this.minor = minor || '0';
this.patch = patch || '0';
this.family = family || UnknownFamily;
this.major = major || UnknownVersion;
this.minor = minor || UnknownVersion;
this.patch = patch || UnknownVersion;
}

/**
Expand Down Expand Up @@ -427,12 +447,12 @@ exports.parse = function parse(userAgent, jsAgent) {
if (res = parsers[i][0].exec(userAgent)) {
parser = parsers[i];

if (parser[1]) res[1] = parser[1].replace('$1', res[1]);
res = replaceRegExResult(res, parser);
if (!jsAgent) return new Agent(
res[1]
, parser[2] || res[2]
, parser[3] || res[3]
, parser[4] || res[4]
, res[2]
, res[3]
, res[4]
, userAgent
);

Expand Down Expand Up @@ -584,6 +604,15 @@ exports.fromJSON = function fromJSON(details) {
return agent;
};


exports.setUnknownFamilyString = function setUnknownFamilyString(s) {
UnknownFamily = s;
}

exports.setUnknownVersionString = function setUnknownVersionString(s) {
UnknownVersion = s;
}

/**
* Library version.
*
Expand Down
73 changes: 41 additions & 32 deletions lib/update.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,35 @@ exports.update = function update(callback) {
});
};

function getReplacement(field, resource, fallback, lastRepl) {
lastRepl = lastRepl || {};
var usedMap = lastRepl.usedMap || {};

var used = [];
if (usedMap[fallback]) {
fallback = '';
} else {
used = [fallback.replace('$', '')];
}

if (! field)
return { text: fallback, used: used, usedMap: usedMap };
if (Array.isArray(field))
field = field.filter(function(f){ return resource[f] })[0];
if (! field)
return { text: fallback, used: used, usedMap: usedMap };
if (resource[field] == undefined)
return { text: fallback, used: used, usedMap: usedMap };

var t = resource[field];
var d = {};
var m = t.match(/\$\d/g);
if (m) m.forEach(function(m) { usedMap[m] = true; m = m.replace('$',''); d[m] = true; });
used = Object.keys(d).sort(function(a,b){ return a-b; });

return { text: t, used: used, usedMap: usedMap };
}

/**
* Parse the given sources.
*
Expand Down Expand Up @@ -93,17 +122,17 @@ exports.parse = function parse(sources, callback) {
[
{
resource: 'user_agent_parsers'
, replacement: 'family_replacement'
, replacement: ['family_replacement', 'v1_replacement', 'v2_replacement', 'v3_replacement' ]
, name: 'browser'
}
, {
resource: 'device_parsers'
, replacement: 'device_replacement'
, replacement: [['brand_replacement', 'device_replacement' ], 'model_replacement'] //
, name: 'device'
}
, {
resource: 'os_parsers'
, replacement: 'os_replacement'
, replacement: ['os_replacement', 'os_v1_replacement', 'os_v2_replacement' ]
, name: 'os'
}
].forEach(function parsing(details) {
Expand All @@ -120,41 +149,21 @@ exports.parse = function parse(sources, callback) {
// 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.
var rflags = resource.regex_flag || '';
if (rflags) rflags = ", '" + rflags + "'";
parser = 'parser = Object.create(null);\n';
parser += 'parser[0] = new RegExp('+ JSON.stringify(resource.regex) + ');\n';
parser += 'parser[0] = new RegExp('+ JSON.stringify(resource.regex) + rflags + ');\n';

// Check if we have replacement for the parsed family name
if (resource[details.replacement]) {
parser += 'parser[1] = "'+ resource[details.replacement].replace('"', '\\"') +'";';
} else {
parser += 'parser[1] = 0;';
}

parser += '\n';

if (resource.v1_replacement) {
parser += 'parser[2] = "'+ resource.v1_replacement.replace('"', '\\"') +'";';
} else {
parser += 'parser[2] = 0;';
}

parser += '\n';

if (resource.v2_replacement) {
parser += 'parser[3] = "'+ resource.v2_replacement.replace('"', '\\"') +'";';
} else {
parser += 'parser[3] = 0;';
}

parser += '\n';
var r = {};
for(var j = 0; j < 4; j++) {
r = getReplacement(details.replacement[j], resource, '$'+(j+1), r);
var used = r.used.join(', ');
parser += 'parser[' + (j+1) + '] = ["'+ r.text.replace('"', '\\"') +'", [' + used + ']];';

if (resource.v3_replacement) {
parser += 'parser[4] = "'+ resource.v3_replacement.replace('"', '\\"') +'";';
} else {
parser += 'parser[4] = 0;';
parser += '\n';
}

parser += '\n';
parser += 'exports.'+ details.name +'['+ i +'] = parser;';
results[details.resource].push(parser);
}
Expand Down