├── .github
└── workflows
│ └── ci.yaml
├── .gitignore
├── .gitmodules
├── LICENSE-Apache2
├── Makefile
├── README.md
├── bower.json
├── lib
├── appliance.js
├── browser.js
├── crawler.js
├── dataset.js
├── misc.js
├── mobilephone.js
├── os.js
├── util.js
└── woothee.js
├── package.json
├── release
└── woothee.js
├── scripts
├── dataset_footer.js
├── dataset_header.js
├── dataset_yaml2js.js
├── jsbuilder.js
├── release_footer.js
└── release_header.js
└── test
├── 00_require.js
├── 01_dataset.js
├── 02_run_testsets.js
└── mocha.opts
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | on:
4 | pull_request:
5 | branches: '*'
6 | push:
7 | branches:
8 | - master
9 | - main
10 | - 'release-*'
11 |
12 | jobs:
13 | test:
14 | strategy:
15 | matrix:
16 | node: ['12.x', '14.x']
17 | runs-on: ubuntu-latest
18 | steps:
19 | - uses: actions/checkout@v2
20 | - uses: actions/setup-node@v2.4.0
21 | with:
22 | node-version: ${{ matrix.node }}
23 | - run: git submodule update --init
24 | - run: npm install
25 | - run: npm test
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "woothee"]
2 | path = woothee
3 | url = https://github.com/woothee/woothee.git
4 |
--------------------------------------------------------------------------------
/LICENSE-Apache2:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | TESTS=test/*.js
2 |
3 | all: test
4 |
5 | datasets:
6 | git submodule update --init
7 |
8 | lib/dataset.js: datasets
9 | node scripts/dataset_yaml2js.js
10 | sync; sync; sync
11 |
12 | release/woothee.js: lib/*.js
13 | mkdir release || ls -d release
14 | node scripts/jsbuilder.js
15 |
16 | test: lib/dataset.js build
17 | @NODE_ENV=test ./node_modules/.bin/mocha \
18 | --require should \
19 | --slow 20 \
20 | $(TESTS)
21 |
22 | build: release/woothee.js
23 |
24 | .PHONY: datasets test
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Woothee javascript
2 |
3 | The Javascript implementation of Project Woothee, which is multi-language user-agent strings parsers.
4 |
5 | https://github.com/woothee/woothee
6 |
7 | ## Installation
8 |
9 | $ npm install woothee
10 |
11 | Or, download `release/woothee.js` and put it on your own directory for js files.
12 |
13 | ## Usage
14 |
15 | ### Parsing user-agent
16 |
17 | In html, load `woothee.js` and call `woothee.parse()` directly.
18 |
19 | ```html
20 |
21 |
25 | ```
26 |
27 | In node.js code, require 'woothee' and call its `parse()`.
28 | ```javascript
29 | var woothee = require('woothee');
30 | woothee.parse('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)')
31 | // => {name: 'Internet Explorer', category: 'pc', os: 'Windows 7', version: '8.0', vendor: 'Microsoft', os_version: 'NT 6.1'}
32 | ```
33 |
34 | Parse user-agent string and returns a object with keys `name`, `category`, `os`, `version` and `vendor`.
35 |
36 | For unknown user-agent (or partially failed to parse), result objects may have value 'UNKNOWN'.
37 |
38 | * `category`
39 | * labels of user terminal type, one of 'pc', 'smartphone', 'mobilephone', 'appliance', 'crawler' or 'misc' (or 'UNKNOWN')
40 | * `name`
41 | * the name of browser, like 'Internet Explorer', 'Firefox', 'GoogleBot'
42 | * `version`
43 | * version string, like '8.0' for IE, '9.0.1' for Firefix, '0.2.149.27' for Chrome, and so on
44 | * `os`
45 | * ex: 'Windows 7', 'Mac OSX', 'iPhone', 'iPad', 'Android'
46 | * This field used to indicate cellar phone carrier for category 'mobilephone'
47 | * `vendor`
48 | * optional field, shows browser vendor
49 | * `os_version`
50 | * optional field, shows version of operating systems
51 |
52 | ### Finding crawlers (almost all, not all) in fast
53 |
54 | ```javascript
55 | woothee.isCrawler('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)')
56 | // => false
57 | ```
58 |
59 | Try to see useragent's category is 'crawler' or not, by casual(fast) method. Minor case of crawlers is not tested in this method. To check crawler strictly, use `woothee.parse(str).category === 'crawler'`.
60 |
61 | * * * * *
62 |
63 | ## Authors
64 |
65 | * TAGOMORI Satoshi
66 |
67 | ## License
68 |
69 | Copyright 2012- TAGOMORI Satoshi (tagomoris)
70 |
71 | Licensed under the Apache License, Version 2.0 (the "License");
72 | you may not use this file except in compliance with the License.
73 | You may obtain a copy of the License at
74 |
75 | http://www.apache.org/licenses/LICENSE-2.0
76 |
77 | Unless required by applicable law or agreed to in writing, software
78 | distributed under the License is distributed on an "AS IS" BASIS,
79 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
80 | See the License for the specific language governing permissions and
81 | limitations under the License.
82 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "whoothee",
3 | "version": "1.4.0",
4 | "homepage": "https://github.com/woothee/woothee-js",
5 | "authors": [
6 | "null"
7 | ],
8 | "description": "Woothee javascript(and node.js) implementation",
9 | "main": "release/whoothee.js",
10 | "moduleType": [
11 | "amd",
12 | "globals",
13 | "node"
14 | ],
15 | "keywords": [
16 | "whoothee"
17 | ],
18 | "license": "MIT",
19 | "ignore": [
20 | "**/.*",
21 | "node_modules",
22 | "bower_components",
23 | "test",
24 | "tests"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/lib/appliance.js:
--------------------------------------------------------------------------------
1 | /* HEADER BEGIN */
2 | var util = require('./util'),
3 | updateMap = util.updateMap,
4 | updateVersion = util.updateVersion,
5 | updateCategory = util.updateCategory,
6 | updateOs = util.updateOs,
7 | updateOsVersion = util.updateOsVersion,
8 | dataset = require('./dataset');
9 | /* HEADER END */
10 |
11 | var challengePlaystation = exports.challengePlaystation = function(ua, result) {
12 | var data = null;
13 | var os_version = null;
14 | var match;
15 | if (ua.indexOf('PSP (PlayStation Portable);') >= 0) {
16 | data = dataset.get('PSP');
17 | if ((match = /PSP \(PlayStation Portable\); ([.0-9]+)\)/.exec(ua)))
18 | os_version = match[1];
19 | } else if (ua.indexOf('PlayStation Vita') >= 0) {
20 | data = dataset.get('PSVita');
21 | if ((match = /PlayStation Vita ([.0-9]+)\)/.exec(ua)))
22 | os_version = match[1];
23 | } else if (ua.indexOf('PLAYSTATION 3 ') >= 0 || ua.indexOf('PLAYSTATION 3;') >= 0) {
24 | data = dataset.get('PS3');
25 | if ((match = /PLAYSTATION 3;? ([.0-9]+)\)/.exec(ua)))
26 | os_version = match[1];
27 | } else if (ua.indexOf('PlayStation 4 ') >= 0) {
28 | data = dataset.get('PS4');
29 | if ((match = /PlayStation 4 ([.0-9]+)\)/.exec(ua)))
30 | os_version = match[1];
31 | }
32 |
33 | if (! data)
34 | return false;
35 |
36 | updateMap(result, data);
37 | if (os_version)
38 | updateOsVersion(result, os_version);
39 | return true;
40 | };
41 |
42 | var challengeNintendo = exports.challengeNintendo = function(ua, result) {
43 | var data = null;
44 | if (ua.indexOf('Nintendo 3DS;') >= 0) data = dataset.get('Nintendo3DS');
45 | else if (ua.indexOf('Nintendo DSi;') >= 0) data = dataset.get('NintendoDSi');
46 | else if (ua.indexOf('Nintendo Wii;') >= 0) data = dataset.get('NintendoWii');
47 | else if (ua.indexOf('(Nintendo WiiU)') >= 0) data = dataset.get('NintendoWiiU');
48 |
49 | if (! data)
50 | return false;
51 |
52 | updateMap(result, data);
53 | return true;
54 | };
55 |
56 | var challengeDigitalTV = exports.challengeDigitalTV = function(ua, result) {
57 | var data = null;
58 | if (ua.indexOf('InettvBrowser/') >= 0) data = dataset.get('DigitalTV');
59 |
60 | if (! data)
61 | return false;
62 |
63 | updateMap(result, data);
64 | return true;
65 | };
--------------------------------------------------------------------------------
/lib/browser.js:
--------------------------------------------------------------------------------
1 | /* HEADER BEGIN */
2 | var util = require('./util'),
3 | updateMap = util.updateMap,
4 | updateVersion = util.updateVersion,
5 | updateCategory = util.updateCategory,
6 | updateOs = util.updateOs,
7 | dataset = require('./dataset');
8 | /* HEADER END */
9 |
10 | var msiePattern = /MSIE ([.0-9]+);/;
11 | var tridentPattern = /Trident\/[.0-9]+;/;
12 | var tridentVersionPattern = / rv:([.0-9]+)/;
13 | var iemobilePattern = /IEMobile\/([.0-9]+);/;
14 | var challengeMSIE = exports.challengeMSIE = function(ua, result) {
15 | if (ua.indexOf('compatible; MSIE') < 0 && ua.indexOf('Trident/') < 0 && ua.indexOf('IEMobile/'))
16 | return false;
17 |
18 | var version;
19 | var match = msiePattern.exec(ua);
20 | if (!match)
21 | if (tridentPattern.exec(ua))
22 | match = tridentVersionPattern.exec(ua);
23 | if (!match)
24 | match = iemobilePattern.exec(ua);
25 |
26 | if (match)
27 | version = match[1];
28 | else
29 | version = dataset.VALUE_UNKNOWN;
30 | updateMap(result, dataset.get('MSIE'));
31 | updateVersion(result, version);
32 | return true;
33 | };
34 |
35 | var yandexBrowserPattern = /YaBrowser\/([.0-9]+)/;
36 | var challengeYandexBrowser = exports.challengeYandexBrowser = function(ua, result) {
37 | if (ua.indexOf('YaBrowser/') < 0)
38 | return false;
39 |
40 | var version;
41 | var match = yandexBrowserPattern.exec(ua);
42 | if (match) {
43 | version = match[1];
44 | } else {
45 | version = dataset.VALUE_UNKNOWN;
46 | }
47 | updateMap(result, dataset.get('YaBrowser'));
48 | updateVersion(result, version);
49 | return true;
50 | };
51 |
52 | var edgePattern = /(?:Edge|Edg|EdgiOS|EdgA)\/([.0-9]+)/;
53 | var firefoxiOSPattern = /FxiOS\/([.0-9]+)/;
54 | var chromePattern = /(?:Chrome|CrMo|CriOS)\/([.0-9]+)/;
55 | var operaBlinkPattern = /OPR\/([.0-9]+)/;
56 | var gsaPattern = /GSA\/([.0-9]+)/;
57 | var safariPattern = /Version\/([.0-9]+)/;
58 | var challengeSafariChrome = exports.challengeSafariChrome = function(ua, result) {
59 | if (ua.indexOf('Safari/') < 0)
60 | return false;
61 | if (ua.indexOf('Chrome') >= 0 && ua.indexOf('wv') >= 0)
62 | return false;
63 |
64 | var version = dataset.VALUE_UNKNOWN;
65 |
66 | var match;
67 | if ((match = edgePattern.exec(ua))) {
68 | version = match[1];
69 | updateMap(result, dataset.get('Edge'));
70 | updateVersion(result, version);
71 | return true;
72 | }
73 |
74 | if ((match = firefoxiOSPattern.exec(ua))) {
75 | version = match[1];
76 | updateMap(result, dataset.get('Firefox'));
77 | updateVersion(result, version);
78 | return true;
79 | }
80 |
81 | if ((match = chromePattern.exec(ua))) {
82 | var matchOpera;
83 | if ((matchOpera = operaBlinkPattern.exec(ua))) {
84 | // Opera w/ blink
85 | version = matchOpera[1];
86 | updateMap(result, dataset.get('Opera'));
87 | updateVersion(result, version);
88 | return true;
89 | }
90 |
91 | // Chrome
92 | version = match[1];
93 | updateMap(result, dataset.get('Chrome'));
94 | updateVersion(result, version);
95 | return true;
96 | }
97 |
98 | if ((match = gsaPattern.exec(ua))) {
99 | version = match[1];
100 | updateMap(result, dataset.get('GSA'));
101 | updateVersion(result, version);
102 | return true;
103 | }
104 |
105 | if ((match = safariPattern.exec(ua))) {
106 | version = match[1];
107 | }
108 |
109 | updateMap(result, dataset.get('Safari'));
110 | updateVersion(result, version);
111 | return true;
112 | };
113 |
114 | var firefoxPattern = /Firefox\/([.0-9]+)/;
115 | var challengeFirefox = exports.challengeFirefox = function(ua, result) {
116 | if (ua.indexOf('Firefox/') < 0)
117 | return false;
118 |
119 | var version = dataset.VALUE_UNKNOWN;
120 | var match = firefoxPattern.exec(ua);
121 | if (match)
122 | version = match[1];
123 | updateMap(result, dataset.get('Firefox'));
124 | updateVersion(result, version);
125 | return true;
126 | };
127 |
128 | var operaPattern1 = /Version\/([.0-9]+)/;
129 | var operaPattern2 = /Opera[\/ ]([.0-9]+)/;
130 | var challengeOpera = exports.challengeOpera = function(ua, result) {
131 | if (ua.indexOf('Opera') < 0)
132 | return false;
133 |
134 | var version = dataset.VALUE_UNKNOWN;
135 | var match = operaPattern1.exec(ua);
136 | if (match) {
137 | version = match[1];
138 | } else {
139 | match = operaPattern2.exec(ua);
140 | if (match)
141 | version = match[1];
142 | }
143 | updateMap(result, dataset.get('Opera'));
144 | updateVersion(result, version);
145 | return true;
146 | };
147 |
148 | var webviewPattern = /iP(hone;|ad;|od) .*like Mac OS X/;
149 | var webviewVersionPattern = /Version\/([.0-9]+)/;
150 | var challengeWebview = exports.challengeWebview = function(ua, result) {
151 | var version = dataset.VALUE_UNKNOWN;
152 |
153 | // Android(Lollipop and Above)
154 | if (ua.indexOf('Chrome') >= 0 && ua.indexOf('wv') >= 0) {
155 | var vmatch = webviewVersionPattern.exec(ua);
156 | if (vmatch)
157 | version = vmatch[1];
158 |
159 | updateMap(result, dataset.get('Webview'));
160 | updateVersion(result, version);
161 | return true;
162 | }
163 |
164 | // iOS
165 | var match = webviewPattern.exec(ua);
166 | if (match) {
167 | if (ua.indexOf('Safari/') > -1)
168 | return false;
169 |
170 | var vmatch = webviewVersionPattern.exec(ua);
171 | if (vmatch)
172 | version = vmatch[1];
173 |
174 | updateMap(result, dataset.get('Webview'));
175 | updateVersion(result, version);
176 | return true;
177 | }
178 |
179 | return false;
180 | };
181 |
182 | var sleipnirPattern = /Sleipnir\/([.0-9]+)/;
183 | var challengeSleipnir = exports.challengeSleipnir = function(ua, result) {
184 | if (ua.indexOf('Sleipnir/') < 0)
185 | return false;
186 |
187 | var version = dataset.VALUE_UNKNOWN;
188 | var match = sleipnirPattern.exec(ua);
189 | if (match)
190 | version = match[1];
191 | updateMap(result, dataset.get('Sleipnir'));
192 | updateVersion(result, version);
193 |
194 | /*
195 | Sleipnir's user-agent doesn't contain Windows version, so put 'Windows UNKNOWN Ver'.
196 | Sleipnir is IE component browser, so for Windows only.
197 | */
198 | var win = dataset.get('Win');
199 | updateCategory(result, win[dataset.KEY_CATEGORY]);
200 | updateOs(result, win[dataset.KEY_NAME]);
201 |
202 | return true;
203 | };
204 |
205 | var vivaldiPattern = /Vivaldi\/([.0-9]+)/;
206 | var challengeVivaldi = exports.challengeVivaldi = function(ua, result) {
207 | if (ua.indexOf('Vivaldi/') < 0)
208 | return false;
209 |
210 | var version = dataset.VALUE_UNKNOWN;
211 | var match = vivaldiPattern.exec(ua);
212 | if (match)
213 | version = match[1];
214 | updateMap(result, dataset.get('Vivaldi'));
215 | updateVersion(result, version);
216 |
217 | return true;
218 | };
219 |
--------------------------------------------------------------------------------
/lib/crawler.js:
--------------------------------------------------------------------------------
1 | /* HEADER BEGIN */
2 | var util = require('./util'),
3 | updateMap = util.updateMap,
4 | updateVersion = util.updateVersion,
5 | updateCategory = util.updateCategory,
6 | updateOs = util.updateOs,
7 | dataset = require('./dataset');
8 | /* HEADER END */
9 |
10 | var challengeGoogle = exports.challengeGoogle = function(ua, result) {
11 | if (ua.indexOf('Google') < 0)
12 | return false;
13 |
14 | if (ua.indexOf('compatible; Googlebot') >= 0) {
15 | if (ua.indexOf('compatible; Googlebot-Mobile') >= 0) {
16 | updateMap(result, dataset.get('GoogleBotMobile'));
17 | return true;
18 | } else {
19 | updateMap(result, dataset.get('GoogleBot'));
20 | return true;
21 | }
22 | }
23 | if (ua.indexOf('Googlebot-Image/') >= 0) {
24 | updateMap(result, dataset.get('GoogleBot'));
25 | return true;
26 | }
27 | if (ua.indexOf('Mediapartners-Google') >= 0) {
28 | if (ua.indexOf('compatible; Mediapartners-Google') >= 0 || ua === 'Mediapartners-Google') {
29 | updateMap(result, dataset.get('GoogleMediaPartners'));
30 | return true;
31 | }
32 | }
33 | if (ua.indexOf('Feedfetcher-Google;') >= 0) {
34 | updateMap(result, dataset.get('GoogleFeedFetcher'));
35 | return true;
36 | }
37 | if (ua.indexOf('AppEngine-Google') >= 0) {
38 | updateMap(result, dataset.get('GoogleAppEngine'));
39 | return true;
40 | }
41 | if (ua.indexOf('Google Web Preview') >= 0) {
42 | updateMap(result, dataset.get('GoogleWebPreview'));
43 | return true;
44 | }
45 |
46 | return false;
47 | };
48 |
49 | var challengeCrawlers = exports.challengeCrawlers = function(ua, result) {
50 | if (ua.indexOf('Yahoo') >= 0
51 | || ua.indexOf('help.yahoo.co.jp/help/jp/') >= 0
52 | || ua.indexOf('listing.yahoo.co.jp/support/faq/') >= 0) {
53 | if (ua.indexOf('compatible; Yahoo! Slurp') >= 0) {
54 | updateMap(result, dataset.get('YahooSlurp'));
55 | return true;
56 | }
57 | if (ua.indexOf('YahooFeedSeekerJp') >= 0 || ua.indexOf('YahooFeedSeekerBetaJp') >= 0) {
58 | updateMap(result, dataset.get('YahooJP'));
59 | return true;
60 | }
61 | if (ua.indexOf('crawler (http://listing.yahoo.co.jp/support/faq/') >= 0
62 | || ua.indexOf('crawler (http://help.yahoo.co.jp/help/jp/') >= 0) {
63 | updateMap(result, dataset.get('YahooJP'));
64 | return true;
65 | }
66 | if (ua.indexOf('Y!J-BRZ/YATSHA crawler') >= 0
67 | || ua.indexOf('Y!J-BRY/YATSH crawler') >= 0) {
68 | updateMap(result, dataset.get('YahooJP'));
69 | return true;
70 | }
71 | if (ua.indexOf('Yahoo Pipes') >= 0) {
72 | updateMap(result, dataset.get('YahooPipes'));
73 | return true;
74 | }
75 | }
76 | if (ua.indexOf('msnbot') >= 0) {
77 | updateMap(result, dataset.get('msnbot'));
78 | return true;
79 | }
80 | if (ua.indexOf('bingbot') >= 0) {
81 | if (ua.indexOf('compatible; bingbot') >= 0) {
82 | updateMap(result, dataset.get('bingbot'));
83 | return true;
84 | }
85 | }
86 | if (ua.indexOf('BingPreview') >= 0) {
87 | updateMap(result, dataset.get('BingPreview'));
88 | return true;
89 | }
90 | if (ua.indexOf('Baidu') >= 0) {
91 | if (ua.indexOf('compatible; Baiduspider') >= 0 ||
92 | ua.indexOf('Baiduspider+') >= 0 ||
93 | ua.indexOf('Baiduspider-image+') >= 0) {
94 | updateMap(result, dataset.get('Baiduspider'));
95 | return true;
96 | }
97 | }
98 | if (ua.indexOf('Yeti') >= 0) {
99 | if (ua.indexOf('http://help.naver.com/robots') >= 0 ||
100 | ua.indexOf('http://help.naver.com/support/robots.html') >= 0 ||
101 | ua.indexOf('http://naver.me/bot') >= 0) {
102 |
103 | updateMap(result, dataset.get('Yeti'));
104 | return true;
105 | }
106 | }
107 | if (ua.indexOf('FeedBurner/') >= 0) {
108 | updateMap(result, dataset.get('FeedBurner'));
109 | return true;
110 | }
111 | if (ua.indexOf('facebookexternalhit') >= 0) {
112 | updateMap(result, dataset.get('facebook'));
113 | return true;
114 | }
115 | if (ua.indexOf('Twitterbot/') >= 0) {
116 | updateMap(result, dataset.get('twitter'));
117 | return true;
118 | }
119 | if (ua.indexOf('ichiro') >= 0) {
120 | if (ua.indexOf('http://help.goo.ne.jp/door/crawler.html') >= 0 ||
121 | ua.indexOf('compatible; ichiro/mobile goo;') >= 0) {
122 | updateMap(result, dataset.get('goo'));
123 | return true;
124 | }
125 | }
126 | if (ua.indexOf('gooblogsearch/') >= 0) {
127 | updateMap(result, dataset.get('goo'));
128 | return true;
129 | }
130 | if (ua.indexOf('Apple-PubSub') >= 0) {
131 | updateMap(result, dataset.get('ApplePubSub'));
132 | return true;
133 | }
134 | if (ua.indexOf('(www.radian6.com/crawler)') >= 0) {
135 | updateMap(result, dataset.get('radian6'));
136 | return true;
137 | }
138 | if (ua.indexOf('Genieo/') >= 0) {
139 | updateMap(result, dataset.get('Genieo'));
140 | return true;
141 | }
142 | if (ua.indexOf('labs.topsy.com/butterfly/') >= 0) {
143 | updateMap(result, dataset.get('topsyButterfly'));
144 | return true;
145 | }
146 | if (ua.indexOf('rogerbot/1.0 (http://www.seomoz.org/dp/rogerbot') >= 0) {
147 | updateMap(result, dataset.get('rogerbot'));
148 | return true;
149 | }
150 | if (ua.indexOf('compatible; AhrefsBot/') >= 0) {
151 | updateMap(result, dataset.get('AhrefsBot'));
152 | return true;
153 | }
154 | if (ua.indexOf('livedoor FeedFetcher') >= 0 || ua.indexOf('Fastladder FeedFetcher') >= 0) {
155 | updateMap(result, dataset.get('livedoorFeedFetcher'));
156 | return true;
157 | }
158 | if (ua.indexOf('Hatena ') >= 0) {
159 | if (ua.indexOf('Hatena Antenna') >= 0 ||
160 | ua.indexOf('Hatena Pagetitle Agent') >= 0 ||
161 | ua.indexOf('Hatena Diary RSS') >= 0) {
162 | updateMap(result, dataset.get('Hatena'));
163 | return true;
164 | }
165 | }
166 | if (ua.indexOf('mixi-check') >= 0 ||
167 | ua.indexOf('mixi-crawler') >= 0 ||
168 | ua.indexOf('mixi-news-crawler') >= 0) {
169 | updateMap(result, dataset.get('mixi'));
170 | return true;
171 | }
172 | if (ua.indexOf('Indy Library') >= 0) {
173 | if (ua.indexOf('compatible; Indy Library') >= 0) {
174 | updateMap(result, dataset.get('IndyLibrary'));
175 | return true;
176 | }
177 | }
178 | if (ua.indexOf('trendictionbot') >= 0) {
179 | updateMap(result, dataset.get('trendictionbot'));
180 | return true;
181 | }
182 |
183 | return false;
184 | };
185 |
186 | var challengeMaybeCrawler = exports.challengeMaybeCrawler = function(ua, result) {
187 | if (/(bot|crawler|spider)(?:[-_ .\/;@()]|$)/i.exec(ua)) {
188 | updateMap(result, dataset.get('VariousCrawler'));
189 | return true;
190 | }
191 | if (/(?:Rome Client |UnwindFetchor\/|ia_archiver |Summify |PostRank\/)/.exec(ua) ||
192 | ua.indexOf('ASP-Ranker Feed Crawler') >= 0) {
193 | updateMap(result, dataset.get('VariousCrawler'));
194 | return true;
195 | }
196 | if (/(feed|web) ?parser/i.exec(ua)) {
197 | updateMap(result, dataset.get('VariousCrawler'));
198 | return true;
199 | }
200 | if (/watch ?dog/i.exec(ua)) {
201 | updateMap(result, dataset.get('VariousCrawler'));
202 | return true;
203 | }
204 | return false;
205 | };
206 |
--------------------------------------------------------------------------------
/lib/dataset.js:
--------------------------------------------------------------------------------
1 | var KEY_LABEL = exports.KEY_LABEL = 'label',
2 | KEY_NAME = exports.KEY_NAME = 'name',
3 | KEY_TYPE = exports.KEY_TYPE = 'type',
4 | KEY_CATEGORY = exports.KEY_CATEGORY = 'category',
5 | KEY_OS = exports.KEY_OS = 'os',
6 | KEY_OS_VERSION = exports.KEY_OS_VERSION = 'os_version',
7 | KEY_VENDOR = exports.KEY_VENDOR = 'vendor',
8 | KEY_VERSION = exports.KEY_VERSION = 'version';
9 |
10 | var TYPE_BROWSER = 'browser',
11 | TYPE_OS = 'os',
12 | TYPE_FULL = 'full';
13 |
14 | var CATEGORY_PC = exports.CATEGORY_PC = 'pc',
15 | CATEGORY_SMARTPHONE = exports.CATEGORY_SMARTPHONE = 'smartphone',
16 | CATEGORY_MOBILEPHONE = exports.CATEGORY_MOBILEPHONE = 'mobilephone',
17 | CATEGORY_CRAWLER = exports.CATEGORY_CRAWLER = 'crawler',
18 | CATEGORY_APPLIANCE = exports.CATEGORY_APPLIANCE = 'appliance',
19 | CATEGORY_MISC = exports.CATEGORY_MISC = 'misc';
20 |
21 | var ATTRIBUTE_NAME = exports.ATTRIBUTE_NAME = 'name',
22 | ATTRIBUTE_CATEGORY = exports.ATTRIBUTE_CATEGORY = 'category',
23 | ATTRIBUTE_OS = exports.ATTRIBUTE_OS = 'os',
24 | ATTRIBUTE_OS_VERSION = exports.ATTRIBUTE_OS_VERSION = 'os_version',
25 | ATTRIBUTE_VENDOR = exports.ATTRIBUTE_VENDOR = 'vendor',
26 | ATTRIBUTE_VERSION = exports.ATTRIBUTE_VERSION = 'version';
27 |
28 | var VALUE_UNKNOWN = exports.VALUE_UNKNOWN = 'UNKNOWN';
29 |
30 | var CATEGORY_LIST = exports.CATEGORY_LIST = [
31 | CATEGORY_PC, CATEGORY_SMARTPHONE, CATEGORY_MOBILEPHONE,
32 | CATEGORY_CRAWLER, CATEGORY_APPLIANCE, CATEGORY_MISC, VALUE_UNKNOWN
33 | ];
34 | var ATTRIBUTE_LIST = exports.ATTRIBUTE_LIST = [ATTRIBUTE_NAME, ATTRIBUTE_CATEGORY, ATTRIBUTE_OS, ATTRIBUTE_VENDOR, ATTRIBUTE_VERSION, ATTRIBUTE_OS_VERSION];
35 | // GENERATED from dataset.yaml at Tue Sep 7 11:33:54 JST 2021 by tagomoris
36 | var DATASET = {MSIE: {label: "MSIE", name: "Internet Explorer", type: "browser", vendor: "Microsoft"}, Edge: {label: "Edge", name: "Edge", type: "browser", vendor: "Microsoft"}, Chrome: {label: "Chrome", name: "Chrome", type: "browser", vendor: "Google"}, Safari: {label: "Safari", name: "Safari", type: "browser", vendor: "Apple"}, Firefox: {label: "Firefox", name: "Firefox", type: "browser", vendor: "Mozilla"}, Opera: {label: "Opera", name: "Opera", type: "browser", vendor: "Opera"}, Vivaldi: {label: "Vivaldi", name: "Vivaldi", type: "browser", vendor: "Vivaldi Technologies"}, Sleipnir: {label: "Sleipnir", name: "Sleipnir", type: "browser", vendor: "Fenrir Inc."}, GSA: {label: "GSA", name: "Google Search App", type: "browser", vendor: "Google"}, Webview: {label: "Webview", name: "Webview", type: "browser", vendor: "OS vendor"}, YaBrowser: {label: "YaBrowser", name: "Yandex Browser", type: "browser", vendor: "Yandex"}, Win: {label: "Win", name: "Windows UNKNOWN Ver", type: "os", category: "pc"}, Win10: {label: "Win10", name: "Windows 10", type: "os", category: "pc"}, "Win8.1": {label: "Win8.1", name: "Windows 8.1", type: "os", category: "pc"}, Win8: {label: "Win8", name: "Windows 8", type: "os", category: "pc"}, Win7: {label: "Win7", name: "Windows 7", type: "os", category: "pc"}, WinVista: {label: "WinVista", name: "Windows Vista", type: "os", category: "pc"}, WinXP: {label: "WinXP", name: "Windows XP", type: "os", category: "pc"}, Win2000: {label: "Win2000", name: "Windows 2000", type: "os", category: "pc"}, WinNT4: {label: "WinNT4", name: "Windows NT 4.0", type: "os", category: "pc"}, WinMe: {label: "WinMe", name: "Windows Me", type: "os", category: "pc"}, Win98: {label: "Win98", name: "Windows 98", type: "os", category: "pc"}, Win95: {label: "Win95", name: "Windows 95", type: "os", category: "pc"}, WinPhone: {label: "WinPhone", name: "Windows Phone OS", type: "os", category: "smartphone"}, WinCE: {label: "WinCE", name: "Windows CE", type: "os", category: "smartphone"}, OSX: {label: "OSX", name: "Mac OSX", type: "os", category: "pc"}, MacOS: {label: "MacOS", name: "Mac OS Classic", type: "os", category: "pc"}, Linux: {label: "Linux", name: "Linux", type: "os", category: "pc"}, BSD: {label: "BSD", name: "BSD", type: "os", category: "pc"}, ChromeOS: {label: "ChromeOS", name: "ChromeOS", type: "os", category: "pc"}, Android: {label: "Android", name: "Android", type: "os", category: "smartphone"}, iPhone: {label: "iPhone", name: "iPhone", type: "os", category: "smartphone"}, iPad: {label: "iPad", name: "iPad", type: "os", category: "smartphone"}, iPod: {label: "iPod", name: "iPod", type: "os", category: "smartphone"}, iOS: {label: "iOS", name: "iOS", type: "os", category: "smartphone"}, FirefoxOS: {label: "FirefoxOS", name: "Firefox OS", type: "os", category: "smartphone"}, BlackBerry: {label: "BlackBerry", name: "BlackBerry", type: "os", category: "smartphone"}, BlackBerry10: {label: "BlackBerry10", name: "BlackBerry 10", type: "os", category: "smartphone"}, docomo: {label: "docomo", name: "docomo", type: "full", vendor: "docomo", category: "mobilephone", os: "docomo"}, au: {label: "au", name: "au by KDDI", type: "full", vendor: "au", category: "mobilephone", os: "au"}, SoftBank: {label: "SoftBank", name: "SoftBank Mobile", type: "full", vendor: "SoftBank", category: "mobilephone", os: "SoftBank"}, willcom: {label: "willcom", name: "WILLCOM", type: "full", vendor: "WILLCOM", category: "mobilephone", os: "WILLCOM"}, jig: {label: "jig", name: "jig browser", type: "full", vendor: "", category: "mobilephone", os: "jig"}, emobile: {label: "emobile", name: "emobile", type: "full", vendor: "", category: "mobilephone", os: "emobile"}, SymbianOS: {label: "SymbianOS", name: "SymbianOS", type: "full", vendor: "", category: "mobilephone", os: "SymbianOS"}, MobileTranscoder: {label: "MobileTranscoder", name: "Mobile Transcoder", type: "full", vendor: "", category: "mobilephone", os: "Mobile Transcoder"}, Nintendo3DS: {label: "Nintendo3DS", name: "Nintendo 3DS", type: "full", vendor: "Nintendo", category: "appliance", os: "Nintendo 3DS"}, NintendoDSi: {label: "NintendoDSi", name: "Nintendo DSi", type: "full", vendor: "Nintendo", category: "appliance", os: "Nintendo DSi"}, NintendoWii: {label: "NintendoWii", name: "Nintendo Wii", type: "full", vendor: "Nintendo", category: "appliance", os: "Nintendo Wii"}, NintendoWiiU: {label: "NintendoWiiU", name: "Nintendo Wii U", type: "full", vendor: "Nintendo", category: "appliance", os: "Nintendo Wii U"}, PSP: {label: "PSP", name: "PlayStation Portable", type: "full", vendor: "Sony", category: "appliance", os: "PlayStation Portable"}, PSVita: {label: "PSVita", name: "PlayStation Vita", type: "full", vendor: "Sony", category: "appliance", os: "PlayStation Vita"}, PS3: {label: "PS3", name: "PlayStation 3", type: "full", vendor: "Sony", category: "appliance", os: "PlayStation 3"}, PS4: {label: "PS4", name: "PlayStation 4", type: "full", vendor: "Sony", category: "appliance", os: "PlayStation 4"}, Xbox360: {label: "Xbox360", name: "Xbox 360", type: "full", vendor: "Microsoft", category: "appliance", os: "Xbox 360"}, XboxOne: {label: "XboxOne", name: "Xbox One", type: "full", vendor: "Microsoft", category: "appliance", os: "Xbox One"}, DigitalTV: {label: "DigitalTV", name: "InternetTVBrowser", type: "full", vendor: "", category: "appliance", os: "DigitalTV"}, SafariRSSReader: {label: "SafariRSSReader", name: "Safari RSSReader", type: "full", vendor: "Apple", category: "misc"}, GoogleDesktop: {label: "GoogleDesktop", name: "Google Desktop", type: "full", vendor: "Google", category: "misc"}, WindowsRSSReader: {label: "WindowsRSSReader", name: "Windows RSSReader", type: "full", vendor: "Microsoft", category: "misc"}, VariousRSSReader: {label: "VariousRSSReader", name: "RSSReader", type: "full", vendor: "", category: "misc"}, HTTPLibrary: {label: "HTTPLibrary", name: "HTTP Library", type: "full", vendor: "", category: "misc"}, GoogleBot: {label: "GoogleBot", name: "Googlebot", type: "full", vendor: "", category: "crawler"}, GoogleBotMobile: {label: "GoogleBotMobile", name: "Googlebot Mobile", type: "full", vendor: "", category: "crawler"}, GoogleMediaPartners: {label: "GoogleMediaPartners", name: "Google Mediapartners", type: "full", vendor: "", category: "crawler"}, GoogleFeedFetcher: {label: "GoogleFeedFetcher", name: "Google Feedfetcher", type: "full", vendor: "", category: "crawler"}, GoogleAppEngine: {label: "GoogleAppEngine", name: "Google AppEngine", type: "full", vendor: "", category: "crawler"}, GoogleWebPreview: {label: "GoogleWebPreview", name: "Google Web Preview", type: "full", vendor: "", category: "crawler"}, YahooSlurp: {label: "YahooSlurp", name: "Yahoo! Slurp", type: "full", vendor: "", category: "crawler"}, YahooJP: {label: "YahooJP", name: "Yahoo! Japan", type: "full", vendor: "", category: "crawler"}, YahooPipes: {label: "YahooPipes", name: "Yahoo! Pipes", type: "full", vendor: "", category: "crawler"}, Baiduspider: {label: "Baiduspider", name: "Baiduspider", type: "full", vendor: "", category: "crawler"}, msnbot: {label: "msnbot", name: "msnbot", type: "full", vendor: "", category: "crawler"}, bingbot: {label: "bingbot", name: "bingbot", type: "full", vendor: "", category: "crawler"}, BingPreview: {label: "BingPreview", name: "BingPreview", type: "full", vendor: "", category: "crawler"}, Yeti: {label: "Yeti", name: "Naver Yeti", type: "full", vendor: "", category: "crawler"}, FeedBurner: {label: "FeedBurner", name: "Google FeedBurner", type: "full", vendor: "", category: "crawler"}, facebook: {label: "facebook", name: "facebook", type: "full", vendor: "", category: "crawler"}, twitter: {label: "twitter", name: "twitter", type: "full", vendor: "", category: "crawler"}, trendictionbot: {label: "trendictionbot", name: "trendiction", type: "full", vendor: "", category: "crawler"}, mixi: {label: "mixi", name: "mixi", type: "full", vendor: "", category: "crawler"}, IndyLibrary: {label: "IndyLibrary", name: "Indy Library", type: "full", vendor: "", category: "crawler"}, ApplePubSub: {label: "ApplePubSub", name: "Apple iCloud", type: "full", vendor: "", category: "crawler"}, Genieo: {label: "Genieo", name: "Genieo Web Filter", type: "full", vendor: "", category: "crawler"}, topsyButterfly: {label: "topsyButterfly", name: "topsy Butterfly", type: "full", vendor: "", category: "crawler"}, rogerbot: {label: "rogerbot", name: "SeoMoz rogerbot", type: "full", vendor: "", category: "crawler"}, AhrefsBot: {label: "AhrefsBot", name: "ahref AhrefsBot", type: "full", vendor: "", category: "crawler"}, radian6: {label: "radian6", name: "salesforce radian6", type: "full", vendor: "", category: "crawler"}, Hatena: {label: "Hatena", name: "Hatena", type: "full", vendor: "", category: "crawler"}, goo: {label: "goo", name: "goo", type: "full", vendor: "", category: "crawler"}, livedoorFeedFetcher: {label: "livedoorFeedFetcher", name: "livedoor FeedFetcher", type: "full", vendor: "", category: "crawler"}, VariousCrawler: {label: "VariousCrawler", name: "misc crawler", type: "full", vendor: "", category: "crawler"}};
37 | var get = exports.get = function(label) {
38 | return DATASET[label];
39 | };
40 |
--------------------------------------------------------------------------------
/lib/misc.js:
--------------------------------------------------------------------------------
1 | /* HEADER BEGIN */
2 | var util = require('./util'),
3 | updateMap = util.updateMap,
4 | updateVersion = util.updateVersion,
5 | updateCategory = util.updateCategory,
6 | updateOs = util.updateOs,
7 | dataset = require('./dataset');
8 | /* HEADER END */
9 |
10 | var challengeDesktopTools = exports.challengeDesktopTools = function(ua, result){
11 | var data = null;
12 | if (ua.indexOf('AppleSyndication/') >= 0) data = dataset.get('SafariRSSReader');
13 | else if (ua.indexOf('compatible; Google Desktop/') >= 0) data = dataset.get('GoogleDesktop');
14 | else if (ua.indexOf('Windows-RSS-Platform') >= 0) data = dataset.get('WindowsRSSReader');
15 |
16 | if (!data)
17 | return false;
18 |
19 | updateMap(result, data);
20 | return true;
21 | };
22 |
23 | var challengeSmartPhonePatterns = exports.challengeSmartPhonePatterns = function(ua, result){
24 | var data;
25 | if (ua.indexOf('CFNetwork/') >= 0) {
26 | data = dataset.get('iOS');
27 | updateCategory(result, data[dataset.KEY_CATEGORY]);
28 | updateOs(result, data[dataset.KEY_NAME]);
29 | return true;
30 | }
31 |
32 | return false;
33 | };
34 |
35 | var challengeHTTPLibrary = exports.challengeHTTPLibrary = function(ua, result){
36 | var data,version;
37 | if (/^(?:Apache-HttpClient\/|Jakarta Commons-HttpClient\/|Java\/)/.exec(ua) || /[- ]HttpClient(\/|$)/.exec(ua)) {
38 | data = dataset.get('HTTPLibrary'); version = 'Java';
39 | } else if (ua.indexOf('Java(TM) 2 Runtime Environment,') >= 0) {
40 | data = dataset.get('HTTPLibrary'); version = 'Java';
41 | } else if (/^Wget/.exec(ua)) {
42 | data = dataset.get('HTTPLibrary'); version = 'wget';
43 | } else if (/^(?:libwww-perl|WWW-Mechanize|LWP::Simple|LWP |lwp-trivial)/.exec(ua)) {
44 | data = dataset.get('HTTPLibrary'); version = 'perl';
45 | } else if (/^(?:Ruby|feedzirra|Typhoeus)/.exec(ua)) {
46 | data = dataset.get('HTTPLibrary'); version = 'ruby';
47 | } else if (/^(?:Python-urllib\/|Twisted )/.exec(ua)) {
48 | data = dataset.get('HTTPLibrary'); version = 'python';
49 | } else if (/^(:?PHP|WordPress|CakePHP|PukiWiki|PECL::HTTP)(?:\/| |$)/.exec(ua) || /(?:PEAR |)HTTP_Request(?: class|2)/.exec(ua)) {
50 | data = dataset.get('HTTPLibrary'); version = 'php';
51 | } else if (ua.indexOf('PEAR HTTP_Request class;') >= 0) {
52 | data = dataset.get('HTTPLibrary'); version = 'php';
53 | } else if (ua.indexOf('curl/') >= 0) {
54 | data = dataset.get('HTTPLibrary'); version = 'curl';
55 | }
56 |
57 | if (! data)
58 | return false;
59 |
60 | updateMap(result, data);
61 | updateVersion(result, version);
62 | return true;
63 | };
64 |
65 | var challengeMaybeRSSReader = exports.challengeMaybeRSSReader = function(ua, result){
66 | var data = null;
67 | if (/rss(?:reader|bar|[-_ \/;()]|[ +]*\/)/i.exec(ua) || /headline-reader/i.exec(ua)) {
68 | data = dataset.get('VariousRSSReader');
69 | }
70 | else {
71 | if (ua.indexOf('cococ/') >= 0)
72 | data = dataset.get('VariousRSSReader');
73 | }
74 |
75 | if (! data)
76 | return false;
77 |
78 | updateMap(result, data);
79 | return true;
80 | };
81 |
--------------------------------------------------------------------------------
/lib/mobilephone.js:
--------------------------------------------------------------------------------
1 | /* HEADER BEGIN */
2 | var util = require('./util'),
3 | updateMap = util.updateMap,
4 | updateVersion = util.updateVersion,
5 | updateCategory = util.updateCategory,
6 | updateOs = util.updateOs,
7 | dataset = require('./dataset');
8 | /* HEADER END */
9 |
10 | var docomoPattern = /DoCoMo\/[.0-9]+[ \/]([^- \/;()"']+)/;
11 | var fomaPattern = /\(([^;)]+);FOMA;/;
12 | var challengeDocomo = exports.challengeDocomo = function(ua, result) {
13 | if (ua.indexOf('DoCoMo') < 0 && ua.indexOf(';FOMA;') < 0)
14 | return false;
15 |
16 | var version = dataset.VALUE_UNKNOWN;
17 | var match;
18 | if ((match = docomoPattern.exec(ua))) {
19 | version = match[1];
20 | } else if ((match = fomaPattern.exec(ua))) {
21 | version = match[1];
22 | }
23 | updateMap(result, dataset.get('docomo'));
24 | updateVersion(result, version);
25 | return true;
26 | };
27 |
28 | var kddiPattern = /KDDI-([^- \/;()"']+)/;
29 | var challengeAu = exports.challengeAu = function(ua, result) {
30 | if (ua.indexOf('KDDI-') < 0)
31 | return false;
32 |
33 | var version = dataset.VALUE_UNKNOWN;
34 | var match = kddiPattern.exec(ua);
35 | if (match)
36 | version = match[1];
37 | updateMap(result, dataset.get('au'));
38 | updateVersion(result, version);
39 | return true;
40 | };
41 |
42 | var softbankPattern = /(?:SoftBank|Vodafone|J-PHONE)\/[.0-9]+\/([^ \/;()]+)/;
43 | var challengeSoftbank = exports.challengeSoftbank = function(ua, result) {
44 | if (ua.indexOf('SoftBank') < 0 && ua.indexOf('Vodafone') < 0 && ua.indexOf('J-PHONE') < 0)
45 | return false;
46 |
47 | var version = dataset.VALUE_UNKNOWN;
48 | var match = softbankPattern.exec(ua);
49 | if (match)
50 | version = match[1];
51 | updateMap(result, dataset.get('SoftBank'));
52 | updateVersion(result, version);
53 | return true;
54 | };
55 |
56 | var willcomPattern = /(?:WILLCOM|DDIPOCKET);[^\/]+\/([^ \/;()]+)/;
57 | var challengeWillcom = exports.challengeWillcom = function(ua, result) {
58 | if (ua.indexOf('WILLCOM') < 0 && ua.indexOf('DDIPOCKET') < 0)
59 | return false;
60 |
61 | var version = dataset.VALUE_UNKNOWN;
62 | var match = willcomPattern.exec(ua);
63 | if (match)
64 | version = match[1];
65 | updateMap(result, dataset.get('willcom'));
66 | updateVersion(result, version);
67 | return true;
68 | };
69 |
70 | var challengeMisc = exports.challengeMisc = function(ua, result) {
71 | var match;
72 | if (ua.indexOf('jig browser') >= 0) {
73 | updateMap(result, dataset.get('jig'));
74 | if ((match = /jig browser[^;]+; ([^);]+)/.exec(ua))) {
75 | updateVersion(result, match[1]);
76 | }
77 | return true;
78 | }
79 | if (ua.indexOf('emobile/') >= 0 || ua.indexOf('OpenBrowser') >= 0 || ua.indexOf('Browser/Obigo-Browser') >= 0) {
80 | updateMap(result, dataset.get('emobile'));
81 | return true;
82 | }
83 | if (ua.indexOf('SymbianOS') >= 0) {
84 | updateMap(result, dataset.get('SymbianOS'));
85 | return true;
86 | }
87 | if (ua.indexOf('Hatena-Mobile-Gateway/') >= 0) {
88 | updateMap(result, dataset.get('MobileTranscoder'));
89 | updateVersion(result, 'Hatena');
90 | return true;
91 | }
92 | if (ua.indexOf('livedoor-Mobile-Gateway/') >= 0) {
93 | updateMap(result, dataset.get('MobileTranscoder'));
94 | updateVersion(result, 'livedoor');
95 | return true;
96 | }
97 | return false;
98 | };
99 |
--------------------------------------------------------------------------------
/lib/os.js:
--------------------------------------------------------------------------------
1 | /* HEADER BEGIN */
2 | var util = require('./util'),
3 | updateMap = util.updateMap,
4 | updateVersion = util.updateVersion,
5 | updateCategory = util.updateCategory,
6 | updateOs = util.updateOs,
7 | updateOsVersion = util.updateOsVersion,
8 | dataset = require('./dataset');
9 | /* HEADER END */
10 |
11 | var windowsPattern = /Windows ([ .a-zA-Z0-9]+)[;\\)]/;
12 | var windowsPhonePattern = /^Phone(?: OS)? ([.0-9]+)/;
13 | var challengeWindows = exports.challengeWindows = function(ua, result) {
14 | if (ua.indexOf('Windows') < 0)
15 | return false;
16 |
17 | // Xbox Series
18 | if (ua.indexOf('Xbox') > -1) {
19 | var d;
20 | if (ua.indexOf('Xbox; Xbox One)') > -1)
21 | d = dataset.get("XboxOne");
22 | else
23 | d = dataset.get("Xbox360");
24 | // overwrite browser detections as appliance
25 | updateMap(result, d);
26 | return true;
27 | }
28 |
29 |
30 | var data = dataset.get('Win');
31 | var match = windowsPattern.exec(ua);
32 | if (!match) {
33 | // Windows, but version unknown
34 | updateCategory(result, data[dataset.KEY_CATEGORY]);
35 | updateOs(result, data[dataset.KEY_NAME]);
36 | return true;
37 | }
38 | var version = match[1];
39 | if (version === 'NT 10.0') data = dataset.get('Win10');
40 | else if (version === 'NT 6.3') data = dataset.get('Win8.1');
41 | else if (version === 'NT 6.2') data = dataset.get('Win8'); // "NT 6.2; ARM;" means Windows RT, oh....
42 | else if (version === 'NT 6.1') data = dataset.get('Win7');
43 | else if (version === 'NT 6.0') data = dataset.get('WinVista');
44 | else if (version === 'NT 5.1') data = dataset.get('WinXP');
45 | else if ((match = windowsPhonePattern.exec(version))) {
46 | data = dataset.get('WinPhone');
47 | version = match[1];
48 | }
49 | else if (version === 'NT 5.0') data = dataset.get('Win2000');
50 | else if (version === 'NT 4.0') data = dataset.get('WinNT4');
51 | else if (version === '98') data = dataset.get('Win98'); // wow, WinMe is shown as 'Windows 98; Win9x 4.90', fxxxk
52 | else if (version === '95') data = dataset.get('Win95');
53 | else if (version === 'CE') data = dataset.get('WinCE');
54 | /*
55 | else
56 | // windows unknown
57 | */
58 | updateCategory(result, data[dataset.KEY_CATEGORY]);
59 | updateOs(result, data[dataset.KEY_NAME]);
60 | updateOsVersion(result, version);
61 | return true;
62 | };
63 |
64 | var challengeOSX = exports.challengeOSX = function(ua, result) {
65 | if (ua.indexOf('Mac OS X') < 0)
66 | return false;
67 |
68 | var data = dataset.get('OSX');
69 | var version;
70 | var match;
71 | if (ua.indexOf('like Mac OS X') >= 0) {
72 | if (ua.indexOf('iPhone;') >= 0) data = dataset.get('iPhone');
73 | else if (ua.indexOf('iPad;') >= 0) data = dataset.get('iPad');
74 | else if (ua.indexOf('iPod') >= 0) data = dataset.get('iPod');
75 |
76 | if ((match = /; CPU(?: iPhone)? OS (\d+_\d+(?:_\d+)?) like Mac OS X/.exec(ua)))
77 | version = match[1].replace(/_/g, '.');
78 | }
79 | else {
80 | if ((match = /Mac OS X (10[._]\d+(?:[._]\d+)?)(?:\)|;)/.exec(ua))) {
81 | version = match[1].replace(/_/g, '.');
82 | }
83 | }
84 | updateCategory(result, data[dataset.KEY_CATEGORY]);
85 | updateOs(result, data[dataset.KEY_NAME]);
86 | if (version)
87 | updateOsVersion(result, version);
88 | return true;
89 | };
90 |
91 | var challengeLinux = exports.challengeLinux = function(ua, result) {
92 | if (ua.indexOf('Linux') < 0)
93 | return false;
94 |
95 | var data = dataset.get('Linux');
96 | var os_version;
97 | var match;
98 | if (ua.indexOf('Android') >= 0) {
99 | data = dataset.get('Android');
100 | if ((match = /Android[- ](\d+(?:\.\d+(?:\.\d+)?)?)/.exec(ua)))
101 | os_version = match[1];
102 | }
103 | updateCategory(result, data[dataset.KEY_CATEGORY]);
104 | updateOs(result, data[dataset.KEY_NAME]);
105 | if (os_version)
106 | updateOsVersion(result, os_version);
107 | return true;
108 | };
109 |
110 | var challengeSmartPhone = exports.challengeSmartPhone = function(ua, result) {
111 | var data = null;
112 | var os_version = null;
113 | var match;
114 | if (ua.indexOf('iPhone') >= 0) data = dataset.get('iPhone');
115 | else if (ua.indexOf('iPad') >= 0) data = dataset.get('iPad');
116 | else if (ua.indexOf('iPod') >= 0) data = dataset.get('iPod');
117 | else if (ua.indexOf('Android') >= 0) data = dataset.get('Android');
118 | else if (ua.indexOf('CFNetwork') >= 0) data = dataset.get('iOS');
119 | else if (ua.indexOf('BB10') >= 0) {
120 | data = dataset.get('BlackBerry10');
121 | if ((match = /BB10(?:.+)Version\/([.0-9]+)/.exec(ua)))
122 | os_version = match[1];
123 | }
124 | else if (ua.indexOf('BlackBerry') >= 0) {
125 | data = dataset.get('BlackBerry');
126 | if ((match = /BlackBerry(?:\d+)\/([.0-9]+) /.exec(ua)))
127 | os_version = match[1];
128 | }
129 |
130 | if (result[dataset.KEY_NAME] && result[dataset.KEY_NAME] === dataset.get('Firefox')[dataset.KEY_NAME]) {
131 | // Firefox OS specific pattern
132 | // http://lawrencemandel.com/2012/07/27/decision-made-firefox-os-user-agent-string/
133 | // https://github.com/woothee/woothee/issues/2
134 | if ((match = /^Mozilla\/[.0-9]+ \((?:Mobile|Tablet);(?:.*;)? rv:([.0-9]+)\) Gecko\/[.0-9]+ Firefox\/[.0-9]+$/.exec(ua))) {
135 | data = dataset.get('FirefoxOS');
136 | os_version = match[1];
137 | }
138 | }
139 |
140 | if (!data)
141 | return false;
142 |
143 | updateCategory(result, data[dataset.KEY_CATEGORY]);
144 | updateOs(result, data[dataset.KEY_NAME]);
145 | if (os_version)
146 | updateOsVersion(result, os_version);
147 | return true;
148 | };
149 |
150 | var challengeMobilePhone = exports.challengeMobilePhone = function(ua, result) {
151 | var data,match;
152 | if (ua.indexOf('KDDI-') >= 0) {
153 | if ((match = /KDDI-([^- \/;()"']+)/.exec(ua))) {
154 | var term = match[1];
155 | data = dataset.get('au');
156 | updateCategory(result, data[dataset.KEY_CATEGORY]);
157 | updateOs(result, data[dataset.KEY_OS]);
158 | updateVersion(result, term);
159 | return true;
160 | }
161 | }
162 | if (ua.indexOf('WILLCOM') >= 0 || ua.indexOf('DDIPOCKET') >= 0) {
163 | if ((match = /(?:WILLCOM|DDIPOCKET);[^\/]+\/([^ \/;()]+)/.exec(ua))) {
164 | term = match[1];
165 | data = dataset.get('willcom');
166 | updateCategory(result, data[dataset.KEY_CATEGORY]);
167 | updateOs(result, data[dataset.KEY_OS]);
168 | updateVersion(result, term);
169 | return true;
170 | }
171 | }
172 | if (ua.indexOf('SymbianOS') >= 0) {
173 | data = dataset.get('SymbianOS');
174 | updateCategory(result, data[dataset.KEY_CATEGORY]);
175 | updateOs(result, data[dataset.KEY_OS]);
176 | return true;
177 | }
178 | if (ua.indexOf('Google Wireless Transcoder') >= 0) {
179 | updateMap(result, dataset.get('MobileTranscoder'));
180 | updateVersion(result, 'Google');
181 | return true;
182 | }
183 | if (ua.indexOf('Naver Transcoder') >= 0) {
184 | updateMap(result, dataset.get('MobileTranscoder'));
185 | updateVersion(result, 'Naver');
186 | return true;
187 | }
188 |
189 | return false;
190 | };
191 |
192 | var challengeAppliance = exports.challengeAppliance = function(ua, result) {
193 | var data;
194 | if (ua.indexOf('Nintendo DSi;') >= 0) {
195 | data = dataset.get('NintendoDSi');
196 | updateCategory(result, data[dataset.KEY_CATEGORY]);
197 | updateOs(result, data[dataset.KEY_OS]);
198 | return true;
199 | }
200 | if (ua.indexOf('Nintendo Wii;') >= 0) {
201 | data = dataset.get('NintendoWii');
202 | updateCategory(result, data[dataset.KEY_CATEGORY]);
203 | updateOs(result, data[dataset.KEY_OS]);
204 | return true;
205 | }
206 | return false;
207 | };
208 |
209 | var challengeMisc = exports.challengeMisc = function(ua, result) {
210 | var data;
211 | var osVersion;
212 | var match;
213 | if (ua.indexOf('(Win98;') >= 0) {
214 | data = dataset.get('Win98');
215 | osVersion = "98";
216 | } else if (ua.indexOf('Macintosh; U; PPC;') >= 0) {
217 | data = dataset.get('MacOS');
218 | if ((match = /rv:(\d+\.\d+\.\d+)/.exec(ua)))
219 | osVersion = match[1];
220 | }
221 | else if (ua.indexOf('Mac_PowerPC') >= 0) data = dataset.get('MacOS');
222 | else if (ua.indexOf('X11; FreeBSD ') >= 0) {
223 | data = dataset.get('BSD');
224 | if ((match = /FreeBSD ([^;\)]+);/.exec(ua)))
225 | osVersion = match[1];
226 | }
227 | else if (ua.indexOf('X11; CrOS ') >= 0) {
228 | data = dataset.get('ChromeOS');
229 | if ((match = /CrOS ([^\)]+)\)/.exec(ua)))
230 | osVersion = match[1];
231 | }
232 |
233 | if (data) {
234 | updateCategory(result, data[dataset.KEY_CATEGORY]);
235 | updateOs(result, data[dataset.KEY_NAME]);
236 | if (osVersion)
237 | updateOsVersion(result, osVersion);
238 | return true;
239 | }
240 |
241 | return false;
242 | };
243 |
--------------------------------------------------------------------------------
/lib/util.js:
--------------------------------------------------------------------------------
1 | /* HEADER BEGIN */
2 | var dataset = require('./dataset');
3 | /* HEADER END */
4 |
5 | var updateMap = exports.updateMap = function(target, source) {
6 | for (var key in source) {
7 | if (key === dataset.KEY_LABEL || key === dataset.KEY_TYPE)
8 | continue;
9 | if (source[key] && source[key].length > 0)
10 | target[key] = source[key];
11 | }
12 | };
13 |
14 | var updateCategory = exports.updateCategory = function(target, category) {
15 | target[dataset.ATTRIBUTE_CATEGORY] = category;
16 | };
17 |
18 | var updateVersion = exports.updateVersion = function(target, version) {
19 | target[dataset.ATTRIBUTE_VERSION] = version;
20 | };
21 |
22 | var updateOs = exports.updateOs = function(target, os) {
23 | target[dataset.ATTRIBUTE_OS] = os;
24 | };
25 |
26 | var updateOsVersion = exports.updateOsVersion = function(target, version) {
27 | target[dataset.ATTRIBUTE_OS_VERSION] = version;
28 | };
--------------------------------------------------------------------------------
/lib/woothee.js:
--------------------------------------------------------------------------------
1 | /* HEADER BEGIN */
2 | var dataset = require('./dataset')
3 | , browser = require('./browser')
4 | , os = require('./os')
5 | , mobilephone = require('./mobilephone')
6 | , crawler = require('./crawler')
7 | , appliance = require('./appliance')
8 | , misc = require('./misc')
9 | , package_info = require('./../package.json');
10 | /* HEADER END */
11 |
12 | var version = exports.VERSION = package_info.version;
13 |
14 | var parse = exports.parse = function(userAgent) {
15 | return fillResult(execParse(userAgent));
16 | };
17 |
18 | var isCrawler = exports.isCrawler = function(userAgent) {
19 | return !!userAgent && userAgent !== '-' && tryCrawler(userAgent, {});
20 | };
21 |
22 | function execParse(userAgent) {
23 | var result = {};
24 |
25 | if (! userAgent || userAgent === '-')
26 | return result;
27 |
28 | if (tryCrawler(userAgent, result))
29 | return result;
30 |
31 | if (tryBrowser(userAgent, result)) {
32 | if (tryOS(userAgent, result))
33 | return result;
34 | else
35 | return result;
36 | }
37 |
38 | if (tryMobilePhone(userAgent, result))
39 | return result;
40 |
41 | if (tryAppliance(userAgent, result))
42 | return result;
43 |
44 | if (tryMisc(userAgent, result))
45 | return result;
46 |
47 | // browser unknown, check os only
48 | if (tryOS(userAgent, result))
49 | return result;
50 |
51 | if (tryRareCases(userAgent, result))
52 | return result;
53 |
54 | return result;
55 | };
56 |
57 | function tryCrawler(userAgent, result) {
58 | if (crawler.challengeGoogle(userAgent, result))
59 | return true;
60 |
61 | if (crawler.challengeCrawlers(userAgent, result))
62 | return true;
63 |
64 | return false;
65 | }
66 |
67 | function tryBrowser(userAgent, result) {
68 | if (browser.challengeMSIE(userAgent, result))
69 | return true;
70 |
71 | if (browser.challengeVivaldi(userAgent, result))
72 | return true;
73 |
74 | if (browser.challengeYandexBrowser(userAgent, result))
75 | return true;
76 |
77 | if (browser.challengeSafariChrome(userAgent, result))
78 | return true;
79 |
80 | if (browser.challengeFirefox(userAgent, result))
81 | return true;
82 |
83 | if (browser.challengeOpera(userAgent, result))
84 | return true;
85 |
86 | if (browser.challengeWebview(userAgent, result))
87 | return true;
88 |
89 | return false;
90 | }
91 |
92 | function tryOS(userAgent, result) {
93 | if (os.challengeWindows(userAgent, result))
94 | return true;
95 |
96 | // OSX PC and iOS devices (strict check)
97 | if (os.challengeOSX(userAgent, result))
98 | return true;
99 |
100 | // Linux PC and Android
101 | if (os.challengeLinux(userAgent, result))
102 | return true;
103 |
104 | // all useragents matches /(iPhone|iPad|iPod|Android|BlackBerry)/
105 | if (os.challengeSmartPhone(userAgent, result))
106 | return true;
107 |
108 | // mobile phones like KDDI-.*
109 | if (os.challengeMobilePhone(userAgent, result))
110 | return true;
111 |
112 | // Nintendo DSi/Wii with Opera
113 | if (os.challengeAppliance(userAgent, result))
114 | return true;
115 |
116 | if (os.challengeMisc(userAgent, result))
117 | return true;
118 |
119 | return false;
120 | }
121 |
122 | function tryMobilePhone(userAgent, result) {
123 | if (mobilephone.challengeDocomo(userAgent, result))
124 | return true;
125 |
126 | if (mobilephone.challengeAu(userAgent, result))
127 | return true;
128 |
129 | if (mobilephone.challengeSoftbank(userAgent, result))
130 | return true;
131 |
132 | if (mobilephone.challengeWillcom(userAgent, result))
133 | return true;
134 |
135 | if (mobilephone.challengeMisc(userAgent, result))
136 | return true;
137 |
138 | return false;
139 | }
140 |
141 | function tryAppliance(userAgent, result) {
142 | if (appliance.challengePlaystation(userAgent, result))
143 | return true;
144 |
145 | if (appliance.challengeNintendo(userAgent, result))
146 | return true;
147 |
148 | if (appliance.challengeDigitalTV(userAgent, result))
149 | return true;
150 |
151 | return false;
152 | }
153 |
154 | function tryMisc(userAgent, result) {
155 | if (misc.challengeDesktopTools(userAgent, result))
156 | return true;
157 |
158 | return false;
159 | }
160 |
161 | function tryRareCases(userAgent, result) {
162 | if (misc.challengeSmartPhonePatterns(userAgent, result))
163 | return true;
164 |
165 | if (browser.challengeSleipnir(userAgent, result))
166 | return true;
167 |
168 | if (misc.challengeHTTPLibrary(userAgent, result))
169 | return true;
170 |
171 | if (misc.challengeMaybeRSSReader(userAgent, result))
172 | return true;
173 |
174 | if (crawler.challengeMaybeCrawler(userAgent, result))
175 | return true;
176 |
177 | return false;
178 | }
179 |
180 | var filled = {};
181 | filled[dataset.ATTRIBUTE_NAME] = dataset.VALUE_UNKNOWN;
182 | filled[dataset.ATTRIBUTE_CATEGORY] = dataset.VALUE_UNKNOWN;
183 | filled[dataset.ATTRIBUTE_OS] = dataset.VALUE_UNKNOWN;
184 | filled[dataset.ATTRIBUTE_OS_VERSION] = dataset.VALUE_UNKNOWN;
185 | filled[dataset.ATTRIBUTE_VERSION] = dataset.VALUE_UNKNOWN;
186 | filled[dataset.ATTRIBUTE_VENDOR] = dataset.VALUE_UNKNOWN;
187 |
188 | function fillResult(result) {
189 | if (! result[dataset.ATTRIBUTE_NAME])
190 | result[dataset.ATTRIBUTE_NAME] = dataset.VALUE_UNKNOWN;
191 | if (! result[dataset.ATTRIBUTE_CATEGORY])
192 | result[dataset.ATTRIBUTE_CATEGORY] = dataset.VALUE_UNKNOWN;
193 | if (! result[dataset.ATTRIBUTE_OS])
194 | result[dataset.ATTRIBUTE_OS] = dataset.VALUE_UNKNOWN;
195 | if (! result[dataset.ATTRIBUTE_OS_VERSION])
196 | result[dataset.ATTRIBUTE_OS_VERSION] = dataset.VALUE_UNKNOWN;
197 | if (! result[dataset.ATTRIBUTE_VERSION])
198 | result[dataset.ATTRIBUTE_VERSION] = dataset.VALUE_UNKNOWN;
199 | if (! result[dataset.ATTRIBUTE_VENDOR])
200 | result[dataset.ATTRIBUTE_VENDOR] = dataset.VALUE_UNKNOWN;
201 | return result;
202 | };
203 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "woothee",
3 | "version": "1.11.0",
4 | "description": "User-Agent string parser (js implementation)",
5 | "main": "./release/woothee",
6 | "devDependencies": {
7 | "chai": ">= 1.3.0",
8 | "js-yaml": ">= 1.0.3",
9 | "mocha": "^6.0.0",
10 | "serialize-to-js": "^3.0.0",
11 | "should": "~1.2.2"
12 | },
13 | "scripts": {
14 | "test": "make test"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "https://github.com/woothee/woothee-js"
19 | },
20 | "author": "tagomoris",
21 | "license": "Apache-2.0"
22 | }
23 |
--------------------------------------------------------------------------------
/release/woothee.js:
--------------------------------------------------------------------------------
1 | (function (){
2 | var root = this;
3 | // embed: dataset, util, browser, mobilephone, crawler, appliance, misc, woothee
4 |
5 | // GENERATED from dataset.yaml at Tue Sep 7 11:33:54 JST 2021 by tagomoris
6 |
7 | // Snapshot from package.json
8 | var package_info = {"name":"woothee","version":"1.11.0","description":"User-Agent string parser (js implementation)","main":"./release/woothee","devDependencies":{"chai":">= 1.3.0","js-yaml":">= 1.0.3","mocha":"^6.0.0","serialize-to-js":"^3.0.0","should":"~1.2.2"},"scripts":{"test":"make test"},"repository":{"type":"git","url":"https://github.com/woothee/woothee-js"},"author":"tagomoris","license":"Apache-2.0"};
9 |
10 | var dataset = {};
11 | (function(){
12 | var exports = dataset;
13 | /* CODE: dataset.js */
14 | var KEY_LABEL = exports.KEY_LABEL = 'label',
15 | KEY_NAME = exports.KEY_NAME = 'name',
16 | KEY_TYPE = exports.KEY_TYPE = 'type',
17 | KEY_CATEGORY = exports.KEY_CATEGORY = 'category',
18 | KEY_OS = exports.KEY_OS = 'os',
19 | KEY_OS_VERSION = exports.KEY_OS_VERSION = 'os_version',
20 | KEY_VENDOR = exports.KEY_VENDOR = 'vendor',
21 | KEY_VERSION = exports.KEY_VERSION = 'version';
22 | var TYPE_BROWSER = 'browser',
23 | TYPE_OS = 'os',
24 | TYPE_FULL = 'full';
25 | var CATEGORY_PC = exports.CATEGORY_PC = 'pc',
26 | CATEGORY_SMARTPHONE = exports.CATEGORY_SMARTPHONE = 'smartphone',
27 | CATEGORY_MOBILEPHONE = exports.CATEGORY_MOBILEPHONE = 'mobilephone',
28 | CATEGORY_CRAWLER = exports.CATEGORY_CRAWLER = 'crawler',
29 | CATEGORY_APPLIANCE = exports.CATEGORY_APPLIANCE = 'appliance',
30 | CATEGORY_MISC = exports.CATEGORY_MISC = 'misc';
31 | var ATTRIBUTE_NAME = exports.ATTRIBUTE_NAME = 'name',
32 | ATTRIBUTE_CATEGORY = exports.ATTRIBUTE_CATEGORY = 'category',
33 | ATTRIBUTE_OS = exports.ATTRIBUTE_OS = 'os',
34 | ATTRIBUTE_OS_VERSION = exports.ATTRIBUTE_OS_VERSION = 'os_version',
35 | ATTRIBUTE_VENDOR = exports.ATTRIBUTE_VENDOR = 'vendor',
36 | ATTRIBUTE_VERSION = exports.ATTRIBUTE_VERSION = 'version';
37 | var VALUE_UNKNOWN = exports.VALUE_UNKNOWN = 'UNKNOWN';
38 | var CATEGORY_LIST = exports.CATEGORY_LIST = [
39 | CATEGORY_PC, CATEGORY_SMARTPHONE, CATEGORY_MOBILEPHONE,
40 | CATEGORY_CRAWLER, CATEGORY_APPLIANCE, CATEGORY_MISC, VALUE_UNKNOWN
41 | ];
42 | var ATTRIBUTE_LIST = exports.ATTRIBUTE_LIST = [ATTRIBUTE_NAME, ATTRIBUTE_CATEGORY, ATTRIBUTE_OS, ATTRIBUTE_VENDOR, ATTRIBUTE_VERSION, ATTRIBUTE_OS_VERSION];
43 | // GENERATED from dataset.yaml at Tue Sep 7 11:33:54 JST 2021 by tagomoris
44 | var DATASET = {MSIE: {label: "MSIE", name: "Internet Explorer", type: "browser", vendor: "Microsoft"}, Edge: {label: "Edge", name: "Edge", type: "browser", vendor: "Microsoft"}, Chrome: {label: "Chrome", name: "Chrome", type: "browser", vendor: "Google"}, Safari: {label: "Safari", name: "Safari", type: "browser", vendor: "Apple"}, Firefox: {label: "Firefox", name: "Firefox", type: "browser", vendor: "Mozilla"}, Opera: {label: "Opera", name: "Opera", type: "browser", vendor: "Opera"}, Vivaldi: {label: "Vivaldi", name: "Vivaldi", type: "browser", vendor: "Vivaldi Technologies"}, Sleipnir: {label: "Sleipnir", name: "Sleipnir", type: "browser", vendor: "Fenrir Inc."}, GSA: {label: "GSA", name: "Google Search App", type: "browser", vendor: "Google"}, Webview: {label: "Webview", name: "Webview", type: "browser", vendor: "OS vendor"}, YaBrowser: {label: "YaBrowser", name: "Yandex Browser", type: "browser", vendor: "Yandex"}, Win: {label: "Win", name: "Windows UNKNOWN Ver", type: "os", category: "pc"}, Win10: {label: "Win10", name: "Windows 10", type: "os", category: "pc"}, "Win8.1": {label: "Win8.1", name: "Windows 8.1", type: "os", category: "pc"}, Win8: {label: "Win8", name: "Windows 8", type: "os", category: "pc"}, Win7: {label: "Win7", name: "Windows 7", type: "os", category: "pc"}, WinVista: {label: "WinVista", name: "Windows Vista", type: "os", category: "pc"}, WinXP: {label: "WinXP", name: "Windows XP", type: "os", category: "pc"}, Win2000: {label: "Win2000", name: "Windows 2000", type: "os", category: "pc"}, WinNT4: {label: "WinNT4", name: "Windows NT 4.0", type: "os", category: "pc"}, WinMe: {label: "WinMe", name: "Windows Me", type: "os", category: "pc"}, Win98: {label: "Win98", name: "Windows 98", type: "os", category: "pc"}, Win95: {label: "Win95", name: "Windows 95", type: "os", category: "pc"}, WinPhone: {label: "WinPhone", name: "Windows Phone OS", type: "os", category: "smartphone"}, WinCE: {label: "WinCE", name: "Windows CE", type: "os", category: "smartphone"}, OSX: {label: "OSX", name: "Mac OSX", type: "os", category: "pc"}, MacOS: {label: "MacOS", name: "Mac OS Classic", type: "os", category: "pc"}, Linux: {label: "Linux", name: "Linux", type: "os", category: "pc"}, BSD: {label: "BSD", name: "BSD", type: "os", category: "pc"}, ChromeOS: {label: "ChromeOS", name: "ChromeOS", type: "os", category: "pc"}, Android: {label: "Android", name: "Android", type: "os", category: "smartphone"}, iPhone: {label: "iPhone", name: "iPhone", type: "os", category: "smartphone"}, iPad: {label: "iPad", name: "iPad", type: "os", category: "smartphone"}, iPod: {label: "iPod", name: "iPod", type: "os", category: "smartphone"}, iOS: {label: "iOS", name: "iOS", type: "os", category: "smartphone"}, FirefoxOS: {label: "FirefoxOS", name: "Firefox OS", type: "os", category: "smartphone"}, BlackBerry: {label: "BlackBerry", name: "BlackBerry", type: "os", category: "smartphone"}, BlackBerry10: {label: "BlackBerry10", name: "BlackBerry 10", type: "os", category: "smartphone"}, docomo: {label: "docomo", name: "docomo", type: "full", vendor: "docomo", category: "mobilephone", os: "docomo"}, au: {label: "au", name: "au by KDDI", type: "full", vendor: "au", category: "mobilephone", os: "au"}, SoftBank: {label: "SoftBank", name: "SoftBank Mobile", type: "full", vendor: "SoftBank", category: "mobilephone", os: "SoftBank"}, willcom: {label: "willcom", name: "WILLCOM", type: "full", vendor: "WILLCOM", category: "mobilephone", os: "WILLCOM"}, jig: {label: "jig", name: "jig browser", type: "full", vendor: "", category: "mobilephone", os: "jig"}, emobile: {label: "emobile", name: "emobile", type: "full", vendor: "", category: "mobilephone", os: "emobile"}, SymbianOS: {label: "SymbianOS", name: "SymbianOS", type: "full", vendor: "", category: "mobilephone", os: "SymbianOS"}, MobileTranscoder: {label: "MobileTranscoder", name: "Mobile Transcoder", type: "full", vendor: "", category: "mobilephone", os: "Mobile Transcoder"}, Nintendo3DS: {label: "Nintendo3DS", name: "Nintendo 3DS", type: "full", vendor: "Nintendo", category: "appliance", os: "Nintendo 3DS"}, NintendoDSi: {label: "NintendoDSi", name: "Nintendo DSi", type: "full", vendor: "Nintendo", category: "appliance", os: "Nintendo DSi"}, NintendoWii: {label: "NintendoWii", name: "Nintendo Wii", type: "full", vendor: "Nintendo", category: "appliance", os: "Nintendo Wii"}, NintendoWiiU: {label: "NintendoWiiU", name: "Nintendo Wii U", type: "full", vendor: "Nintendo", category: "appliance", os: "Nintendo Wii U"}, PSP: {label: "PSP", name: "PlayStation Portable", type: "full", vendor: "Sony", category: "appliance", os: "PlayStation Portable"}, PSVita: {label: "PSVita", name: "PlayStation Vita", type: "full", vendor: "Sony", category: "appliance", os: "PlayStation Vita"}, PS3: {label: "PS3", name: "PlayStation 3", type: "full", vendor: "Sony", category: "appliance", os: "PlayStation 3"}, PS4: {label: "PS4", name: "PlayStation 4", type: "full", vendor: "Sony", category: "appliance", os: "PlayStation 4"}, Xbox360: {label: "Xbox360", name: "Xbox 360", type: "full", vendor: "Microsoft", category: "appliance", os: "Xbox 360"}, XboxOne: {label: "XboxOne", name: "Xbox One", type: "full", vendor: "Microsoft", category: "appliance", os: "Xbox One"}, DigitalTV: {label: "DigitalTV", name: "InternetTVBrowser", type: "full", vendor: "", category: "appliance", os: "DigitalTV"}, SafariRSSReader: {label: "SafariRSSReader", name: "Safari RSSReader", type: "full", vendor: "Apple", category: "misc"}, GoogleDesktop: {label: "GoogleDesktop", name: "Google Desktop", type: "full", vendor: "Google", category: "misc"}, WindowsRSSReader: {label: "WindowsRSSReader", name: "Windows RSSReader", type: "full", vendor: "Microsoft", category: "misc"}, VariousRSSReader: {label: "VariousRSSReader", name: "RSSReader", type: "full", vendor: "", category: "misc"}, HTTPLibrary: {label: "HTTPLibrary", name: "HTTP Library", type: "full", vendor: "", category: "misc"}, GoogleBot: {label: "GoogleBot", name: "Googlebot", type: "full", vendor: "", category: "crawler"}, GoogleBotMobile: {label: "GoogleBotMobile", name: "Googlebot Mobile", type: "full", vendor: "", category: "crawler"}, GoogleMediaPartners: {label: "GoogleMediaPartners", name: "Google Mediapartners", type: "full", vendor: "", category: "crawler"}, GoogleFeedFetcher: {label: "GoogleFeedFetcher", name: "Google Feedfetcher", type: "full", vendor: "", category: "crawler"}, GoogleAppEngine: {label: "GoogleAppEngine", name: "Google AppEngine", type: "full", vendor: "", category: "crawler"}, GoogleWebPreview: {label: "GoogleWebPreview", name: "Google Web Preview", type: "full", vendor: "", category: "crawler"}, YahooSlurp: {label: "YahooSlurp", name: "Yahoo! Slurp", type: "full", vendor: "", category: "crawler"}, YahooJP: {label: "YahooJP", name: "Yahoo! Japan", type: "full", vendor: "", category: "crawler"}, YahooPipes: {label: "YahooPipes", name: "Yahoo! Pipes", type: "full", vendor: "", category: "crawler"}, Baiduspider: {label: "Baiduspider", name: "Baiduspider", type: "full", vendor: "", category: "crawler"}, msnbot: {label: "msnbot", name: "msnbot", type: "full", vendor: "", category: "crawler"}, bingbot: {label: "bingbot", name: "bingbot", type: "full", vendor: "", category: "crawler"}, BingPreview: {label: "BingPreview", name: "BingPreview", type: "full", vendor: "", category: "crawler"}, Yeti: {label: "Yeti", name: "Naver Yeti", type: "full", vendor: "", category: "crawler"}, FeedBurner: {label: "FeedBurner", name: "Google FeedBurner", type: "full", vendor: "", category: "crawler"}, facebook: {label: "facebook", name: "facebook", type: "full", vendor: "", category: "crawler"}, twitter: {label: "twitter", name: "twitter", type: "full", vendor: "", category: "crawler"}, trendictionbot: {label: "trendictionbot", name: "trendiction", type: "full", vendor: "", category: "crawler"}, mixi: {label: "mixi", name: "mixi", type: "full", vendor: "", category: "crawler"}, IndyLibrary: {label: "IndyLibrary", name: "Indy Library", type: "full", vendor: "", category: "crawler"}, ApplePubSub: {label: "ApplePubSub", name: "Apple iCloud", type: "full", vendor: "", category: "crawler"}, Genieo: {label: "Genieo", name: "Genieo Web Filter", type: "full", vendor: "", category: "crawler"}, topsyButterfly: {label: "topsyButterfly", name: "topsy Butterfly", type: "full", vendor: "", category: "crawler"}, rogerbot: {label: "rogerbot", name: "SeoMoz rogerbot", type: "full", vendor: "", category: "crawler"}, AhrefsBot: {label: "AhrefsBot", name: "ahref AhrefsBot", type: "full", vendor: "", category: "crawler"}, radian6: {label: "radian6", name: "salesforce radian6", type: "full", vendor: "", category: "crawler"}, Hatena: {label: "Hatena", name: "Hatena", type: "full", vendor: "", category: "crawler"}, goo: {label: "goo", name: "goo", type: "full", vendor: "", category: "crawler"}, livedoorFeedFetcher: {label: "livedoorFeedFetcher", name: "livedoor FeedFetcher", type: "full", vendor: "", category: "crawler"}, VariousCrawler: {label: "VariousCrawler", name: "misc crawler", type: "full", vendor: "", category: "crawler"}};
45 | var get = exports.get = function(label) {
46 | return DATASET[label];
47 | };
48 |
49 | })();
50 | var util = {};
51 | (function(){
52 | var exports = util;
53 | /* CODE: util.js */
54 | var updateMap = exports.updateMap = function(target, source) {
55 | for (var key in source) {
56 | if (key === dataset.KEY_LABEL || key === dataset.KEY_TYPE)
57 | continue;
58 | if (source[key] && source[key].length > 0)
59 | target[key] = source[key];
60 | }
61 | };
62 | var updateCategory = exports.updateCategory = function(target, category) {
63 | target[dataset.ATTRIBUTE_CATEGORY] = category;
64 | };
65 | var updateVersion = exports.updateVersion = function(target, version) {
66 | target[dataset.ATTRIBUTE_VERSION] = version;
67 | };
68 | var updateOs = exports.updateOs = function(target, os) {
69 | target[dataset.ATTRIBUTE_OS] = os;
70 | };
71 | var updateOsVersion = exports.updateOsVersion = function(target, version) {
72 | target[dataset.ATTRIBUTE_OS_VERSION] = version;
73 | };
74 |
75 | })();
76 | var browser = {};
77 | (function(){
78 | var exports = browser;
79 | /* CODE: browser.js */
80 | var msiePattern = /MSIE ([.0-9]+);/;
81 | var tridentPattern = /Trident\/[.0-9]+;/;
82 | var tridentVersionPattern = / rv:([.0-9]+)/;
83 | var iemobilePattern = /IEMobile\/([.0-9]+);/;
84 | var challengeMSIE = exports.challengeMSIE = function(ua, result) {
85 | if (ua.indexOf('compatible; MSIE') < 0 && ua.indexOf('Trident/') < 0 && ua.indexOf('IEMobile/'))
86 | return false;
87 | var version;
88 | var match = msiePattern.exec(ua);
89 | if (!match)
90 | if (tridentPattern.exec(ua))
91 | match = tridentVersionPattern.exec(ua);
92 | if (!match)
93 | match = iemobilePattern.exec(ua);
94 | if (match)
95 | version = match[1];
96 | else
97 | version = dataset.VALUE_UNKNOWN;
98 | updateMap(result, dataset.get('MSIE'));
99 | updateVersion(result, version);
100 | return true;
101 | };
102 | var yandexBrowserPattern = /YaBrowser\/([.0-9]+)/;
103 | var challengeYandexBrowser = exports.challengeYandexBrowser = function(ua, result) {
104 | if (ua.indexOf('YaBrowser/') < 0)
105 | return false;
106 | var version;
107 | var match = yandexBrowserPattern.exec(ua);
108 | if (match) {
109 | version = match[1];
110 | } else {
111 | version = dataset.VALUE_UNKNOWN;
112 | }
113 | updateMap(result, dataset.get('YaBrowser'));
114 | updateVersion(result, version);
115 | return true;
116 | };
117 | var edgePattern = /(?:Edge|Edg|EdgiOS|EdgA)\/([.0-9]+)/;
118 | var firefoxiOSPattern = /FxiOS\/([.0-9]+)/;
119 | var chromePattern = /(?:Chrome|CrMo|CriOS)\/([.0-9]+)/;
120 | var operaBlinkPattern = /OPR\/([.0-9]+)/;
121 | var gsaPattern = /GSA\/([.0-9]+)/;
122 | var safariPattern = /Version\/([.0-9]+)/;
123 | var challengeSafariChrome = exports.challengeSafariChrome = function(ua, result) {
124 | if (ua.indexOf('Safari/') < 0)
125 | return false;
126 | if (ua.indexOf('Chrome') >= 0 && ua.indexOf('wv') >= 0)
127 | return false;
128 | var version = dataset.VALUE_UNKNOWN;
129 | var match;
130 | if ((match = edgePattern.exec(ua))) {
131 | version = match[1];
132 | updateMap(result, dataset.get('Edge'));
133 | updateVersion(result, version);
134 | return true;
135 | }
136 | if ((match = firefoxiOSPattern.exec(ua))) {
137 | version = match[1];
138 | updateMap(result, dataset.get('Firefox'));
139 | updateVersion(result, version);
140 | return true;
141 | }
142 | if ((match = chromePattern.exec(ua))) {
143 | var matchOpera;
144 | if ((matchOpera = operaBlinkPattern.exec(ua))) {
145 | // Opera w/ blink
146 | version = matchOpera[1];
147 | updateMap(result, dataset.get('Opera'));
148 | updateVersion(result, version);
149 | return true;
150 | }
151 | // Chrome
152 | version = match[1];
153 | updateMap(result, dataset.get('Chrome'));
154 | updateVersion(result, version);
155 | return true;
156 | }
157 | if ((match = gsaPattern.exec(ua))) {
158 | version = match[1];
159 | updateMap(result, dataset.get('GSA'));
160 | updateVersion(result, version);
161 | return true;
162 | }
163 | if ((match = safariPattern.exec(ua))) {
164 | version = match[1];
165 | }
166 | updateMap(result, dataset.get('Safari'));
167 | updateVersion(result, version);
168 | return true;
169 | };
170 | var firefoxPattern = /Firefox\/([.0-9]+)/;
171 | var challengeFirefox = exports.challengeFirefox = function(ua, result) {
172 | if (ua.indexOf('Firefox/') < 0)
173 | return false;
174 | var version = dataset.VALUE_UNKNOWN;
175 | var match = firefoxPattern.exec(ua);
176 | if (match)
177 | version = match[1];
178 | updateMap(result, dataset.get('Firefox'));
179 | updateVersion(result, version);
180 | return true;
181 | };
182 | var operaPattern1 = /Version\/([.0-9]+)/;
183 | var operaPattern2 = /Opera[\/ ]([.0-9]+)/;
184 | var challengeOpera = exports.challengeOpera = function(ua, result) {
185 | if (ua.indexOf('Opera') < 0)
186 | return false;
187 | var version = dataset.VALUE_UNKNOWN;
188 | var match = operaPattern1.exec(ua);
189 | if (match) {
190 | version = match[1];
191 | } else {
192 | match = operaPattern2.exec(ua);
193 | if (match)
194 | version = match[1];
195 | }
196 | updateMap(result, dataset.get('Opera'));
197 | updateVersion(result, version);
198 | return true;
199 | };
200 | var webviewPattern = /iP(hone;|ad;|od) .*like Mac OS X/;
201 | var webviewVersionPattern = /Version\/([.0-9]+)/;
202 | var challengeWebview = exports.challengeWebview = function(ua, result) {
203 | var version = dataset.VALUE_UNKNOWN;
204 | // Android(Lollipop and Above)
205 | if (ua.indexOf('Chrome') >= 0 && ua.indexOf('wv') >= 0) {
206 | var vmatch = webviewVersionPattern.exec(ua);
207 | if (vmatch)
208 | version = vmatch[1];
209 | updateMap(result, dataset.get('Webview'));
210 | updateVersion(result, version);
211 | return true;
212 | }
213 | // iOS
214 | var match = webviewPattern.exec(ua);
215 | if (match) {
216 | if (ua.indexOf('Safari/') > -1)
217 | return false;
218 | var vmatch = webviewVersionPattern.exec(ua);
219 | if (vmatch)
220 | version = vmatch[1];
221 | updateMap(result, dataset.get('Webview'));
222 | updateVersion(result, version);
223 | return true;
224 | }
225 | return false;
226 | };
227 | var sleipnirPattern = /Sleipnir\/([.0-9]+)/;
228 | var challengeSleipnir = exports.challengeSleipnir = function(ua, result) {
229 | if (ua.indexOf('Sleipnir/') < 0)
230 | return false;
231 | var version = dataset.VALUE_UNKNOWN;
232 | var match = sleipnirPattern.exec(ua);
233 | if (match)
234 | version = match[1];
235 | updateMap(result, dataset.get('Sleipnir'));
236 | updateVersion(result, version);
237 | /*
238 | Sleipnir's user-agent doesn't contain Windows version, so put 'Windows UNKNOWN Ver'.
239 | Sleipnir is IE component browser, so for Windows only.
240 | */
241 | var win = dataset.get('Win');
242 | updateCategory(result, win[dataset.KEY_CATEGORY]);
243 | updateOs(result, win[dataset.KEY_NAME]);
244 | return true;
245 | };
246 | var vivaldiPattern = /Vivaldi\/([.0-9]+)/;
247 | var challengeVivaldi = exports.challengeVivaldi = function(ua, result) {
248 | if (ua.indexOf('Vivaldi/') < 0)
249 | return false;
250 | var version = dataset.VALUE_UNKNOWN;
251 | var match = vivaldiPattern.exec(ua);
252 | if (match)
253 | version = match[1];
254 | updateMap(result, dataset.get('Vivaldi'));
255 | updateVersion(result, version);
256 | return true;
257 | };
258 |
259 | })();
260 | var os = {};
261 | (function(){
262 | var exports = os;
263 | /* CODE: os.js */
264 | var windowsPattern = /Windows ([ .a-zA-Z0-9]+)[;\\)]/;
265 | var windowsPhonePattern = /^Phone(?: OS)? ([.0-9]+)/;
266 | var challengeWindows = exports.challengeWindows = function(ua, result) {
267 | if (ua.indexOf('Windows') < 0)
268 | return false;
269 | // Xbox Series
270 | if (ua.indexOf('Xbox') > -1) {
271 | var d;
272 | if (ua.indexOf('Xbox; Xbox One)') > -1)
273 | d = dataset.get("XboxOne");
274 | else
275 | d = dataset.get("Xbox360");
276 | // overwrite browser detections as appliance
277 | updateMap(result, d);
278 | return true;
279 | }
280 | var data = dataset.get('Win');
281 | var match = windowsPattern.exec(ua);
282 | if (!match) {
283 | // Windows, but version unknown
284 | updateCategory(result, data[dataset.KEY_CATEGORY]);
285 | updateOs(result, data[dataset.KEY_NAME]);
286 | return true;
287 | }
288 | var version = match[1];
289 | if (version === 'NT 10.0') data = dataset.get('Win10');
290 | else if (version === 'NT 6.3') data = dataset.get('Win8.1');
291 | else if (version === 'NT 6.2') data = dataset.get('Win8'); // "NT 6.2; ARM;" means Windows RT, oh....
292 | else if (version === 'NT 6.1') data = dataset.get('Win7');
293 | else if (version === 'NT 6.0') data = dataset.get('WinVista');
294 | else if (version === 'NT 5.1') data = dataset.get('WinXP');
295 | else if ((match = windowsPhonePattern.exec(version))) {
296 | data = dataset.get('WinPhone');
297 | version = match[1];
298 | }
299 | else if (version === 'NT 5.0') data = dataset.get('Win2000');
300 | else if (version === 'NT 4.0') data = dataset.get('WinNT4');
301 | else if (version === '98') data = dataset.get('Win98'); // wow, WinMe is shown as 'Windows 98; Win9x 4.90', fxxxk
302 | else if (version === '95') data = dataset.get('Win95');
303 | else if (version === 'CE') data = dataset.get('WinCE');
304 | /*
305 | else
306 | // windows unknown
307 | */
308 | updateCategory(result, data[dataset.KEY_CATEGORY]);
309 | updateOs(result, data[dataset.KEY_NAME]);
310 | updateOsVersion(result, version);
311 | return true;
312 | };
313 | var challengeOSX = exports.challengeOSX = function(ua, result) {
314 | if (ua.indexOf('Mac OS X') < 0)
315 | return false;
316 | var data = dataset.get('OSX');
317 | var version;
318 | var match;
319 | if (ua.indexOf('like Mac OS X') >= 0) {
320 | if (ua.indexOf('iPhone;') >= 0) data = dataset.get('iPhone');
321 | else if (ua.indexOf('iPad;') >= 0) data = dataset.get('iPad');
322 | else if (ua.indexOf('iPod') >= 0) data = dataset.get('iPod');
323 | if ((match = /; CPU(?: iPhone)? OS (\d+_\d+(?:_\d+)?) like Mac OS X/.exec(ua)))
324 | version = match[1].replace(/_/g, '.');
325 | }
326 | else {
327 | if ((match = /Mac OS X (10[._]\d+(?:[._]\d+)?)(?:\)|;)/.exec(ua))) {
328 | version = match[1].replace(/_/g, '.');
329 | }
330 | }
331 | updateCategory(result, data[dataset.KEY_CATEGORY]);
332 | updateOs(result, data[dataset.KEY_NAME]);
333 | if (version)
334 | updateOsVersion(result, version);
335 | return true;
336 | };
337 | var challengeLinux = exports.challengeLinux = function(ua, result) {
338 | if (ua.indexOf('Linux') < 0)
339 | return false;
340 | var data = dataset.get('Linux');
341 | var os_version;
342 | var match;
343 | if (ua.indexOf('Android') >= 0) {
344 | data = dataset.get('Android');
345 | if ((match = /Android[- ](\d+(?:\.\d+(?:\.\d+)?)?)/.exec(ua)))
346 | os_version = match[1];
347 | }
348 | updateCategory(result, data[dataset.KEY_CATEGORY]);
349 | updateOs(result, data[dataset.KEY_NAME]);
350 | if (os_version)
351 | updateOsVersion(result, os_version);
352 | return true;
353 | };
354 | var challengeSmartPhone = exports.challengeSmartPhone = function(ua, result) {
355 | var data = null;
356 | var os_version = null;
357 | var match;
358 | if (ua.indexOf('iPhone') >= 0) data = dataset.get('iPhone');
359 | else if (ua.indexOf('iPad') >= 0) data = dataset.get('iPad');
360 | else if (ua.indexOf('iPod') >= 0) data = dataset.get('iPod');
361 | else if (ua.indexOf('Android') >= 0) data = dataset.get('Android');
362 | else if (ua.indexOf('CFNetwork') >= 0) data = dataset.get('iOS');
363 | else if (ua.indexOf('BB10') >= 0) {
364 | data = dataset.get('BlackBerry10');
365 | if ((match = /BB10(?:.+)Version\/([.0-9]+)/.exec(ua)))
366 | os_version = match[1];
367 | }
368 | else if (ua.indexOf('BlackBerry') >= 0) {
369 | data = dataset.get('BlackBerry');
370 | if ((match = /BlackBerry(?:\d+)\/([.0-9]+) /.exec(ua)))
371 | os_version = match[1];
372 | }
373 | if (result[dataset.KEY_NAME] && result[dataset.KEY_NAME] === dataset.get('Firefox')[dataset.KEY_NAME]) {
374 | // Firefox OS specific pattern
375 | // http://lawrencemandel.com/2012/07/27/decision-made-firefox-os-user-agent-string/
376 | // https://github.com/woothee/woothee/issues/2
377 | if ((match = /^Mozilla\/[.0-9]+ \((?:Mobile|Tablet);(?:.*;)? rv:([.0-9]+)\) Gecko\/[.0-9]+ Firefox\/[.0-9]+$/.exec(ua))) {
378 | data = dataset.get('FirefoxOS');
379 | os_version = match[1];
380 | }
381 | }
382 | if (!data)
383 | return false;
384 | updateCategory(result, data[dataset.KEY_CATEGORY]);
385 | updateOs(result, data[dataset.KEY_NAME]);
386 | if (os_version)
387 | updateOsVersion(result, os_version);
388 | return true;
389 | };
390 | var challengeMobilePhone = exports.challengeMobilePhone = function(ua, result) {
391 | var data,match;
392 | if (ua.indexOf('KDDI-') >= 0) {
393 | if ((match = /KDDI-([^- \/;()"']+)/.exec(ua))) {
394 | var term = match[1];
395 | data = dataset.get('au');
396 | updateCategory(result, data[dataset.KEY_CATEGORY]);
397 | updateOs(result, data[dataset.KEY_OS]);
398 | updateVersion(result, term);
399 | return true;
400 | }
401 | }
402 | if (ua.indexOf('WILLCOM') >= 0 || ua.indexOf('DDIPOCKET') >= 0) {
403 | if ((match = /(?:WILLCOM|DDIPOCKET);[^\/]+\/([^ \/;()]+)/.exec(ua))) {
404 | term = match[1];
405 | data = dataset.get('willcom');
406 | updateCategory(result, data[dataset.KEY_CATEGORY]);
407 | updateOs(result, data[dataset.KEY_OS]);
408 | updateVersion(result, term);
409 | return true;
410 | }
411 | }
412 | if (ua.indexOf('SymbianOS') >= 0) {
413 | data = dataset.get('SymbianOS');
414 | updateCategory(result, data[dataset.KEY_CATEGORY]);
415 | updateOs(result, data[dataset.KEY_OS]);
416 | return true;
417 | }
418 | if (ua.indexOf('Google Wireless Transcoder') >= 0) {
419 | updateMap(result, dataset.get('MobileTranscoder'));
420 | updateVersion(result, 'Google');
421 | return true;
422 | }
423 | if (ua.indexOf('Naver Transcoder') >= 0) {
424 | updateMap(result, dataset.get('MobileTranscoder'));
425 | updateVersion(result, 'Naver');
426 | return true;
427 | }
428 | return false;
429 | };
430 | var challengeAppliance = exports.challengeAppliance = function(ua, result) {
431 | var data;
432 | if (ua.indexOf('Nintendo DSi;') >= 0) {
433 | data = dataset.get('NintendoDSi');
434 | updateCategory(result, data[dataset.KEY_CATEGORY]);
435 | updateOs(result, data[dataset.KEY_OS]);
436 | return true;
437 | }
438 | if (ua.indexOf('Nintendo Wii;') >= 0) {
439 | data = dataset.get('NintendoWii');
440 | updateCategory(result, data[dataset.KEY_CATEGORY]);
441 | updateOs(result, data[dataset.KEY_OS]);
442 | return true;
443 | }
444 | return false;
445 | };
446 | var challengeMisc = exports.challengeMisc = function(ua, result) {
447 | var data;
448 | var osVersion;
449 | var match;
450 | if (ua.indexOf('(Win98;') >= 0) {
451 | data = dataset.get('Win98');
452 | osVersion = "98";
453 | } else if (ua.indexOf('Macintosh; U; PPC;') >= 0) {
454 | data = dataset.get('MacOS');
455 | if ((match = /rv:(\d+\.\d+\.\d+)/.exec(ua)))
456 | osVersion = match[1];
457 | }
458 | else if (ua.indexOf('Mac_PowerPC') >= 0) data = dataset.get('MacOS');
459 | else if (ua.indexOf('X11; FreeBSD ') >= 0) {
460 | data = dataset.get('BSD');
461 | if ((match = /FreeBSD ([^;\)]+);/.exec(ua)))
462 | osVersion = match[1];
463 | }
464 | else if (ua.indexOf('X11; CrOS ') >= 0) {
465 | data = dataset.get('ChromeOS');
466 | if ((match = /CrOS ([^\)]+)\)/.exec(ua)))
467 | osVersion = match[1];
468 | }
469 | if (data) {
470 | updateCategory(result, data[dataset.KEY_CATEGORY]);
471 | updateOs(result, data[dataset.KEY_NAME]);
472 | if (osVersion)
473 | updateOsVersion(result, osVersion);
474 | return true;
475 | }
476 | return false;
477 | };
478 |
479 | })();
480 | var mobilephone = {};
481 | (function(){
482 | var exports = mobilephone;
483 | /* CODE: mobilephone.js */
484 | var docomoPattern = /DoCoMo\/[.0-9]+[ \/]([^- \/;()"']+)/;
485 | var fomaPattern = /\(([^;)]+);FOMA;/;
486 | var challengeDocomo = exports.challengeDocomo = function(ua, result) {
487 | if (ua.indexOf('DoCoMo') < 0 && ua.indexOf(';FOMA;') < 0)
488 | return false;
489 | var version = dataset.VALUE_UNKNOWN;
490 | var match;
491 | if ((match = docomoPattern.exec(ua))) {
492 | version = match[1];
493 | } else if ((match = fomaPattern.exec(ua))) {
494 | version = match[1];
495 | }
496 | updateMap(result, dataset.get('docomo'));
497 | updateVersion(result, version);
498 | return true;
499 | };
500 | var kddiPattern = /KDDI-([^- \/;()"']+)/;
501 | var challengeAu = exports.challengeAu = function(ua, result) {
502 | if (ua.indexOf('KDDI-') < 0)
503 | return false;
504 | var version = dataset.VALUE_UNKNOWN;
505 | var match = kddiPattern.exec(ua);
506 | if (match)
507 | version = match[1];
508 | updateMap(result, dataset.get('au'));
509 | updateVersion(result, version);
510 | return true;
511 | };
512 | var softbankPattern = /(?:SoftBank|Vodafone|J-PHONE)\/[.0-9]+\/([^ \/;()]+)/;
513 | var challengeSoftbank = exports.challengeSoftbank = function(ua, result) {
514 | if (ua.indexOf('SoftBank') < 0 && ua.indexOf('Vodafone') < 0 && ua.indexOf('J-PHONE') < 0)
515 | return false;
516 | var version = dataset.VALUE_UNKNOWN;
517 | var match = softbankPattern.exec(ua);
518 | if (match)
519 | version = match[1];
520 | updateMap(result, dataset.get('SoftBank'));
521 | updateVersion(result, version);
522 | return true;
523 | };
524 | var willcomPattern = /(?:WILLCOM|DDIPOCKET);[^\/]+\/([^ \/;()]+)/;
525 | var challengeWillcom = exports.challengeWillcom = function(ua, result) {
526 | if (ua.indexOf('WILLCOM') < 0 && ua.indexOf('DDIPOCKET') < 0)
527 | return false;
528 | var version = dataset.VALUE_UNKNOWN;
529 | var match = willcomPattern.exec(ua);
530 | if (match)
531 | version = match[1];
532 | updateMap(result, dataset.get('willcom'));
533 | updateVersion(result, version);
534 | return true;
535 | };
536 | var challengeMisc = exports.challengeMisc = function(ua, result) {
537 | var match;
538 | if (ua.indexOf('jig browser') >= 0) {
539 | updateMap(result, dataset.get('jig'));
540 | if ((match = /jig browser[^;]+; ([^);]+)/.exec(ua))) {
541 | updateVersion(result, match[1]);
542 | }
543 | return true;
544 | }
545 | if (ua.indexOf('emobile/') >= 0 || ua.indexOf('OpenBrowser') >= 0 || ua.indexOf('Browser/Obigo-Browser') >= 0) {
546 | updateMap(result, dataset.get('emobile'));
547 | return true;
548 | }
549 | if (ua.indexOf('SymbianOS') >= 0) {
550 | updateMap(result, dataset.get('SymbianOS'));
551 | return true;
552 | }
553 | if (ua.indexOf('Hatena-Mobile-Gateway/') >= 0) {
554 | updateMap(result, dataset.get('MobileTranscoder'));
555 | updateVersion(result, 'Hatena');
556 | return true;
557 | }
558 | if (ua.indexOf('livedoor-Mobile-Gateway/') >= 0) {
559 | updateMap(result, dataset.get('MobileTranscoder'));
560 | updateVersion(result, 'livedoor');
561 | return true;
562 | }
563 | return false;
564 | };
565 |
566 | })();
567 | var crawler = {};
568 | (function(){
569 | var exports = crawler;
570 | /* CODE: crawler.js */
571 | var challengeGoogle = exports.challengeGoogle = function(ua, result) {
572 | if (ua.indexOf('Google') < 0)
573 | return false;
574 | if (ua.indexOf('compatible; Googlebot') >= 0) {
575 | if (ua.indexOf('compatible; Googlebot-Mobile') >= 0) {
576 | updateMap(result, dataset.get('GoogleBotMobile'));
577 | return true;
578 | } else {
579 | updateMap(result, dataset.get('GoogleBot'));
580 | return true;
581 | }
582 | }
583 | if (ua.indexOf('Googlebot-Image/') >= 0) {
584 | updateMap(result, dataset.get('GoogleBot'));
585 | return true;
586 | }
587 | if (ua.indexOf('Mediapartners-Google') >= 0) {
588 | if (ua.indexOf('compatible; Mediapartners-Google') >= 0 || ua === 'Mediapartners-Google') {
589 | updateMap(result, dataset.get('GoogleMediaPartners'));
590 | return true;
591 | }
592 | }
593 | if (ua.indexOf('Feedfetcher-Google;') >= 0) {
594 | updateMap(result, dataset.get('GoogleFeedFetcher'));
595 | return true;
596 | }
597 | if (ua.indexOf('AppEngine-Google') >= 0) {
598 | updateMap(result, dataset.get('GoogleAppEngine'));
599 | return true;
600 | }
601 | if (ua.indexOf('Google Web Preview') >= 0) {
602 | updateMap(result, dataset.get('GoogleWebPreview'));
603 | return true;
604 | }
605 | return false;
606 | };
607 | var challengeCrawlers = exports.challengeCrawlers = function(ua, result) {
608 | if (ua.indexOf('Yahoo') >= 0
609 | || ua.indexOf('help.yahoo.co.jp/help/jp/') >= 0
610 | || ua.indexOf('listing.yahoo.co.jp/support/faq/') >= 0) {
611 | if (ua.indexOf('compatible; Yahoo! Slurp') >= 0) {
612 | updateMap(result, dataset.get('YahooSlurp'));
613 | return true;
614 | }
615 | if (ua.indexOf('YahooFeedSeekerJp') >= 0 || ua.indexOf('YahooFeedSeekerBetaJp') >= 0) {
616 | updateMap(result, dataset.get('YahooJP'));
617 | return true;
618 | }
619 | if (ua.indexOf('crawler (http://listing.yahoo.co.jp/support/faq/') >= 0
620 | || ua.indexOf('crawler (http://help.yahoo.co.jp/help/jp/') >= 0) {
621 | updateMap(result, dataset.get('YahooJP'));
622 | return true;
623 | }
624 | if (ua.indexOf('Y!J-BRZ/YATSHA crawler') >= 0
625 | || ua.indexOf('Y!J-BRY/YATSH crawler') >= 0) {
626 | updateMap(result, dataset.get('YahooJP'));
627 | return true;
628 | }
629 | if (ua.indexOf('Yahoo Pipes') >= 0) {
630 | updateMap(result, dataset.get('YahooPipes'));
631 | return true;
632 | }
633 | }
634 | if (ua.indexOf('msnbot') >= 0) {
635 | updateMap(result, dataset.get('msnbot'));
636 | return true;
637 | }
638 | if (ua.indexOf('bingbot') >= 0) {
639 | if (ua.indexOf('compatible; bingbot') >= 0) {
640 | updateMap(result, dataset.get('bingbot'));
641 | return true;
642 | }
643 | }
644 | if (ua.indexOf('BingPreview') >= 0) {
645 | updateMap(result, dataset.get('BingPreview'));
646 | return true;
647 | }
648 | if (ua.indexOf('Baidu') >= 0) {
649 | if (ua.indexOf('compatible; Baiduspider') >= 0 ||
650 | ua.indexOf('Baiduspider+') >= 0 ||
651 | ua.indexOf('Baiduspider-image+') >= 0) {
652 | updateMap(result, dataset.get('Baiduspider'));
653 | return true;
654 | }
655 | }
656 | if (ua.indexOf('Yeti') >= 0) {
657 | if (ua.indexOf('http://help.naver.com/robots') >= 0 ||
658 | ua.indexOf('http://help.naver.com/support/robots.html') >= 0 ||
659 | ua.indexOf('http://naver.me/bot') >= 0) {
660 | updateMap(result, dataset.get('Yeti'));
661 | return true;
662 | }
663 | }
664 | if (ua.indexOf('FeedBurner/') >= 0) {
665 | updateMap(result, dataset.get('FeedBurner'));
666 | return true;
667 | }
668 | if (ua.indexOf('facebookexternalhit') >= 0) {
669 | updateMap(result, dataset.get('facebook'));
670 | return true;
671 | }
672 | if (ua.indexOf('Twitterbot/') >= 0) {
673 | updateMap(result, dataset.get('twitter'));
674 | return true;
675 | }
676 | if (ua.indexOf('ichiro') >= 0) {
677 | if (ua.indexOf('http://help.goo.ne.jp/door/crawler.html') >= 0 ||
678 | ua.indexOf('compatible; ichiro/mobile goo;') >= 0) {
679 | updateMap(result, dataset.get('goo'));
680 | return true;
681 | }
682 | }
683 | if (ua.indexOf('gooblogsearch/') >= 0) {
684 | updateMap(result, dataset.get('goo'));
685 | return true;
686 | }
687 | if (ua.indexOf('Apple-PubSub') >= 0) {
688 | updateMap(result, dataset.get('ApplePubSub'));
689 | return true;
690 | }
691 | if (ua.indexOf('(www.radian6.com/crawler)') >= 0) {
692 | updateMap(result, dataset.get('radian6'));
693 | return true;
694 | }
695 | if (ua.indexOf('Genieo/') >= 0) {
696 | updateMap(result, dataset.get('Genieo'));
697 | return true;
698 | }
699 | if (ua.indexOf('labs.topsy.com/butterfly/') >= 0) {
700 | updateMap(result, dataset.get('topsyButterfly'));
701 | return true;
702 | }
703 | if (ua.indexOf('rogerbot/1.0 (http://www.seomoz.org/dp/rogerbot') >= 0) {
704 | updateMap(result, dataset.get('rogerbot'));
705 | return true;
706 | }
707 | if (ua.indexOf('compatible; AhrefsBot/') >= 0) {
708 | updateMap(result, dataset.get('AhrefsBot'));
709 | return true;
710 | }
711 | if (ua.indexOf('livedoor FeedFetcher') >= 0 || ua.indexOf('Fastladder FeedFetcher') >= 0) {
712 | updateMap(result, dataset.get('livedoorFeedFetcher'));
713 | return true;
714 | }
715 | if (ua.indexOf('Hatena ') >= 0) {
716 | if (ua.indexOf('Hatena Antenna') >= 0 ||
717 | ua.indexOf('Hatena Pagetitle Agent') >= 0 ||
718 | ua.indexOf('Hatena Diary RSS') >= 0) {
719 | updateMap(result, dataset.get('Hatena'));
720 | return true;
721 | }
722 | }
723 | if (ua.indexOf('mixi-check') >= 0 ||
724 | ua.indexOf('mixi-crawler') >= 0 ||
725 | ua.indexOf('mixi-news-crawler') >= 0) {
726 | updateMap(result, dataset.get('mixi'));
727 | return true;
728 | }
729 | if (ua.indexOf('Indy Library') >= 0) {
730 | if (ua.indexOf('compatible; Indy Library') >= 0) {
731 | updateMap(result, dataset.get('IndyLibrary'));
732 | return true;
733 | }
734 | }
735 | if (ua.indexOf('trendictionbot') >= 0) {
736 | updateMap(result, dataset.get('trendictionbot'));
737 | return true;
738 | }
739 | return false;
740 | };
741 | var challengeMaybeCrawler = exports.challengeMaybeCrawler = function(ua, result) {
742 | if (/(bot|crawler|spider)(?:[-_ .\/;@()]|$)/i.exec(ua)) {
743 | updateMap(result, dataset.get('VariousCrawler'));
744 | return true;
745 | }
746 | if (/(?:Rome Client |UnwindFetchor\/|ia_archiver |Summify |PostRank\/)/.exec(ua) ||
747 | ua.indexOf('ASP-Ranker Feed Crawler') >= 0) {
748 | updateMap(result, dataset.get('VariousCrawler'));
749 | return true;
750 | }
751 | if (/(feed|web) ?parser/i.exec(ua)) {
752 | updateMap(result, dataset.get('VariousCrawler'));
753 | return true;
754 | }
755 | if (/watch ?dog/i.exec(ua)) {
756 | updateMap(result, dataset.get('VariousCrawler'));
757 | return true;
758 | }
759 | return false;
760 | };
761 |
762 | })();
763 | var appliance = {};
764 | (function(){
765 | var exports = appliance;
766 | /* CODE: appliance.js */
767 | var challengePlaystation = exports.challengePlaystation = function(ua, result) {
768 | var data = null;
769 | var os_version = null;
770 | var match;
771 | if (ua.indexOf('PSP (PlayStation Portable);') >= 0) {
772 | data = dataset.get('PSP');
773 | if ((match = /PSP \(PlayStation Portable\); ([.0-9]+)\)/.exec(ua)))
774 | os_version = match[1];
775 | } else if (ua.indexOf('PlayStation Vita') >= 0) {
776 | data = dataset.get('PSVita');
777 | if ((match = /PlayStation Vita ([.0-9]+)\)/.exec(ua)))
778 | os_version = match[1];
779 | } else if (ua.indexOf('PLAYSTATION 3 ') >= 0 || ua.indexOf('PLAYSTATION 3;') >= 0) {
780 | data = dataset.get('PS3');
781 | if ((match = /PLAYSTATION 3;? ([.0-9]+)\)/.exec(ua)))
782 | os_version = match[1];
783 | } else if (ua.indexOf('PlayStation 4 ') >= 0) {
784 | data = dataset.get('PS4');
785 | if ((match = /PlayStation 4 ([.0-9]+)\)/.exec(ua)))
786 | os_version = match[1];
787 | }
788 | if (! data)
789 | return false;
790 | updateMap(result, data);
791 | if (os_version)
792 | updateOsVersion(result, os_version);
793 | return true;
794 | };
795 | var challengeNintendo = exports.challengeNintendo = function(ua, result) {
796 | var data = null;
797 | if (ua.indexOf('Nintendo 3DS;') >= 0) data = dataset.get('Nintendo3DS');
798 | else if (ua.indexOf('Nintendo DSi;') >= 0) data = dataset.get('NintendoDSi');
799 | else if (ua.indexOf('Nintendo Wii;') >= 0) data = dataset.get('NintendoWii');
800 | else if (ua.indexOf('(Nintendo WiiU)') >= 0) data = dataset.get('NintendoWiiU');
801 | if (! data)
802 | return false;
803 | updateMap(result, data);
804 | return true;
805 | };
806 | var challengeDigitalTV = exports.challengeDigitalTV = function(ua, result) {
807 | var data = null;
808 | if (ua.indexOf('InettvBrowser/') >= 0) data = dataset.get('DigitalTV');
809 | if (! data)
810 | return false;
811 | updateMap(result, data);
812 | return true;
813 | };
814 |
815 | })();
816 | var misc = {};
817 | (function(){
818 | var exports = misc;
819 | /* CODE: misc.js */
820 | var challengeDesktopTools = exports.challengeDesktopTools = function(ua, result){
821 | var data = null;
822 | if (ua.indexOf('AppleSyndication/') >= 0) data = dataset.get('SafariRSSReader');
823 | else if (ua.indexOf('compatible; Google Desktop/') >= 0) data = dataset.get('GoogleDesktop');
824 | else if (ua.indexOf('Windows-RSS-Platform') >= 0) data = dataset.get('WindowsRSSReader');
825 | if (!data)
826 | return false;
827 | updateMap(result, data);
828 | return true;
829 | };
830 | var challengeSmartPhonePatterns = exports.challengeSmartPhonePatterns = function(ua, result){
831 | var data;
832 | if (ua.indexOf('CFNetwork/') >= 0) {
833 | data = dataset.get('iOS');
834 | updateCategory(result, data[dataset.KEY_CATEGORY]);
835 | updateOs(result, data[dataset.KEY_NAME]);
836 | return true;
837 | }
838 | return false;
839 | };
840 | var challengeHTTPLibrary = exports.challengeHTTPLibrary = function(ua, result){
841 | var data,version;
842 | if (/^(?:Apache-HttpClient\/|Jakarta Commons-HttpClient\/|Java\/)/.exec(ua) || /[- ]HttpClient(\/|$)/.exec(ua)) {
843 | data = dataset.get('HTTPLibrary'); version = 'Java';
844 | } else if (ua.indexOf('Java(TM) 2 Runtime Environment,') >= 0) {
845 | data = dataset.get('HTTPLibrary'); version = 'Java';
846 | } else if (/^Wget/.exec(ua)) {
847 | data = dataset.get('HTTPLibrary'); version = 'wget';
848 | } else if (/^(?:libwww-perl|WWW-Mechanize|LWP::Simple|LWP |lwp-trivial)/.exec(ua)) {
849 | data = dataset.get('HTTPLibrary'); version = 'perl';
850 | } else if (/^(?:Ruby|feedzirra|Typhoeus)/.exec(ua)) {
851 | data = dataset.get('HTTPLibrary'); version = 'ruby';
852 | } else if (/^(?:Python-urllib\/|Twisted )/.exec(ua)) {
853 | data = dataset.get('HTTPLibrary'); version = 'python';
854 | } else if (/^(:?PHP|WordPress|CakePHP|PukiWiki|PECL::HTTP)(?:\/| |$)/.exec(ua) || /(?:PEAR |)HTTP_Request(?: class|2)/.exec(ua)) {
855 | data = dataset.get('HTTPLibrary'); version = 'php';
856 | } else if (ua.indexOf('PEAR HTTP_Request class;') >= 0) {
857 | data = dataset.get('HTTPLibrary'); version = 'php';
858 | } else if (ua.indexOf('curl/') >= 0) {
859 | data = dataset.get('HTTPLibrary'); version = 'curl';
860 | }
861 | if (! data)
862 | return false;
863 | updateMap(result, data);
864 | updateVersion(result, version);
865 | return true;
866 | };
867 | var challengeMaybeRSSReader = exports.challengeMaybeRSSReader = function(ua, result){
868 | var data = null;
869 | if (/rss(?:reader|bar|[-_ \/;()]|[ +]*\/)/i.exec(ua) || /headline-reader/i.exec(ua)) {
870 | data = dataset.get('VariousRSSReader');
871 | }
872 | else {
873 | if (ua.indexOf('cococ/') >= 0)
874 | data = dataset.get('VariousRSSReader');
875 | }
876 | if (! data)
877 | return false;
878 | updateMap(result, data);
879 | return true;
880 | };
881 |
882 | })();
883 | var woothee = {};
884 | (function(){
885 | var exports = woothee;
886 | /* CODE: woothee.js */
887 | var version = exports.VERSION = package_info.version;
888 | var parse = exports.parse = function(userAgent) {
889 | return fillResult(execParse(userAgent));
890 | };
891 | var isCrawler = exports.isCrawler = function(userAgent) {
892 | return !!userAgent && userAgent !== '-' && tryCrawler(userAgent, {});
893 | };
894 | function execParse(userAgent) {
895 | var result = {};
896 | if (! userAgent || userAgent === '-')
897 | return result;
898 | if (tryCrawler(userAgent, result))
899 | return result;
900 | if (tryBrowser(userAgent, result)) {
901 | if (tryOS(userAgent, result))
902 | return result;
903 | else
904 | return result;
905 | }
906 | if (tryMobilePhone(userAgent, result))
907 | return result;
908 | if (tryAppliance(userAgent, result))
909 | return result;
910 | if (tryMisc(userAgent, result))
911 | return result;
912 | // browser unknown, check os only
913 | if (tryOS(userAgent, result))
914 | return result;
915 | if (tryRareCases(userAgent, result))
916 | return result;
917 | return result;
918 | };
919 | function tryCrawler(userAgent, result) {
920 | if (crawler.challengeGoogle(userAgent, result))
921 | return true;
922 | if (crawler.challengeCrawlers(userAgent, result))
923 | return true;
924 | return false;
925 | }
926 | function tryBrowser(userAgent, result) {
927 | if (browser.challengeMSIE(userAgent, result))
928 | return true;
929 | if (browser.challengeVivaldi(userAgent, result))
930 | return true;
931 | if (browser.challengeYandexBrowser(userAgent, result))
932 | return true;
933 | if (browser.challengeSafariChrome(userAgent, result))
934 | return true;
935 | if (browser.challengeFirefox(userAgent, result))
936 | return true;
937 | if (browser.challengeOpera(userAgent, result))
938 | return true;
939 | if (browser.challengeWebview(userAgent, result))
940 | return true;
941 | return false;
942 | }
943 | function tryOS(userAgent, result) {
944 | if (os.challengeWindows(userAgent, result))
945 | return true;
946 | // OSX PC and iOS devices (strict check)
947 | if (os.challengeOSX(userAgent, result))
948 | return true;
949 | // Linux PC and Android
950 | if (os.challengeLinux(userAgent, result))
951 | return true;
952 | // all useragents matches /(iPhone|iPad|iPod|Android|BlackBerry)/
953 | if (os.challengeSmartPhone(userAgent, result))
954 | return true;
955 | // mobile phones like KDDI-.*
956 | if (os.challengeMobilePhone(userAgent, result))
957 | return true;
958 | // Nintendo DSi/Wii with Opera
959 | if (os.challengeAppliance(userAgent, result))
960 | return true;
961 | if (os.challengeMisc(userAgent, result))
962 | return true;
963 | return false;
964 | }
965 | function tryMobilePhone(userAgent, result) {
966 | if (mobilephone.challengeDocomo(userAgent, result))
967 | return true;
968 | if (mobilephone.challengeAu(userAgent, result))
969 | return true;
970 | if (mobilephone.challengeSoftbank(userAgent, result))
971 | return true;
972 | if (mobilephone.challengeWillcom(userAgent, result))
973 | return true;
974 | if (mobilephone.challengeMisc(userAgent, result))
975 | return true;
976 | return false;
977 | }
978 | function tryAppliance(userAgent, result) {
979 | if (appliance.challengePlaystation(userAgent, result))
980 | return true;
981 | if (appliance.challengeNintendo(userAgent, result))
982 | return true;
983 | if (appliance.challengeDigitalTV(userAgent, result))
984 | return true;
985 | return false;
986 | }
987 | function tryMisc(userAgent, result) {
988 | if (misc.challengeDesktopTools(userAgent, result))
989 | return true;
990 | return false;
991 | }
992 | function tryRareCases(userAgent, result) {
993 | if (misc.challengeSmartPhonePatterns(userAgent, result))
994 | return true;
995 | if (browser.challengeSleipnir(userAgent, result))
996 | return true;
997 | if (misc.challengeHTTPLibrary(userAgent, result))
998 | return true;
999 | if (misc.challengeMaybeRSSReader(userAgent, result))
1000 | return true;
1001 | if (crawler.challengeMaybeCrawler(userAgent, result))
1002 | return true;
1003 | return false;
1004 | }
1005 | var filled = {};
1006 | filled[dataset.ATTRIBUTE_NAME] = dataset.VALUE_UNKNOWN;
1007 | filled[dataset.ATTRIBUTE_CATEGORY] = dataset.VALUE_UNKNOWN;
1008 | filled[dataset.ATTRIBUTE_OS] = dataset.VALUE_UNKNOWN;
1009 | filled[dataset.ATTRIBUTE_OS_VERSION] = dataset.VALUE_UNKNOWN;
1010 | filled[dataset.ATTRIBUTE_VERSION] = dataset.VALUE_UNKNOWN;
1011 | filled[dataset.ATTRIBUTE_VENDOR] = dataset.VALUE_UNKNOWN;
1012 | function fillResult(result) {
1013 | if (! result[dataset.ATTRIBUTE_NAME])
1014 | result[dataset.ATTRIBUTE_NAME] = dataset.VALUE_UNKNOWN;
1015 | if (! result[dataset.ATTRIBUTE_CATEGORY])
1016 | result[dataset.ATTRIBUTE_CATEGORY] = dataset.VALUE_UNKNOWN;
1017 | if (! result[dataset.ATTRIBUTE_OS])
1018 | result[dataset.ATTRIBUTE_OS] = dataset.VALUE_UNKNOWN;
1019 | if (! result[dataset.ATTRIBUTE_OS_VERSION])
1020 | result[dataset.ATTRIBUTE_OS_VERSION] = dataset.VALUE_UNKNOWN;
1021 | if (! result[dataset.ATTRIBUTE_VERSION])
1022 | result[dataset.ATTRIBUTE_VERSION] = dataset.VALUE_UNKNOWN;
1023 | if (! result[dataset.ATTRIBUTE_VENDOR])
1024 | result[dataset.ATTRIBUTE_VENDOR] = dataset.VALUE_UNKNOWN;
1025 | return result;
1026 | };
1027 |
1028 | })();
1029 | var updateMap = util.updateMap,
1030 | updateCategory = util.updateCategory,
1031 | updateOs = util.updateOs,
1032 | updateOsVersion = util.updateOsVersion,
1033 | updateVersion = util.updateVersion;
1034 |
1035 | // AMD / RequireJS
1036 | if (typeof define !== 'undefined' && define.amd) {
1037 | define('woothee', [], function () {
1038 | return woothee;
1039 | });
1040 | }
1041 | // Node.js
1042 | else if (typeof module !== 'undefined' && module.exports) {
1043 | module.exports = woothee;
1044 | }
1045 | // included directly via