useragent_parser 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/LICENSE +13 -0
- data/README.md +49 -0
- data/Rakefile +5 -0
- data/config/user_agent_parser.yaml +259 -0
- data/lib/useragent_parser/parser.rb +51 -0
- data/lib/useragent_parser/version.rb +3 -0
- data/lib/useragent_parser.rb +79 -0
- data/spec/fixtures/firefox_user_agent_strings.yaml +1386 -0
- data/spec/fixtures/pgts_browser_list.yaml +62356 -0
- data/spec/fixtures/test_user_agent_parser.yaml +831 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/useragent_parser/useragent_parser_spec.rb +52 -0
- data/useragent_parser.gemspec +25 -0
- metadata +105 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2011 Morton Jonuschat
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# UseragentParser
|
2
|
+
|
3
|
+
UseragentParser is a library to detect useful information contained
|
4
|
+
within the Useragent Header transmitted by modern web clients.
|
5
|
+
|
6
|
+
# Usage
|
7
|
+
|
8
|
+
require 'useragent_parser'
|
9
|
+
ua = UseragentParser.parse('Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/8.0.552.237 Safari/525.13')
|
10
|
+
|
11
|
+
This will return a hash containing the following keys:
|
12
|
+
|
13
|
+
**family** - The browser family
|
14
|
+
**v1** - The major browser version
|
15
|
+
**v2** - The minor browser version
|
16
|
+
**v3** - The patch level of the browser
|
17
|
+
**os_family** - The operating system
|
18
|
+
**os_v1** - The major operating system version
|
19
|
+
**os_v2** - The minor operating system version
|
20
|
+
**os_v3** - The patch level of the operating system
|
21
|
+
|
22
|
+
In the case that the **family** or **os_family** is unknown, 'Other' will be
|
23
|
+
returned. The version components will return _nil_ in the case that the
|
24
|
+
respective version level in undetectable.
|
25
|
+
|
26
|
+
# Additional information
|
27
|
+
|
28
|
+
This is a port of the Python [ua-parser](http://code.google.com/p/ua-parser/) library.
|
29
|
+
|
30
|
+
## Maintainers
|
31
|
+
|
32
|
+
* Morton Jonuschat ([github.com/yabawock](https://github.com/yabawock))
|
33
|
+
|
34
|
+
# License
|
35
|
+
|
36
|
+
Copyright 2011 Morton Jonuschat
|
37
|
+
|
38
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
39
|
+
you may not use this file except in compliance with the License.
|
40
|
+
You may obtain a copy of the License at
|
41
|
+
|
42
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
43
|
+
|
44
|
+
Unless required by applicable law or agreed to in writing, software
|
45
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
46
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
47
|
+
See the License for the specific language governing permissions and
|
48
|
+
limitations under the License.
|
49
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,259 @@
|
|
1
|
+
user_agent_parsers:
|
2
|
+
#### SPECIAL CASES TOP ####
|
3
|
+
|
4
|
+
# must go before Opera
|
5
|
+
- regex: '^(Opera)/(\d+)\.(\d+) \(Nintendo Wii'
|
6
|
+
family_replacement: 'Wii'
|
7
|
+
|
8
|
+
# must go before Firefox to catch SeaMonkey.
|
9
|
+
- regex: '(SeaMonkey|Fennec|Camino)/(\d+)\.(\d+)\.?([ab]?\d+[a-z]*)'
|
10
|
+
|
11
|
+
# must go before Firefox
|
12
|
+
|
13
|
+
# must go before Browser/major_version.minor_version - eg: Minefield/3.1a1pre
|
14
|
+
- regex: '(Namoroka|Shiretoko|Minefield)/(\d+)\.(\d+)\.(\d+(?:pre)?)'
|
15
|
+
family_replacement: 'Firefox ($1)'
|
16
|
+
|
17
|
+
- regex: '(Firefox)/(\d+)\.(\d+)([ab]\d+[a-z]*)'
|
18
|
+
family_replacement: 'Firefox Beta'
|
19
|
+
|
20
|
+
- regex: '(Firefox)-(?:\d+\.\d+)?/(\d+)\.(\d+)([ab]\d+[a-z]*)'
|
21
|
+
family_replacement: 'Firefox Beta'
|
22
|
+
|
23
|
+
- regex: '(Namoroka|Shiretoko|Minefield)/(\d+)\.(\d+)([ab]\d+[a-z]*)?'
|
24
|
+
family_replacement: 'Firefox ($1)'
|
25
|
+
|
26
|
+
- regex: '(Firefox).*Tablet browser (\d+)\.(\d+)\.(\d+)'
|
27
|
+
family_replacement: 'MicroB'
|
28
|
+
|
29
|
+
- regex: '(MozillaDeveloperPreview)/(\d+)\.(\d+)([ab]\d+[a-z]*)?'
|
30
|
+
|
31
|
+
# e.g.: Flock/2.0b2
|
32
|
+
- regex: '(Flock)/(\d+)\.(\d+)(b\d+?)'
|
33
|
+
|
34
|
+
# RockMelt
|
35
|
+
- regex: '(RockMelt)/(\d+)\.(\d+)\.(\d+)'
|
36
|
+
|
37
|
+
# e.g.: Fennec/0.9pre
|
38
|
+
- regex: '(Fennec)/(\d+)\.(\d+)(pre)'
|
39
|
+
|
40
|
+
- regex: '(Navigator)/(\d+)\.(\d+)\.(\d+)'
|
41
|
+
family_replacement: 'Netscape'
|
42
|
+
|
43
|
+
- regex: '(Navigator)/(\d+)\.(\d+)([ab]\d+)'
|
44
|
+
family_replacement: 'Netscape'
|
45
|
+
|
46
|
+
- regex: '(Netscape6)/(\d+)\.(\d+)\.(\d+)'
|
47
|
+
family_replacement: 'Netscape'
|
48
|
+
|
49
|
+
- regex: '(MyIBrow)/(\d+)\.(\d+)'
|
50
|
+
family_replacement: 'My Internet Browser'
|
51
|
+
|
52
|
+
# Opera will stop at 9.80 and hide the real version in the Version string.
|
53
|
+
# see: http://dev.opera.com/articles/view/opera-ua-string-changes/
|
54
|
+
- regex: '(Opera Tablet).*Version\/(\d+)\.(\d+)(?:\.(\d+))?'
|
55
|
+
|
56
|
+
- regex: '(Opera)/.+Opera Mobi.+Version/(\d+)\.(\d+)'
|
57
|
+
family_replacement: 'Opera Mobile'
|
58
|
+
|
59
|
+
- regex: '(Opera Mini)/(\d+)\.(\d+)'
|
60
|
+
|
61
|
+
- regex: '(Opera)/9.80.*Version\/(\d+)\.(\d+)(?:\.(\d+))?'
|
62
|
+
|
63
|
+
# Palm WebOS looks a lot like Safari.
|
64
|
+
- regex: '(webOS)/(\d+)\.(\d+)'
|
65
|
+
family_replacement: 'Palm webOS'
|
66
|
+
|
67
|
+
# LuaKit has no version info.
|
68
|
+
# http://luakit.org/projects/luakit/
|
69
|
+
- regex: '(luakit)'
|
70
|
+
family_replacement: 'LuaKit'
|
71
|
+
|
72
|
+
# Lightning (for Thunderbird)
|
73
|
+
# http://www.mozilla.org/projects/calendar/lightning/
|
74
|
+
- regex: '(Lightning)/(\d+)\.(\d+)([ab]?\d+[a-z]*)'
|
75
|
+
|
76
|
+
# Swiftfox
|
77
|
+
- regex: '(Firefox)/(\d+)\.(\d+)\.(\d+(?:pre)?) \(Swiftfox\)'
|
78
|
+
family_replacement: 'Swiftfox'
|
79
|
+
- regex: '(Firefox)/(\d+)\.(\d+)([ab]\d+[a-z]*)? \(Swiftfox\)'
|
80
|
+
family_replacement: 'Swiftfox'
|
81
|
+
|
82
|
+
# Rekonq
|
83
|
+
- regex: 'rekonq'
|
84
|
+
family_replacement: 'Rekonq'
|
85
|
+
|
86
|
+
# Conkeror lowercase/uppercase
|
87
|
+
# http://conkeror.org/
|
88
|
+
- regex: '(conkeror|Conkeror)/(\d+)\.(\d+)\.?(\d+)?'
|
89
|
+
family_replacement: 'Conkeror'
|
90
|
+
|
91
|
+
# catches lower case konqueror
|
92
|
+
- regex: '(konqueror)/(\d+)\.(\d+)\.(\d+)'
|
93
|
+
family_replacement: 'Konqueror'
|
94
|
+
|
95
|
+
- regex: '(PlayBook).+RIM Tablet OS (\d+)\.(\d+)\.(\d+)'
|
96
|
+
|
97
|
+
- regex: '(WeTab)-Browser'
|
98
|
+
|
99
|
+
- regex: '(wOSBrowser).+TouchPad/(\d+)\.(\d+)'
|
100
|
+
family_replacement: 'webOS TouchPad'
|
101
|
+
|
102
|
+
- regex: '(Comodo_Dragon)/(\d+)\.(\d+)\.(\d+)'
|
103
|
+
family_replacement: 'Comodo Dragon'
|
104
|
+
|
105
|
+
# YottaaMonitor bot
|
106
|
+
- regex: '(YottaaMonitor)'
|
107
|
+
|
108
|
+
# must go before NetFront below
|
109
|
+
- regex: '(Kindle)/(\d+)\.(\d+)'
|
110
|
+
|
111
|
+
# Maxthon Googlebot
|
112
|
+
- regex: 'Googlebot\/\d+\.\d+.+(Maxthon)'
|
113
|
+
|
114
|
+
# Mobile Safari on Android
|
115
|
+
- regex: 'Android.*\).*(Version)/(\d+)\.(\d+)(?:\.(\d+))?.*Safari/'
|
116
|
+
family_replacement: 'Mobile Safari'
|
117
|
+
|
118
|
+
# Mobile Safari on iOS
|
119
|
+
- regex: '(Version)/(\d+)\.(\d+)(?:\.(\d+))?.*Mobile/\w+ Safari/\d+'
|
120
|
+
family_replacement: 'Mobile Safari'
|
121
|
+
|
122
|
+
#### END SPECIAL CASES TOP ####
|
123
|
+
|
124
|
+
#### MAIN CASES - this catches > 50% of all browsers ####
|
125
|
+
|
126
|
+
# Browser/major_version.minor_version.beta_version
|
127
|
+
- regex: '(AdobeAIR|Chromium|FireWeb|Jasmine|ANTGalio|Midori|Fresco|Lobo|PaleMoon|Maxthon|Lynx|OmniWeb|Dillo|Camino|Demeter|Fluid|Fennec|Shiira|Sunrise|Chrome|Flock|Netscape|Lunascape|Epiphany|WebPilot|Vodafone|NetFront|Konqueror|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|Opera Mini|iCab|NetNewsWire|ThunderBrowse|Thunderbird|Iron|Iris)/(\d+)\.(\d+)\.(\d+)'
|
128
|
+
|
129
|
+
# Browser/major_version.minor_version
|
130
|
+
- regex: '(Bolt|Jasmine|IEMobile|IceCat|Skyfire|Midori|Maxthon|Lynx|Arora|IBrowse|Dillo|Camino|Shiira|Fennec|Phoenix|Chrome|Flock|Netscape|Lunascape|Epiphany|WebPilot|Opera Mini|Opera|Vodafone|NetFront|Konqueror|Googlebot|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|iCab|NetNewsWire|Iron|Space Bison|Stainless|Orca|Dolfin|BOLT)/(\d+)\.(\d+)'
|
131
|
+
|
132
|
+
# Browser major_version.minor_version.beta_version (space instead of slash)
|
133
|
+
- regex: '(iRider|Crazy Browser|SkipStone|iCab|Lunascape|Sleipnir|Maemo Browser) (\d+)\.(\d+)\.(\d+)'
|
134
|
+
# Browser major_version.minor_version (space instead of slash)
|
135
|
+
- regex: '(iCab|Lunascape|Opera|Android) (\d+)\.(\d+)\.?(\d+)?'
|
136
|
+
|
137
|
+
- regex: '(IEMobile) (\d+)\.(\d+)'
|
138
|
+
family_replacement: 'IE Mobile'
|
139
|
+
|
140
|
+
# AFTER THE EDGE CASES ABOVE!
|
141
|
+
- regex: '(Firefox)/(\d+)\.(\d+)\.(\d+)'
|
142
|
+
|
143
|
+
- regex: '(Firefox)/(\d+)\.(\d+)(pre|[ab]\d+[a-z]*)?'
|
144
|
+
#### END MAIN CASES ####
|
145
|
+
|
146
|
+
#### SPECIAL CASES ####
|
147
|
+
- regex: '(Obigo|OBIGO)[^\d]*(\d+)(?:.(\d+))?'
|
148
|
+
family_replacement: 'Obigo'
|
149
|
+
|
150
|
+
- regex: '(MAXTHON|Maxthon) (\d+)\.(\d+)'
|
151
|
+
family_replacement: 'Maxthon'
|
152
|
+
|
153
|
+
- regex: '(Maxthon|MyIE2|Uzbl|Shiira)'
|
154
|
+
major_version_replacement: '0'
|
155
|
+
|
156
|
+
- regex: '(PLAYSTATION) (\d+)'
|
157
|
+
family_replacement: 'PlayStation'
|
158
|
+
|
159
|
+
- regex: '(PlayStation Portable)[^\d]+(\d+).(\d+)'
|
160
|
+
|
161
|
+
- regex: '(BrowseX) \((\d+)\.(\d+)\.(\d+)'
|
162
|
+
|
163
|
+
- regex: '(POLARIS)/(\d+)\.(\d+)'
|
164
|
+
family_replacement: 'Polaris'
|
165
|
+
|
166
|
+
- regex: '(BonEcho)/(\d+)\.(\d+)\.(\d+)'
|
167
|
+
family_replacement: 'Bon Echo'
|
168
|
+
|
169
|
+
- regex: '(iPod).+Version/(\d+)\.(\d+)\.(\d+)'
|
170
|
+
|
171
|
+
- regex: '(iPhone) OS (\d+)_(\d+)(?:_(\d+))?'
|
172
|
+
|
173
|
+
- regex: '(iPad).+ OS (\d+)_(\d+)(?:_(\d+))?'
|
174
|
+
|
175
|
+
- regex: '(Avant)'
|
176
|
+
major_version_replacement: '1'
|
177
|
+
|
178
|
+
- regex: '(Nokia)[EN]?(\d+)'
|
179
|
+
|
180
|
+
# BlackBerry devices
|
181
|
+
- regex: '(Black[bB]erry).+Version\/(\d+)\.(\d+)\.(\d+)'
|
182
|
+
family_replacement: 'Blackberry'
|
183
|
+
|
184
|
+
- regex: '(Black[bB]erry)\s?(\d+)'
|
185
|
+
family_replacement: 'Blackberry'
|
186
|
+
|
187
|
+
- regex: '(OmniWeb)/v(\d+)\.(\d+)'
|
188
|
+
|
189
|
+
- regex: '(Blazer)/(\d+)\.(\d+)'
|
190
|
+
family_replacement: 'Palm Blazer'
|
191
|
+
|
192
|
+
- regex: '(Pre)/(\d+)\.(\d+)'
|
193
|
+
family_replacement: 'Palm Pre'
|
194
|
+
|
195
|
+
- regex: '(Links) \((\d+)\.(\d+)'
|
196
|
+
|
197
|
+
- regex: '(QtWeb) Internet Browser/(\d+)\.(\d+)'
|
198
|
+
|
199
|
+
#- regex: '\(iPad;.+(Version)/(\d+)\.(\d+)(?:\.(\d+))?.*Safari/'
|
200
|
+
# family_replacement: 'iPad'
|
201
|
+
|
202
|
+
# Safari
|
203
|
+
- regex: '(Version)/(\d+)\.(\d+)(?:\.(\d+))?.*Safari/'
|
204
|
+
family_replacement: 'Safari'
|
205
|
+
# Safari didn't provide "Version/d.d.d" prior to 3.0
|
206
|
+
- regex: '(Safari)/\d+'
|
207
|
+
|
208
|
+
- regex: '(OLPC)/Update(\d+)\.(\d+)'
|
209
|
+
|
210
|
+
- regex: '(OLPC)/Update()\.(\d+)'
|
211
|
+
major_version_replacement: '0'
|
212
|
+
|
213
|
+
- regex: '(SamsungSGHi560)'
|
214
|
+
family_replacement: 'Samsung SGHi560'
|
215
|
+
|
216
|
+
- regex: '^(SonyEricssonK800i)'
|
217
|
+
family_replacement: 'Sony Ericsson K800i'
|
218
|
+
|
219
|
+
- regex: '(Teleca Q7)'
|
220
|
+
|
221
|
+
- regex: '(MSIE) (\d+)\.(\d+)'
|
222
|
+
family_replacement: 'IE'
|
223
|
+
|
224
|
+
|
225
|
+
os_parsers:
|
226
|
+
- regex: '(Windows NT|Windows) (\d+)\.(\d+)'
|
227
|
+
family_replacement: 'Windows'
|
228
|
+
- regex: '(Windows Phone OS) (\d+)\.(\d+)'
|
229
|
+
family_replacement: 'Windows Phone'
|
230
|
+
- regex: '(Windows|Win) (XP|9x|95|98|ME)'
|
231
|
+
family_replacement: 'Windows'
|
232
|
+
- regex: '(Windows CE)'
|
233
|
+
- regex: '(Win)(95|98|9x)'
|
234
|
+
family_replacement: 'Windows'
|
235
|
+
- regex: '(Windows)'
|
236
|
+
|
237
|
+
- regex: '(Android) (\d+)\.(\d+)\.(\d+)'
|
238
|
+
- regex: '(Android) (\d+)\.(\d+)'
|
239
|
+
|
240
|
+
- regex: '(CPU OS|CPU iPhone OS) (\d+)_(\d+)_(\d+) like Mac OS X'
|
241
|
+
family_replacement: 'iOS'
|
242
|
+
- regex: '(CPU OS|CPU iPhone OS) (\d+)_(\d+) like Mac OS X'
|
243
|
+
family_replacement: 'iOS'
|
244
|
+
|
245
|
+
- regex: '(Mac OS X) v?(\d+)\.(\d+)\.?(\d+)?'
|
246
|
+
- regex: '(Mac OS X) v?(\d+)_(\d+)_?(\d+)?'
|
247
|
+
- regex: '(Mac OS X)/(\d+)\.(\d+)\.?(\d+)?'
|
248
|
+
- regex: '(os=Mac) (\d+)\.(\d+)\.(\d+)'
|
249
|
+
family_replacement: 'Mac OS X'
|
250
|
+
- regex: '(Mac OS X|Mac_PowerPC|iMac|MacBook|Macintosh|PowerMac)'
|
251
|
+
family_replacement: 'Mac OS X'
|
252
|
+
|
253
|
+
- regex: '(FreeBSD)'
|
254
|
+
- regex: '(iPhone Simulator)'
|
255
|
+
|
256
|
+
- regex: '(Linux)'
|
257
|
+
|
258
|
+
product_parsers:
|
259
|
+
- regex: '(iPhone)'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module UseragentParser
|
4
|
+
class Parser
|
5
|
+
attr_accessor :pattern, :user_agent_re, :family_replacement, :v1_replacement
|
6
|
+
|
7
|
+
def initialize(pattern, family_replacement = nil, v1_replacement = nil)
|
8
|
+
@pattern = pattern
|
9
|
+
@user_agent_re = Regexp.compile(pattern)
|
10
|
+
@family_replacement = family_replacement
|
11
|
+
@v1_replacement = v1_replacement
|
12
|
+
end
|
13
|
+
|
14
|
+
def match_spans(user_agent_string)
|
15
|
+
match_spans = []
|
16
|
+
match = @user_agent_re.match(user_agent_string)
|
17
|
+
if match
|
18
|
+
# Return the offsets
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse(user_agent_string)
|
23
|
+
family, v1, v2, v3 = nil, nil, nil, nil
|
24
|
+
match = @user_agent_re.match(user_agent_string)
|
25
|
+
if match
|
26
|
+
family = match[1]
|
27
|
+
if @family_replacement
|
28
|
+
if %r'\$1'.match @family_replacement
|
29
|
+
family = @family_replacement.gsub(%r'\$1', match[1])
|
30
|
+
else
|
31
|
+
family = @family_replacement
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if @v1_replacement
|
36
|
+
v1 = @v1_replacement
|
37
|
+
else
|
38
|
+
v1 = match[2]
|
39
|
+
end
|
40
|
+
|
41
|
+
if match.size >= 4
|
42
|
+
v2 = match[3]
|
43
|
+
if match.size >= 5
|
44
|
+
v3 = match[4]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
return family, v1, v2, v3
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "useragent_parser/version"
|
2
|
+
require "useragent_parser/parser"
|
3
|
+
|
4
|
+
module UseragentParser
|
5
|
+
USER_AGENT_PARSERS = []
|
6
|
+
OS_PARSERS = []
|
7
|
+
|
8
|
+
def UseragentParser.load_parsers!
|
9
|
+
YAML.load_file(File.expand_path(File.dirname(__FILE__)) + "/../config/user_agent_parser.yaml")['user_agent_parsers'].each do |parser|
|
10
|
+
regex = parser['regex']
|
11
|
+
family_replacement, v1_replacement = nil, nil
|
12
|
+
if parser.has_key?('family_replacement')
|
13
|
+
family_replacement = parser['family_replacement']
|
14
|
+
end
|
15
|
+
|
16
|
+
if parser.has_key?('v1_replacement')
|
17
|
+
v1_replacement = parser['v1_replacement']
|
18
|
+
end
|
19
|
+
|
20
|
+
USER_AGENT_PARSERS.push UseragentParser::Parser.new(regex, family_replacement, v1_replacement)
|
21
|
+
end
|
22
|
+
|
23
|
+
YAML.load_file(File.expand_path(File.dirname(__FILE__)) + "/../config/user_agent_parser.yaml")['os_parsers'].each do |parser|
|
24
|
+
regex = parser['regex']
|
25
|
+
family_replacement, v1_replacement, code_name = nil, nil, nil
|
26
|
+
if parser.has_key?('family_replacement')
|
27
|
+
family_replacement = parser['family_replacement']
|
28
|
+
end
|
29
|
+
|
30
|
+
if parser.has_key?('v1_replacement')
|
31
|
+
v1_replacement = parser['v1_replacement']
|
32
|
+
end
|
33
|
+
|
34
|
+
OS_PARSERS.push UseragentParser::Parser.new(regex, family_replacement, v1_replacement)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def UseragentParser.parse(user_agent_string, js_user_agent_string = nil, js_user_agent_family = nil, js_user_agent_v1 = nil, js_user_agent_v2 = nil, js_user_agent_v3 = nil)
|
39
|
+
family, v1, v2, v3 = nil, nil, nil, nil
|
40
|
+
os_family, os_v1, os_v2, os_v3 = nil, nil, nil, nil
|
41
|
+
|
42
|
+
# Override via JS properties.
|
43
|
+
if js_user_agent_family.nil?
|
44
|
+
USER_AGENT_PARSERS.each do |parser|
|
45
|
+
family, v1, v2, v3 = parser.parse(user_agent_string)
|
46
|
+
break unless family.nil?
|
47
|
+
end
|
48
|
+
else
|
49
|
+
family = js_user_agent_family
|
50
|
+
v1 = js_user_agent_v1 unless js_user_agent_v1.nil?
|
51
|
+
v2 = js_user_agent_v2 unless js_user_agent_v3.nil?
|
52
|
+
v3 = js_user_agent_v3 unless js_user_agent_v3.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Override for Chrome Frame IFF Chrome is enabled.
|
56
|
+
if js_user_agent_string && js_user_agent_string.include?('Chrome/') && user_agent_string.include?('chromeframe')
|
57
|
+
family = 'Chrome Frame (%{family} %{v1})' % { :family => family, :v1 => v1 }
|
58
|
+
cf_family, v1, v2, v3 = UseragentParser.parse(js_user_agent_string).values
|
59
|
+
end
|
60
|
+
|
61
|
+
OS_PARSERS.each do |parser|
|
62
|
+
os_family, os_v1, os_v2, os_v3 = parser.parse(user_agent_string)
|
63
|
+
break unless os_family.nil?
|
64
|
+
end
|
65
|
+
|
66
|
+
{
|
67
|
+
'family' => family || 'Other',
|
68
|
+
'v1' => v1,
|
69
|
+
'v2' => v2,
|
70
|
+
'v3' => v3,
|
71
|
+
'os_family' => os_family || 'Other',
|
72
|
+
'os_v1' => os_v1,
|
73
|
+
'os_v2' => os_v2,
|
74
|
+
'os_v3' => os_v3
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
UseragentParser.load_parsers!
|