useragent_parser 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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!
|