first commit

This commit is contained in:
armansansd
2023-07-12 16:31:19 +01:00
commit 3424b1927b
23306 changed files with 2217776 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
language: node_js
node_js:
- '8'
- '6'
+73
View File
@@ -0,0 +1,73 @@
## Version 2.0
* __v2.3__
- Updated dependencies
* __v2.1__
- Default all values to minor,patch and major fields to 0 by default.
* __v2.0.x__
- Parse file updates.
* __v2.0.0__ *breaking*
- Added support for Operating System version parsing
- Added support for Device parsing
- Introduced deferred OnDemand parsing for Operating and Devices
- The `Agent#toJSON` method now returns an object instread of JSON string. Use
`JSON.stringify(agent)` instead.
- Removed the fromAgent method
- semver is removed from the dependencies, if you use the useragent/features
you should add it to your own dependencies.
* __v2.0.1__
- Fixed broken reference to the update module.
- Updated with some new parsers.
* __v2.0.2__
- Use LRU-cache for the lookups so it doesn't create a memory "leak" #22
- Updated with some new parsers.
* __v2.0.3__
- Updated regexp library with new parsers as Opera's latest browser which runs
WebKit was detected as Chrome Mobile.
* __v2.0.4__
- Added support for IE11 and PhantomJS. In addition to that when you run the
updater without the correct dependencies it will just output an error
instead of throwing an error.
* __v2.0.5__
- Upgraded the regular expressions to support Opera Next
* __v2.0.6__
- Only write the parse file when there isn't an error. #30
- Output an error in the console when we fail to compile new parsers #30
## Version 1.0
* __v1.1.0__
- Removed the postupdate hook, it was causing to much issues #9
* __v1.0.6__
- Updated the agent parser, JHint issues and leaking globals.
* __v1.0.5__
- Potential fix for #11 where it doesn't install the stuff in windows this also
brings a fresh update of the agents.js.
* __v1.0.3__
- Rewritten the `is` method so it doesn't display IE as true for firefox, chrome
etc fixes #10 and #7.
* __v1.0.3__
- A fix for bug #6, updated the semver dependency for browserify support.
* __v1.0.2__
- Don't throw errors when .parse is called without a useragent string. It now
defaults to a empty Agent instance.
* __v1.0.1__
- Added support for cURL, Wget and thunderbird using a custom useragent
definition file.
* __v1.0.0__ *breaking*
- Complete rewrite of the API and major performance improvements.
Generated Vendored Executable
+16
View File
@@ -0,0 +1,16 @@
The regex library that the useragent parser uses if from; code.google.com/p/ua-parser/
which is released under Apache license:
# Copyright 2009 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
Generated Vendored Executable
+19
View File
@@ -0,0 +1,19 @@
# MIT LICENSED Copyright (c) 2013 Arnout Kazemier (http://3rd-Eden.com)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
+395
View File
@@ -0,0 +1,395 @@
# useragent - high performance user agent parser for Node.js
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.
---
### Build status [![BuildStatus](https://secure.travis-ci.org/3rd-Eden/useragent.png?branch=master)](http://travis-ci.org/3rd-Eden/useragent)
---
### High performance
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]:
```
Starting the benchmark, parsing 62 useragent strings per run
Executed benchmark against node module: "useragent"
Count (61), Cycles (5), Elapsed (5.559), Hz (1141.3739447904327)
Executed benchmark against node module: "useragent_parser"
Count (29), Cycles (3), Elapsed (5.448), Hz (545.6817291171243)
Executed benchmark against node module: "useragent-parser"
Count (16), Cycles (4), Elapsed (5.48), Hz (304.5373431830105)
Executed benchmark against node module: "ua-parser"
Count (54), Cycles (3), Elapsed (5.512), Hz (1018.7561434659247)
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][npm]
```
npm install useragent --save
```
The `--save` flag tells NPM to automatically add it to your `package.json` file.
---
### API
Include the `useragent` parser in you node.js application:
```js
var useragent = require('useragent');
```
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 RegExp library to be up to date to match with
agent the widest range of `useragent` strings you can do:
```js
var useragent = require('useragent');
useragent(true);
```
This will async load the database from the server and compile it to a proper
JavaScript supported format. If it fails to compile or load it from the remote
location it will just fall back silently to the shipped version. If you want to
use this feature you need to add `yamlparser` and `request` to your package.json
```
npm install yamlparser --save
npm install request --save
```
#### useragent.parse(useragent string[, js useragent]);
This is the actual user agent parser, this is where all the magic is happening.
The function accepts 2 arguments, both should be a `string`. The first argument
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 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` extension.
The parser returns a Agent instance, this allows you to output user agent
information in different predefined formats. See the Agent section for more
information.
```js
var agent = useragent.parse(req.headers['user-agent']);
// example for parsing both the useragent header and a optional js useragent
var agent2 = useragent.parse(req.headers['user-agent'], req.query.jsuseragent);
```
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 user agent
string and it's parsed result in memory to provide faster lookups in the
future. This can be handy if you expect to parse a lot of user agent strings.
It uses the same arguments as the `useragent.parse` method and returns exactly
the same result, but it's just cached.
```js
var agent = useragent.lookup(req.headers['user-agent']);
```
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.fromJSON(JSON.stringify(agent));
console.log(agent == another);
```
#### 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 `useragent.parse` method but it might be needed for legacy reasons.
`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
mozilla: true
android: false
}
```
---
### 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 necessarily need to be in the
core agent instance, allowing us to expose a plugin interface for third party
developers and at the same time create a uniform interface for all versioning.
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.
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:
#### Agent.toAgent();
Returns the family and version number concatinated in a nice human readable
string.
```js
var agent = useragent.parse(req.headers['user-agent']);
agent.toAgent(); // 'Chrome 15.0.874'
```
#### Agent.toString();
Returns the results of the `Agent.toAgent()` but also adds the parsed operating
system to the string in a human readable format.
```js
var agent = useragent.parse(req.headers['user-agent']);
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 10.8.1'
```
#### Agent.toVersion();
Returns the version of the browser in a human readable string.
```js
var agent = useragent.parse(req.headers['user-agent']);
agent.toVersion(); // '15.0.874'
```
#### Agent.toJSON();
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(); // returns an object
JSON.stringify(agent);
```
#### OperatingSystem.toString();
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 extensions to the Agent prototype can be loaded by requiring the
`useragent/features` file:
```js
var useragent = require('useragent');
require('useragent/features');
```
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
semver library to do all the range calculations but here is a small summary of
the supported range styles:
* `>1.2.3` Greater than a specific version.
* `<1.2.3` Less than.
* `1.2.3 - 2.3.4` := `>=1.2.3 <=2.3.4`.
* `~1.2.3` := `>=1.2.3 <1.3.0`.
* `~1.2` := `>=1.2.0 <2.0.0`.
* `~1` := `>=1.0.0 <2.0.0`.
* `1.2.x` := `>=1.2.0 <1.3.0`.
* `1.x` := `>=1.0.0 <2.0.0`.
As it requires the `semver` module to function you need to install it
seperately:
```
npm install semver --save
```
#### Agent.satisfies('range style here');
Check if the agent matches the supplied range.
```js
var agent = useragent.parse(req.headers['user-agent']);
agent.satisfies('15.x || >=19.5.0 || 25.0.0 - 17.2.3'); // true
agent.satisfies('>16.12.0'); // false
```
---
### 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()`.
- `semver` has been removed from the dependencies, so if you are using the
`require('useragent/features')` you need to add it to your own dependencies
#### 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
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env node
var request = require('request')
, path = require('path')
, fs = require('fs');
var files = {
'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'
};
/**
* Update the fixtures
*/
Object.keys(files).forEach(function (key) {
request(files[key], function response (err, res, data) {
if (err || res.statusCode !== 200) return console.error('failed to update');
console.log('downloaded', files[key]);
fs.writeFileSync(path.join(__dirname, '..', 'tests', 'fixtures', key), data);
});
});
+17
View File
@@ -0,0 +1,17 @@
#!/usr/bin/env node
'use strict';
/**
* 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);
return;
}
console.log('Successfully fetched and generated new parsers from the internets.');
});
+19
View File
@@ -0,0 +1,19 @@
"use strict";
/**
* Plugin dependencies
*/
var Agent = require('../').Agent
, semver = require('semver');
/**
* Checks if the user agent's version can be satisfied agents the give
* ranged argument. This uses the semver libraries range construction.
*
* @param {String} ranged The range the version has to satisfie
* @returns {Boolean}
* @api public
*/
Agent.prototype.satisfies = function satisfies (range) {
return semver.satisfies((Number(this.major) || 0) + '.' + (Number(this.minor) || 0) + '.' + (Number(this.patch) || 0), range);
};
+621
View File
@@ -0,0 +1,621 @@
'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 || '0';
this.minor = minor || '0';
this.patch = patch || '0';
}
/**
* 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 device
* @param {String} major Major version of the device
* @param {String} minor Minor version of the device
* @param {String} patch Patch version of the device
* @api public
*/
function Device(family, major, minor, patch) {
this.family = family || 'Other';
this.major = major || '0';
this.minor = minor || '0';
this.patch = patch || '0';
}
/**
* 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;
};
/**
* 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() {
try {
require('./lib/update').update(function updating(err, results) {
if (err) {
console.log('[useragent] Failed to update the parsed due to an error:');
console.log('[useragent] '+ (err.message ? err.message : err));
return;
}
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;
});
} catch (e) {
console.error('[useragent] If you want to use automatic updating, please add:');
console.error('[useragent] - request (npm install request --save)');
console.error('[useragent] - yamlparser (npm install yamlparser --save)');
console.error('[useragent] To your own package.json');
}
};
// 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;
/**
* Check if the userAgent is something we want to parse with regexp's.
*
* @param {String} userAgent The userAgent.
* @returns {Boolean}
*/
function isSafe(userAgent) {
var consecutive = 0
, code = 0;
for (var i = 0; i < userAgent.length; i++) {
code = userAgent.charCodeAt(i);
// numbers between 0 and 9, letters between a and z
if ((code >= 48 && code <= 57) || (code >= 97 && code <= 122)) {
consecutive++;
} else {
consecutive = 0;
}
if (consecutive >= 100) {
return false;
}
}
return true
}
/**
* 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 || !isSafe(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 LRU = require('lru-cache')(5000);
exports.lookup = function lookup(userAgent, jsAgent) {
var key = (userAgent || '')+(jsAgent || '')
, cached = LRU.get(key);
if (cached) return cached;
LRU.set(key, (cached = exports.parse(userAgent, jsAgent)));
return cached;
};
/**
* 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
, android: false
, version: (ua.match(exports.is.versionRE) || [0, "0"])[1]
};
if (~ua.indexOf('webkit')) {
details.webkit = true;
if (~ua.indexOf('android')){
details.android = 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('trident') || ~ua.indexOf('msie')) {
details.ie = true;
} else if (~ua.indexOf('mozilla') && !~ua.indexOf('compatible')) {
details.mozilla = true;
if (~ua.indexOf('firefox')) details.firefox = 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;
+16
View File
@@ -0,0 +1,16 @@
/**
* TL;DR: Don't touch this.
*
* DO NOT EDIT THIS FILE, IT IS AUTOMATICALLY GENERATED AND COMPILED FROM AN
* EXTERNAL REGEXPS DATABASE. TO UPDATE THIS FILE MANUALLY PLEASE RUN THE:
*
* ```
* npm run update
* ```
*
* COMMAND FROM THE ROOT OF THIS REPOSITORY. IF YOU FOUND A REGULAR EXPRESSION THAT
* IS BROKEN OR MISSING. PLEASE MAKE THE PULL REQUESTS TO REGEXP DATABASE NOT TO
* THIS MODULE AS THEY WILL BE REJECTED INSTANTLY. THE SOURCE FILE LOCATION CAN
* BE FOUND IN THE `update.js` FILE WHICH IS IN THE SAME FOLDER AS THIS FILE. PLEASE
* SEE THE `exports.remote` PART OF THE FILE.
*/
+6952
View File
File diff suppressed because it is too large Load Diff
+249
View File
@@ -0,0 +1,249 @@
'use strict';
/**
* Build in Native modules.
*/
var path = require('path')
, fs = require('fs')
, vm = require('vm')
, tmp = require('tmp');
/**
* Third party modules.
*/
var request = require('request')
, yaml = require('yamlparser');
/**
* Update the regexp.js file
*
* @param {Function} callback Completion callback.
* @api public
*/
exports.update = function update(callback) {
// Prepend local additions that are missing from the source
fs.readFile(exports.before, 'utf8', function reading(err, before) {
if (err) return callback(err);
// 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'));
// Append get some local additions that are missing from the source
fs.readFile(exports.after, 'utf8', function reading(err, after) {
if (err) return callback(err);
// Parse the contents
exports.parse([ before, remote, after ], function parsing(err, results, source) {
callback(err, results);
if (!source || err) return;
//
// Save to a tmp file to avoid potential concurrency issues.
//
tmp.file(function (err, tempFilePath) {
if (err) return;
fs.writeFile(tempFilePath, source, function idk(err) {
if (err) return
fs.rename(tempFilePath, exports.output, function(err) {
});
});
});
});
});
});
});
};
/**
* Parse the given sources.
*
* @param {Array} sources String versions of the source
* @param {Function} callback completion callback
* @api public
*/
exports.parse = function parse(sources, callback) {
var results = {};
var data = sources.reduce(function parser(memo, data) {
// Try to repair some of the odd structures that are in the yaml files
// before parsing it so we generate a uniform structure:
// Normalize the Operating system versions:
data = data.replace(/os_v([1-3])_replacement/gim, function replace(match, version) {
return 'v'+ version +'_replacement';
});
// Make sure that we are able to parse the yaml string
try { data = yaml.eval(data); }
catch (e) {
callback(e);
callback = null;
return memo;
}
// merge the data with the memo;
Object.keys(data).forEach(function (key) {
var results = data[key];
memo[key] = memo[key] || [];
for (var i = 0, l = results.length; i < l; i++) {
memo[key].push(results[i]);
}
});
return memo;
}, {});
[
{
resource: 'user_agent_parsers'
, replacement: 'family_replacement'
, name: 'browser'
}
, {
resource: 'device_parsers'
, replacement: 'device_replacement'
, name: 'device'
}
, {
resource: 'os_parsers'
, replacement: 'os_replacement'
, name: 'os'
}
].forEach(function parsing(details) {
results[details.resource] = results[details.resource] || [];
var resources = data[details.resource]
, name = details.resource.replace('_parsers', '')
, resource
, parser;
for (var i = 0, l = resources.length; i < l; i++) {
resource = resources[i];
// 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 = 'parser = Object.create(null);\n';
parser += 'parser[0] = new RegExp('+ JSON.stringify(resource.regex) + ');\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';
if (resource.v3_replacement) {
parser += 'parser[4] = "'+ resource.v3_replacement.replace('"', '\\"') +'";';
} else {
parser += 'parser[4] = 0;';
}
parser += '\n';
parser += 'exports.'+ details.name +'['+ i +'] = parser;';
results[details.resource].push(parser);
}
});
// Generate a correct format
exports.generate(results, callback);
};
/**
* Generate the regular expressions file source code.
*
* @param {Object} results The parsed result of the regexp.yaml.
* @param {Function} callback Completion callback
* @api public
*/
exports.generate = function generate(results, callback) {
var regexps = [
'"use strict";'
, exports.LEADER
, 'var parser;'
, 'exports.browser = Object.create(null);'
, results.user_agent_parsers.join('\n')
, 'exports.browser.length = '+ results.user_agent_parsers.length +';'
, 'exports.device = Object.create(null);'
, results.device_parsers.join('\n')
, 'exports.device.length = '+ results.device_parsers.length +';'
, 'exports.os = Object.create(null);'
, results.os_parsers.join('\n')
, 'exports.os.length = '+ results.os_parsers.length +';'
].join('\n\n');
// 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
};
// Crossing our fingers that it worked
try { vm.runInNewContext(regexps, sandbox, 'validating.vm'); }
catch (e) { return callback(e, null, regexps); }
callback(undefined, sandbox.exports, regexps);
};
/**
* The location of the ua-parser regexes yaml file.
*
* @type {String}
* @api private
*/
exports.remote = 'https://raw.githubusercontent.com/ua-parser/uap-core/master/regexes.yaml';
/**
* The locations of our local regexes yaml files.
*
* @type {String}
* @api private
*/
exports.before = path.resolve(__dirname, '..', 'static', 'user_agent.before.yaml');
exports.after = 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');
/**
* The leader that needs to be added so people know they shouldn't touch all the
* things.
*
* @type {String}
* @api private
*/
exports.LEADER = fs.readFileSync(path.join(__dirname, 'donotedit'), 'UTF-8');
+88
View File
@@ -0,0 +1,88 @@
{
"_from": "useragent@^2.1.5",
"_id": "useragent@2.3.0",
"_inBundle": false,
"_integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==",
"_location": "/useragent",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "useragent@^2.1.5",
"name": "useragent",
"escapedName": "useragent",
"rawSpec": "^2.1.5",
"saveSpec": null,
"fetchSpec": "^2.1.5"
},
"_requiredBy": [
"/6to5-core"
],
"_resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz",
"_shasum": "217f943ad540cb2128658ab23fc960f6a88c9972",
"_spec": "useragent@^2.1.5",
"_where": "/srv/http/mmap_sarrebourg/user/themes/basic/node_modules/6to5-core",
"author": {
"name": "Arnout Kazemier"
},
"bugs": {
"url": "https://github.com/3rd-Eden/useragent/issues"
},
"bundleDependencies": false,
"dependencies": {
"lru-cache": "4.1.x",
"tmp": "0.0.x"
},
"deprecated": false,
"description": "Fastest, most accurate & effecient user agent string parser, uses Browserscope's research for parsing",
"devDependencies": {
"assume": "1.5.x",
"mocha": "5.0.x",
"pre-commit": "1.2.x",
"request": "2.83.x",
"semver": "5.5.x",
"yamlparser": "0.0.x"
},
"homepage": "https://github.com/3rd-Eden/useragent#readme",
"keywords": [
"agent",
"browser",
"browserscope",
"os",
"parse",
"parser",
"ua",
"ua-parse",
"ua-parser",
"user agent",
"user",
"user-agent",
"useragent",
"version"
],
"license": "MIT",
"main": "./index.js",
"maintainers": [
{
"name": "Arnout Kazemier",
"email": "info@3rd-Eden.com",
"url": "http://www.3rd-Eden.com"
}
],
"name": "useragent",
"pre-commit": [
"test",
"update"
],
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/3rd-Eden/useragent.git"
},
"scripts": {
"prepublish": "npm run update",
"qa": "mocha --ui exports $(find test -name '*.qa.js')",
"test": "mocha $(find test -name '*.test.js')",
"update": "node ./bin/update.js"
},
"version": "2.3.0"
}
+12
View File
@@ -0,0 +1,12 @@
user_agent_parsers:
# command line tools
- regex: '(Wget)/(\d+)\.(\d+)\.?([ab]?\d+[a-z]*)'
- regex: '(curl)/(\d+)\.(\d+)\.(\d+)'
family_replacement: 'cURL'
os_parsers:
# See #33 for matching the iOS AFHTTPClient
- regex: '(iOS) (\d+)\.(\d+)(?:\.(\d+))?'
device_parsers:
+11
View File
@@ -0,0 +1,11 @@
user_agent_parsers:
# Rival IQ crawler/bot
- regex: '(Rival IQ, rivaliq.com)'
family_replacement: 'Rival IQ'
device_parsers:
# Rival IQ crawler/bot
- regex: '(Rival IQ, rivaliq.com)'
device_replacement: 'Spider'
os_parsers: