user_agent_parser 1.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of user_agent_parser might be problematic. Click here for more details.

data/Readme.md CHANGED
@@ -4,9 +4,7 @@ UserAgentParser is a simple, comprehensive Ruby gem for parsing user agent strin
4
4
 
5
5
  ## Requirements
6
6
 
7
- * Ruby >= 1.9.2
8
-
9
- Note: Ruby 1.8.7 is not supported due to the requirement for the newer psych YAML parser. If you can get it working on 1.8.7 please send a pull request.
7
+ * Ruby >= 1.8.7
10
8
 
11
9
  ## Installation
12
10
 
@@ -19,49 +17,48 @@ $ gem install user_agent_parser
19
17
  ```ruby
20
18
  require 'user_agent_parser'
21
19
  => true
22
- ua = UserAgentParser.parse 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0;)'
20
+ user_agent = UserAgentParser.parse 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0;)'
23
21
  => #<UserAgentParser::UserAgent IE 9.0 (Windows Vista)>
24
- ua.to_s
22
+ user_agent.to_s
25
23
  => "IE 9.0"
26
- ua.family
24
+ user_agent.name
27
25
  => "IE"
28
- ua.version.to_s
26
+ user_agent.version.to_s
29
27
  => "9.0"
30
- ua.version.major
31
- => 9
32
- ua.version.minor
33
- => 0
34
- os = ua.os
28
+ user_agent.version.major
29
+ => "9"
30
+ user_agent.version.minor
31
+ => "0"
32
+ operating_system = user_agent.os
35
33
  => #<UserAgentParser::OperatingSystem Windows Vista>
36
- os.to_s
34
+ operating_system.to_s
37
35
  => "Windows Vista"
36
+
37
+ # The parser database will be loaded and parsed on every call to
38
+ # UserAgentParser.parse. To avoid this, instantiate your own Parser instance.
39
+ parser = UserAgentParser::Parser.new
40
+ parser.parse 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0;)'
41
+ => #<UserAgentParser::UserAgent IE 9.0 (Windows Vista)>
42
+ parser.parse 'Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.5.24 Version/10.53'
43
+ => #<UserAgentParser::UserAgent Opera 10.53 (Windows XP)>
38
44
  ```
39
45
 
40
46
  ## The pattern database
41
47
 
42
48
  The [ua-parser database](https://github.com/tobie/ua-parser/blob/master/regexes.yaml) is included via a [git submodule](http://help.github.com/submodules/). To update the database the submodule needs to be updated and the gem re-released (pull requests for this are very welcome!).
43
49
 
44
- You can also specify the path to your own, updated and/or customised `regexes.yaml` file:
50
+ You can also specify the path to your own, updated and/or customised `regexes.yaml` file as a second argument to `UserAgentParser.parse`:
45
51
 
46
52
  ```ruby
47
- UserAgentParser.patterns_path = '/some/path/to/regexes.yaml'
53
+ UserAgentParser.parse(ua_string, patterns_path: '/some/path/to/regexes.yaml')
48
54
  ```
49
55
 
50
- ## Comprehensive you say?
51
-
52
- ```bash
53
- $ rake test
54
- ...
55
-
56
- Finished tests in 144.220280s, 89.0027 tests/s, 234.9739 assertions/s.
56
+ or when instantiating a `UserAgentParser::Parser`:
57
57
 
58
- 12836 tests, 33888 assertions, 0 failures, 0 errors, 0 skips
58
+ ```ruby
59
+ UserAgentParser::Parser.new(patterns_path: '/some/path/to/regexes.yaml').parse(ua_string)
59
60
  ```
60
61
 
61
- ## Limitations
62
-
63
- There's no support for providing overrides from Javascript user agent detection like there is with original BrowserScope library. The Javascript overrides were only necessary for detecting IE 9 Platform Preview and older versions of [Chrome Frame](https://developers.google.com/chrome/chrome-frame/).
64
-
65
62
  ## Contributing
66
63
 
67
64
  1. Fork
@@ -73,4 +70,4 @@ All accepted pull requests will earn you commit and release rights.
73
70
 
74
71
  ## License
75
72
 
76
- MIT
73
+ MIT
@@ -2,24 +2,13 @@ require 'user_agent_parser/parser'
2
2
  require 'user_agent_parser/user_agent'
3
3
  require 'user_agent_parser/version'
4
4
  require 'user_agent_parser/operating_system'
5
+ require 'user_agent_parser/device'
5
6
 
6
7
  module UserAgentParser
7
-
8
- # Path to the ua-parser regexes pattern database
9
- def self.patterns_path
10
- @patterns_path
11
- end
12
-
13
- # Sets the path to the ua-parser regexes pattern database
14
- def self.patterns_path=(path)
15
- @patterns_path = path
16
- end
17
-
18
- self.patterns_path = File.join(File.dirname(__FILE__), "../vendor/ua-parser/regexes.yaml")
8
+ DefaultPatternsPath = File.join(File.dirname(__FILE__), "../vendor/ua-parser/regexes.yaml")
19
9
 
20
10
  # Parse the given +user_agent_string+, returning a +UserAgent+
21
- def self.parse user_agent_string
22
- Parser.new.parse user_agent_string
11
+ def self.parse(user_agent_string, options={})
12
+ Parser.new(options).parse(user_agent_string)
23
13
  end
24
-
25
14
  end
@@ -0,0 +1,23 @@
1
+ module UserAgentParser
2
+ class Device
3
+ attr_reader :name
4
+
5
+ def initialize(name = nil)
6
+ @name = name || 'Other'
7
+ end
8
+
9
+ def to_s
10
+ name
11
+ end
12
+
13
+ def inspect
14
+ "#<#{self.class} #{to_s}>"
15
+ end
16
+
17
+ def eql?(other)
18
+ self.class.eql?(other.class) && name == other.name
19
+ end
20
+
21
+ alias_method :==, :eql?
22
+ end
23
+ end
@@ -1,28 +1,30 @@
1
1
  module UserAgentParser
2
-
3
2
  class OperatingSystem
4
-
5
- attr_accessor :name, :version
6
-
7
- def initialize(name="Other", version=nil)
8
- self.name = name
9
- self.version = version
3
+ attr_reader :name, :version
4
+
5
+ def initialize(name = 'Other', version = nil)
6
+ @name = name
7
+ @version = version
10
8
  end
11
-
9
+
12
10
  def to_s
13
- s = name
14
- s += " #{version}" if version
15
- s
11
+ string = name
12
+ unless version.nil?
13
+ string += " #{version}"
14
+ end
15
+ string
16
16
  end
17
-
17
+
18
18
  def inspect
19
19
  "#<#{self.class} #{to_s}>"
20
20
  end
21
-
22
- def ==(other)
23
- name == other.name && version == other.version
21
+
22
+ def eql?(other)
23
+ self.class.eql?(other.class) &&
24
+ name == other.name &&
25
+ version == other.version
24
26
  end
25
-
26
- end
27
27
 
28
- end
28
+ alias_method :==, :eql?
29
+ end
30
+ end
@@ -1,41 +1,49 @@
1
1
  require 'yaml'
2
2
 
3
3
  module UserAgentParser
4
-
4
+
5
5
  class Parser
6
+ attr_reader :patterns_path
6
7
 
7
- def parse user_agent
8
- ua = parse_ua(user_agent)
9
- ua.os = parse_os(user_agent)
10
- ua
8
+ def initialize(options={})
9
+ @patterns_path = options[:patterns_path] || UserAgentParser::DefaultPatternsPath
10
+ @ua_patterns, @os_patterns, @device_patterns = load_patterns(patterns_path)
11
11
  end
12
-
13
- private
14
12
 
15
- def all_patterns
16
- @all_patterns ||= YAML.load_file(UserAgentParser.patterns_path)
13
+ def parse(user_agent)
14
+ os = parse_os(user_agent)
15
+ device = parse_device(user_agent)
16
+ parse_ua(user_agent, os, device)
17
17
  end
18
18
 
19
- def patterns type
20
- @patterns ||= {}
21
- @patterns[type] ||= begin
22
- all_patterns[type].each do |p|
23
- p["regex"] = Regexp.new(p["regex"])
19
+ private
20
+
21
+ def load_patterns(path)
22
+ yml = YAML.load_file(path)
23
+
24
+ # Parse all the regexs
25
+ yml.each_pair do |type, patterns|
26
+ patterns.each do |pattern|
27
+ pattern["regex"] = Regexp.new(pattern["regex"])
24
28
  end
25
29
  end
30
+
31
+ [ yml["user_agent_parsers"], yml["os_parsers"], yml["device_parsers"] ]
26
32
  end
27
-
28
- def parse_ua user_agent
29
- pattern, match = first_pattern_match(patterns("user_agent_parsers"), user_agent)
33
+
34
+ def parse_ua(user_agent, os = nil, device = nil)
35
+ pattern, match = first_pattern_match(@ua_patterns, user_agent)
36
+
30
37
  if match
31
- user_agent_from_pattern_match(pattern, match)
38
+ user_agent_from_pattern_match(pattern, match, os, device)
32
39
  else
33
- UserAgent.new
40
+ UserAgent.new(nil, nil, os, device)
34
41
  end
35
42
  end
36
-
37
- def parse_os user_agent
38
- pattern, match = first_pattern_match(patterns("os_parsers"), user_agent)
43
+
44
+ def parse_os(user_agent)
45
+ pattern, match = first_pattern_match(@os_patterns, user_agent)
46
+
39
47
  if match
40
48
  os_from_pattern_match(pattern, match)
41
49
  else
@@ -43,45 +51,90 @@ module UserAgentParser
43
51
  end
44
52
  end
45
53
 
46
- def first_pattern_match patterns, value
47
- for p in patterns
48
- if m = p["regex"].match(value)
49
- return [p, m]
54
+ def parse_device(user_agent)
55
+ pattern, match = first_pattern_match(@device_patterns, user_agent)
56
+
57
+ if match
58
+ device_from_pattern_match(pattern, match)
59
+ else
60
+ Device.new
61
+ end
62
+ end
63
+
64
+ def first_pattern_match(patterns, value)
65
+ patterns.each do |pattern|
66
+ if match = pattern["regex"].match(value)
67
+ return [pattern, match]
50
68
  end
51
69
  end
52
70
  nil
53
71
  end
54
72
 
55
- def user_agent_from_pattern_match pattern, match
56
- family, v1, v2, v3 = match[1], match[2], match[3], match[4]
73
+ def user_agent_from_pattern_match(pattern, match, os = nil, device = nil)
74
+ name, v1, v2, v3 = match[1], match[2], match[3], match[4]
75
+
57
76
  if pattern["family_replacement"]
58
- family = pattern["family_replacement"].sub('$1', family || '')
77
+ name = pattern["family_replacement"].sub('$1', name || '')
78
+ end
79
+
80
+ if pattern["v1_replacement"]
81
+ v1 = pattern["v1_replacement"].sub('$1', v1 || '')
82
+ end
83
+
84
+ if pattern["v2_replacement"]
85
+ v2 = pattern["v2_replacement"].sub('$1', v2 || '')
86
+ end
87
+
88
+ if pattern["v3_replacement"]
89
+ v3 = pattern["v3_replacement"].sub('$1', v3 || '')
59
90
  end
60
- v1 = pattern["v1_replacement"].sub('$1', v1 || '') if pattern["v1_replacement"]
61
- v2 = pattern["v2_replacement"].sub('$1', v2 || '') if pattern["v2_replacement"]
62
- v3 = pattern["v3_replacement"].sub('$1', v3 || '') if pattern["v3_replacement"]
63
- ua = UserAgent.new(family)
64
- ua.version = version_from_segments(v1, v2, v3)
65
- ua
91
+
92
+ version = version_from_segments(v1, v2, v3)
93
+
94
+ UserAgent.new(name, version, os, device)
66
95
  end
67
-
68
- def os_from_pattern_match pattern, match
96
+
97
+ def os_from_pattern_match(pattern, match)
69
98
  os, v1, v2, v3, v4 = match[1], match[2], match[3], match[4], match[5]
70
- os = pattern["os_replacement"].sub('$1', os || '') if pattern["os_replacement"]
71
- v1 = pattern["v1_replacement"].sub('$1', v1 || '') if pattern["v1_replacement"]
72
- v2 = pattern["v2_replacement"].sub('$1', v2 || '') if pattern["v2_replacement"]
73
- v3 = pattern["v3_replacement"].sub('$1', v3 || '') if pattern["v3_replacement"]
74
- v4 = pattern["v3_replacement"].sub('$1', v3 || '') if pattern["v4_replacement"]
75
- os = OperatingSystem.new(os)
76
- os.version = version_from_segments(v1, v2, v3, v4)
77
- os
99
+
100
+ if pattern["os_replacement"]
101
+ os = pattern["os_replacement"].sub('$1', os || '')
102
+ end
103
+
104
+ if pattern["os_v1_replacement"]
105
+ v1 = pattern["os_v1_replacement"].sub('$1', v1 || '')
106
+ end
107
+
108
+ if pattern["os_v2_replacement"]
109
+ v2 = pattern["os_v2_replacement"].sub('$1', v2 || '')
110
+ end
111
+
112
+ if pattern["os_v3_replacement"]
113
+ v3 = pattern["os_v3_replacement"].sub('$1', v3 || '')
114
+ end
115
+
116
+ if pattern["os_v4_replacement"]
117
+ v4 = pattern["os_v4_replacement"].sub('$1', v4 || '')
118
+ end
119
+
120
+ version = version_from_segments(v1, v2, v3, v4)
121
+
122
+ OperatingSystem.new(os, version)
78
123
  end
79
-
124
+
125
+ def device_from_pattern_match(pattern, match)
126
+ device = match[1]
127
+
128
+ if pattern["device_replacement"]
129
+ device = pattern["device_replacement"].sub('$1', device || '')
130
+ end
131
+
132
+ Device.new(device)
133
+ end
134
+
80
135
  def version_from_segments(*segments)
81
136
  version_string = segments.compact.join(".")
82
137
  version_string.empty? ? nil : Version.new(version_string)
83
138
  end
84
-
85
139
  end
86
-
87
140
  end
@@ -1,33 +1,37 @@
1
1
  module UserAgentParser
2
-
3
2
  class UserAgent
3
+ attr_reader :name, :version, :os, :device
4
4
 
5
- attr_accessor :family, :version, :os
5
+ # For backwards compatibility with older versions of this gem.
6
+ alias_method :family, :name
6
7
 
7
- def initialize(family="Other", version=nil, os=nil)
8
- self.family = family
9
- self.version = version
10
- self.os = os
8
+ def initialize(name = nil, version = nil, os = nil, device = nil)
9
+ @name = name || 'Other'
10
+ @version = version
11
+ @os = os
12
+ @device = device
11
13
  end
12
14
 
13
15
  def to_s
14
- s = family
15
- s += " #{version}" if version
16
- s
16
+ string = name
17
+ string += " #{version}" if version
18
+ string
17
19
  end
18
20
 
19
21
  def inspect
20
- s = to_s
21
- s += " (#{os})" if os
22
- "#<#{self.class} #{s}>"
22
+ string = to_s
23
+ string += " (#{os})" if os
24
+ string += " (#{device})" if device
25
+ "#<#{self.class} #{string}>"
23
26
  end
24
-
25
- def ==(other)
26
- family == other.family &&
27
+
28
+ def eql?(other)
29
+ self.class.eql?(other.class) &&
30
+ name == other.name &&
27
31
  version == other.version &&
28
32
  os == other.os
29
33
  end
30
34
 
35
+ alias_method :==, :eql?
31
36
  end
32
-
33
37
  end
@@ -1,37 +1,47 @@
1
1
  module UserAgentParser
2
-
3
2
  class Version
4
3
 
5
- attr_accessor :version
4
+ # Private: Regex used to split version string into major, minor, patch,
5
+ # and patch_minor.
6
+ SEGMENTS_REGEX = /\d+\-\d+|\d+[a-zA-Z]+$|\d+|[A-Za-z][0-9A-Za-z-]*$/
7
+
8
+ attr_reader :version
6
9
  alias :to_s :version
7
10
 
8
11
  def initialize(version)
9
- self.version = version.to_s.strip
12
+ @version = version.to_s.strip
10
13
  end
11
14
 
12
- def segments
13
- version.scan(/\d+\-\d+|\d+[a-zA-Z]+$|\d+|[A-Za-z][0-9A-Za-z-]*$/).map do |s|
14
- /^\d+$/ =~ s ? s.to_i : s
15
- end
15
+ def major
16
+ segments[0]
17
+ end
18
+
19
+ def minor
20
+ segments[1]
21
+ end
22
+
23
+ def patch
24
+ segments[2]
16
25
  end
17
26
 
18
- def [](segment)
19
- segments[segment]
27
+ def patch_minor
28
+ segments[3]
20
29
  end
21
-
22
- def major; self[0] end
23
- def minor; self[1] end
24
- def patch; self[2] end
25
- def patch_minor; self[3] end
26
-
30
+
27
31
  def inspect
28
32
  "#<#{self.class} #{to_s}>"
29
33
  end
30
-
31
- def ==(other)
32
- version == other.version
34
+
35
+ def eql?(other)
36
+ self.class.eql?(other.class) &&
37
+ version == other.version
33
38
  end
34
39
 
35
- end
40
+ alias_method :==, :eql?
36
41
 
37
- end
42
+ # Private
43
+ def segments
44
+ @segments ||= version.scan(SEGMENTS_REGEX)
45
+ end
46
+ end
47
+ end
@@ -1,10 +1,6 @@
1
1
  user_agent_parsers:
2
2
  #### SPECIAL CASES TOP ####
3
3
 
4
- # must go before Opera
5
- - regex: '^(Opera)/(\d+)\.(\d+) \(Nintendo Wii'
6
- family_replacement: 'Wii'
7
-
8
4
  # must go before Firefox to catch SeaMonkey/Camino
9
5
  - regex: '(SeaMonkey|Camino)/(\d+)\.(\d+)\.?([ab]?\d+[a-z]*)'
10
6
 
@@ -66,16 +62,16 @@ user_agent_parsers:
66
62
  - regex: '(Opera)/9.80.*Version/(\d+)\.(\d+)(?:\.(\d+))?'
67
63
 
68
64
  # Palm WebOS looks a lot like Safari.
69
- - regex: '(webOSBrowser)/(\d+)\.(\d+)'
70
- - regex: '(webOS)/(\d+)\.(\d+)'
71
- family_replacement: 'webOSBrowser'
72
- - regex: '(wOSBrowser).+TouchPad/(\d+)\.(\d+)'
73
- family_replacement: 'webOS TouchPad'
65
+ - regex: '(hpw|web)OS/(\d+)\.(\d+)(?:\.(\d+))?'
66
+ family_replacement: 'webOS Browser'
74
67
 
75
68
  # LuaKit has no version info.
76
69
  # http://luakit.org/projects/luakit/
77
70
  - regex: '(luakit)'
78
71
  family_replacement: 'LuaKit'
72
+
73
+ # Snowshoe
74
+ - regex: '(Snowshoe)/(\d+)\.(\d+).(\d+)'
79
75
 
80
76
  # Lightning (for Thunderbird)
81
77
  # http://www.mozilla.org/projects/calendar/lightning/
@@ -110,9 +106,6 @@ user_agent_parsers:
110
106
  # Bots
111
107
  - regex: '(YottaaMonitor|BrowserMob|HttpMonitor|YandexBot|Slurp|BingPreview|PagePeeker|ThumbShotsBot|WebThumb|URL2PNG|ZooShot|GomezA|Catchpoint bot|Willow Internet Crawler|Google SketchUp|Read%20Later)'
112
108
 
113
- # must go before NetFront below
114
- - regex: '(Kindle)/(\d+)\.(\d+)'
115
-
116
109
  - regex: '(Symphony) (\d+).(\d+)'
117
110
 
118
111
  - regex: '(Minimo)'
@@ -130,7 +123,11 @@ user_agent_parsers:
130
123
  family_replacement: 'Chrome Frame'
131
124
 
132
125
  # UC Browser
133
- - regex: '(UC Browser)(\d+)\.(\d+)\.(\d+)'
126
+ - regex: '(UCBrowser)[ /](\d+)\.(\d+)\.(\d+)'
127
+ family_replacement: 'UC Browser'
128
+ - regex: '(UC Browser)[ /](\d+)\.(\d+)\.(\d+)'
129
+ - regex: '(UC Browser|UCBrowser|UCWEB)(\d+)\.(\d+)\.(\d+)'
130
+ family_replacement: 'UC Browser'
134
131
 
135
132
  # Tizen Browser (second case included in browser/major.minor regex)
136
133
  - regex: '(SLP Browser)/(\d+)\.(\d+)'
@@ -142,8 +139,10 @@ user_agent_parsers:
142
139
  # Sogou Explorer 2.X
143
140
  - regex: '(SE 2\.X) MetaSr (\d+)\.(\d+)'
144
141
  family_replacement: 'Sogou Explorer'
145
-
146
- # Baidu Browser
142
+
143
+ # Baidu Browsers (desktop spoofs chrome & IE, explorer is mobile)
144
+ - regex: '(baidubrowser)[/\s](\d+)'
145
+ family_replacement: 'Baidu Browser'
147
146
  - regex: '(FlyFlow)/(\d+)\.(\d+)'
148
147
  family_replacement: 'Baidu Explorer'
149
148
 
@@ -159,24 +158,39 @@ user_agent_parsers:
159
158
  - regex: '(Twitterbot)/(\d+)\.(\d+)'
160
159
  family_replacement: 'TwitterBot'
161
160
 
161
+ # Rackspace Monitoring
162
+ - regex: '(Rackspace Monitoring)/(\d+)\.(\d+)'
163
+ family_replacement: 'RackspaceBot'
164
+
162
165
  # PyAMF
163
166
  - regex: '(PyAMF)/(\d+)\.(\d+)\.(\d+)'
164
167
 
168
+ # Yandex Browser
169
+ - regex: '(YaBrowser)/(\d+)\.(\d+)\.(\d+)'
170
+ family_replacement: 'Yandex Browser'
171
+
172
+ # Mail.ru Amigo/Internet Browser (Chromium-based)
173
+ - regex: '(Chrome)/(\d+)\.(\d+)\.(\d+).* MRCHROME'
174
+ family_replacement: 'Mail.ru Chromium Browser'
175
+
165
176
  #### END SPECIAL CASES TOP ####
166
177
 
167
178
  #### MAIN CASES - this catches > 50% of all browsers ####
168
179
 
169
180
  # Browser/major_version.minor_version.beta_version
170
- - regex: '(AdobeAIR|Chromium|FireWeb|Jasmine|ANTGalio|Midori|Fresco|Lobo|PaleMoon|Maxthon|Lynx|OmniWeb|Dillo|Camino|Demeter|Fluid|Fennec|Shiira|Sunrise|Chrome|Flock|Netscape|Lunascape|WebPilot|Vodafone|NetFront|Netfront|Konqueror|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|Opera Mini|iCab|NetNewsWire|ThunderBrowse|Iris|UP\.Browser|Bunjaloo|Google Earth|Raven for Mac)/(\d+)\.(\d+)\.(\d+)'
181
+ - regex: '(AdobeAIR|Chromium|FireWeb|Jasmine|ANTGalio|Midori|Fresco|Lobo|PaleMoon|Maxthon|Lynx|OmniWeb|Dillo|Camino|Demeter|Fluid|Fennec|Shiira|Sunrise|Chrome|Flock|Netscape|Lunascape|WebPilot|Vodafone|NetFront|Netfront|Konqueror|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|Opera Mini|iCab|NetNewsWire|ThunderBrowse|Iris|UP\.Browser|Bunjalloo|Google Earth|Raven for Mac|Openwave)/(\d+)\.(\d+)\.(\d+)'
171
182
 
172
183
  # Browser/major_version.minor_version
173
- - regex: '(Bolt|Jasmine|IceCat|Skyfire|Midori|Maxthon|Lynx|Arora|IBrowse|Dillo|Camino|Shiira|Fennec|Phoenix|Chrome|Flock|Netscape|Lunascape|Epiphany|WebPilot|Opera Mini|Opera|Vodafone|NetFront|Netfront|Konqueror|Googlebot|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|iCab|NetNewsWire|Space Bison|Stainless|Orca|Dolfin|BOLT|Minimo|Tizen Browser|Polaris|Abrowser)/(\d+)\.(\d+)'
184
+ - regex: '(Bolt|Jasmine|IceCat|Skyfire|Midori|Maxthon|Lynx|Arora|IBrowse|Dillo|Camino|Shiira|Fennec|Phoenix|Chrome|Flock|Netscape|Lunascape|Epiphany|WebPilot|Opera Mini|Opera|Vodafone|NetFront|Netfront|Konqueror|Googlebot|SeaMonkey|Kazehakase|Vienna|Iceape|Iceweasel|IceWeasel|Iron|K-Meleon|Sleipnir|Galeon|GranParadiso|iCab|NetNewsWire|Space Bison|Stainless|Orca|Dolfin|BOLT|Minimo|Tizen Browser|Polaris|Abrowser|Planetweb|ICE Browser)/(\d+)\.(\d+)'
174
185
 
175
186
  # Browser major_version.minor_version.beta_version (space instead of slash)
176
187
  - regex: '(iRider|Crazy Browser|SkipStone|iCab|Lunascape|Sleipnir|Maemo Browser) (\d+)\.(\d+)\.(\d+)'
177
188
  # Browser major_version.minor_version (space instead of slash)
178
- - regex: '(iCab|Lunascape|Opera|Android|Jasmine|Polaris|BREW) (\d+)\.(\d+)\.?(\d+)?'
179
-
189
+ - regex: '(iCab|Lunascape|Opera|Android|Jasmine|Polaris) (\d+)\.(\d+)\.?(\d+)?'
190
+
191
+ # Kindle WebKit
192
+ - regex: '(Kindle)/(\d+)\.(\d+)'
193
+
180
194
  # weird android UAs
181
195
  - regex: '(Android) Donut'
182
196
  v1_replacement: '1'
@@ -215,18 +229,32 @@ user_agent_parsers:
215
229
  - regex: '(Obigo)InternetBrowser'
216
230
  - regex: '(Obigo)\-Browser'
217
231
  - regex: '(Obigo|OBIGO)[^\d]*(\d+)(?:.(\d+))?'
232
+ family_replacement: 'Obigo'
218
233
 
219
234
  - regex: '(MAXTHON|Maxthon) (\d+)\.(\d+)'
220
235
  family_replacement: 'Maxthon'
221
236
  - regex: '(Maxthon|MyIE2|Uzbl|Shiira)'
222
237
  v1_replacement: '0'
223
238
 
224
- - regex: '(PLAYSTATION) (\d+)'
225
- family_replacement: 'PlayStation'
226
- - regex: '(PlayStation Portable)[^\d]+(\d+).(\d+)'
239
+ - regex: 'PLAYSTATION 3.+WebKit'
240
+ family_replacement: 'NetFront NX'
241
+ - regex: 'PLAYSTATION 3'
242
+ family_replacement: 'NetFront'
243
+ - regex: '(PlayStation Portable)'
244
+ family_replacement: 'NetFront'
245
+ - regex: '(PlayStation Vita)'
246
+ family_replacement: 'NetFront NX'
247
+
248
+ - regex: 'AppleWebKit.+ (NX)/(\d+)\.(\d+)\.(\d+)'
249
+ family_replacement: 'NetFront NX'
250
+ - regex: '(Nintendo 3DS)'
251
+ family_replacement: 'NetFront NX'
227
252
 
228
253
  - regex: '(BrowseX) \((\d+)\.(\d+)\.(\d+)'
229
254
 
255
+ - regex: '(NCSA_Mosaic)/(\d+)\.(\d+)'
256
+ family_replacement: 'NCSA Mosaic'
257
+
230
258
  # Polaris/d.d is above
231
259
  - regex: '(POLARIS)/(\d+)\.(\d+)'
232
260
  family_replacement: 'Polaris'
@@ -236,6 +264,9 @@ user_agent_parsers:
236
264
  - regex: '(BonEcho)/(\d+)\.(\d+)\.(\d+)'
237
265
  family_replacement: 'Bon Echo'
238
266
 
267
+ - regex: 'M?QQBrowser'
268
+ family_replacement: 'QQ Browser'
269
+
239
270
  - regex: '(iPod).+Version/(\d+)\.(\d+)\.(\d+)'
240
271
  family_replacement: 'Mobile Safari'
241
272
  - regex: '(iPod).*Version/(\d+)\.(\d+)'
@@ -254,36 +285,50 @@ user_agent_parsers:
254
285
  family_replacement: 'Mobile Safari'
255
286
 
256
287
  - regex: '(AvantGo) (\d+).(\d+)'
288
+
289
+ - regex: '(OneBrowser)/(\d+).(\d+)'
290
+ family_replacement: 'ONE Browser'
257
291
 
258
292
  - regex: '(Avant)'
259
293
  v1_replacement: '1'
260
294
 
295
+ # This is the Tesla Model S (see similar entry in device parsers)
296
+ - regex: '(QtCarBrowser)'
297
+ v1_replacement: '1'
298
+
299
+ - regex: '(iBrowser/Mini)(\d+).(\d+)'
300
+ family_replacement: 'iBrowser Mini'
261
301
  # nokia browsers
262
302
  # based on: http://www.developer.nokia.com/Community/Wiki/User-Agent_headers_for_Nokia_devices
263
303
  - regex: '^(Nokia)'
264
304
  family_replacement: 'Nokia Services (WAP) Browser'
265
305
  - regex: '(NokiaBrowser)/(\d+)\.(\d+).(\d+)\.(\d+)'
306
+ family_replacement: 'Nokia Browser'
266
307
  - regex: '(NokiaBrowser)/(\d+)\.(\d+).(\d+)'
308
+ family_replacement: 'Nokia Browser'
267
309
  - regex: '(NokiaBrowser)/(\d+)\.(\d+)'
310
+ family_replacement: 'Nokia Browser'
268
311
  - regex: '(BrowserNG)/(\d+)\.(\d+).(\d+)'
269
- family_replacement: 'NokiaBrowser'
312
+ family_replacement: 'Nokia Browser'
270
313
  - regex: '(Series60)/5\.0'
271
- family_replacement: 'NokiaBrowser'
314
+ family_replacement: 'Nokia Browser'
272
315
  v1_replacement: '7'
273
316
  v2_replacement: '0'
274
317
  - regex: '(Series60)/(\d+)\.(\d+)'
275
318
  family_replacement: 'Nokia OSS Browser'
276
319
  - regex: '(S40OviBrowser)/(\d+)\.(\d+)\.(\d+)\.(\d+)'
277
- family_replacement: 'Nokia Series 40 Ovi Browser'
320
+ family_replacement: 'Ovi Browser'
278
321
  - regex: '(Nokia)[EN]?(\d+)'
279
322
 
280
323
  # BlackBerry devices
324
+ - regex: '(BB10);'
325
+ family_replacement: 'BlackBerry WebKit'
281
326
  - regex: '(PlayBook).+RIM Tablet OS (\d+)\.(\d+)\.(\d+)'
282
- family_replacement: 'Blackberry WebKit'
327
+ family_replacement: 'BlackBerry WebKit'
283
328
  - regex: '(Black[bB]erry).+Version/(\d+)\.(\d+)\.(\d+)'
284
- family_replacement: 'Blackberry WebKit'
329
+ family_replacement: 'BlackBerry WebKit'
285
330
  - regex: '(Black[bB]erry)\s?(\d+)'
286
- family_replacement: 'Blackberry'
331
+ family_replacement: 'BlackBerry'
287
332
 
288
333
  - regex: '(OmniWeb)/v(\d+)\.(\d+)'
289
334
 
@@ -302,6 +347,7 @@ user_agent_parsers:
302
347
 
303
348
  # Amazon Silk, should go before Safari
304
349
  - regex: '(Silk)/(\d+)\.(\d+)(?:\.([0-9\-]+))?'
350
+ family_replacement: 'Amazon Silk'
305
351
 
306
352
  # WebKit Nightly
307
353
  - regex: '(AppleWebKit)/(\d+)\.?(\d+)?\+ .* Safari'
@@ -322,11 +368,15 @@ user_agent_parsers:
322
368
 
323
369
  - regex: '(Teleca)'
324
370
  family_replacement: 'Teleca Browser'
371
+
372
+ - regex: '(Phantom)/V(\d+)\.(\d+)'
373
+ family_replacement: 'Phantom Browser'
325
374
 
326
375
  - regex: '(MSIE) (\d+)\.(\d+)'
327
376
  family_replacement: 'IE'
328
377
 
329
- - regex: '(Nintendo 3DS).* Version/(\d+)\.(\d+)(?:\.(\w+))'
378
+ - regex: '(python-requests)/(\d+)\.(\d+)'
379
+ family_replacement: 'Python Requests'
330
380
 
331
381
  os_parsers:
332
382
 
@@ -356,6 +406,12 @@ os_parsers:
356
406
  - regex: '(Android) Honeycomb'
357
407
  os_v1_replacement: '3'
358
408
 
409
+ ##########
410
+ # Kindle Android
411
+ ##########
412
+ - regex: '(Silk-Accelerated=[a-z]{4,5})'
413
+ os_replacement: 'Android'
414
+
359
415
  ##########
360
416
  # Windows
361
417
  # http://en.wikipedia.org/wiki/Windows_NT#Releases
@@ -364,15 +420,14 @@ os_parsers:
364
420
  # lots of ua strings have Windows NT 4.1 !?!?!?!? !?!? !? !????!?! !!! ??? !?!?! ?
365
421
  # (very) roughly ordered in terms of frequency of occurence of regex (win xp currently most frequent, etc)
366
422
  ##########
367
- - regex: '(Windows Phone 6\.5)'
368
-
423
+
369
424
  - regex: '(Windows (?:NT 5\.2|NT 5\.1))'
370
425
  os_replacement: 'Windows XP'
371
426
 
372
427
  # ie mobile des ktop mode
373
428
  # spoofs nt 6.1. must come before windows 7
374
429
  - regex: '(XBLWP7)'
375
- os_replacement: 'Windows Phone OS'
430
+ os_replacement: 'Windows Phone'
376
431
 
377
432
  - regex: '(Windows NT 6\.1)'
378
433
  os_replacement: 'Windows 7'
@@ -380,7 +435,10 @@ os_parsers:
380
435
  - regex: '(Windows NT 6\.0)'
381
436
  os_replacement: 'Windows Vista'
382
437
 
383
- - regex: '(Windows 98|Windows XP|Windows ME|Windows 95|Windows CE|Windows 7|Windows NT 4\.0|Windows Vista|Windows 2000)'
438
+ - regex: '(Windows 98|Windows XP|Windows ME|Windows 95|Windows CE|Windows 7|Windows NT 4\.0|Windows Vista|Windows 2000|Windows 3.1)'
439
+
440
+ - regex: '(Windows NT 6\.2; ARM;)'
441
+ os_replacement: 'Windows RT'
384
442
 
385
443
  # is this a spoof or is nt 6.2 out and about in some capacity?
386
444
  - regex: '(Windows NT 6\.2)'
@@ -389,8 +447,8 @@ os_parsers:
389
447
  - regex: '(Windows NT 5\.0)'
390
448
  os_replacement: 'Windows 2000'
391
449
 
392
- - regex: '(Windows Phone OS) (\d+)\.(\d+)'
393
-
450
+ - regex: '(Windows Phone) (\d+)\.(\d+)'
451
+ - regex: '(Windows Phone) OS (\d+)\.(\d+)'
394
452
  - regex: '(Windows ?Mobile)'
395
453
  os_replacement: 'Windows Mobile'
396
454
 
@@ -406,13 +464,15 @@ os_parsers:
406
464
  ##########
407
465
  - regex: '(Tizen)/(\d+)\.(\d+)'
408
466
 
409
-
410
-
411
467
  ##########
412
468
  # Mac OS
413
469
  # http://en.wikipedia.org/wiki/Mac_OS_X#Versions
414
470
  ##########
415
471
  - regex: '(Mac OS X) (\d+)[_.](\d+)(?:[_.](\d+))?'
472
+
473
+ # IE on Mac doesn't specify version number
474
+ - regex: 'Mac_PowerPC'
475
+ os_replacement: 'Mac OS'
416
476
 
417
477
  # builds before tiger don't seem to specify version?
418
478
 
@@ -472,10 +532,14 @@ os_parsers:
472
532
  - regex: '(MeeGo)'
473
533
  - regex: 'Symbian [Oo][Ss]'
474
534
  os_replacement: 'Symbian OS'
535
+ - regex: 'Series40;'
536
+ os_replacement: 'Nokia Series 40'
475
537
 
476
538
  ##########
477
539
  # BlackBerry devices
478
540
  ##########
541
+ - regex: '(BB10);.+Version/(\d+)\.(\d+)\.(\d+)'
542
+ os_replacement: 'BlackBerry OS'
479
543
  - regex: '(Black[Bb]erry)[0-9a-z]+/(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?'
480
544
  os_replacement: 'BlackBerry OS'
481
545
  - regex: '(Black[Bb]erry).+Version/(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?'
@@ -485,7 +549,24 @@ os_parsers:
485
549
  - regex: '(Play[Bb]ook)'
486
550
  os_replacement: 'BlackBerry Tablet OS'
487
551
  - regex: '(Black[Bb]erry)'
488
- os_replacement: 'Blackberry OS'
552
+ os_replacement: 'BlackBerry OS'
553
+
554
+ ##########
555
+ # Firefox OS
556
+ ##########
557
+ - regex: '\(Mobile;.+Firefox/\d+\.\d+'
558
+ os_replacement: 'Firefox OS'
559
+
560
+ ##########
561
+ # BREW
562
+ # yes, Brew is lower-cased for Brew MP
563
+ ##########
564
+ - regex: '(BREW)[ /](\d+)\.(\d+)\.(\d+)'
565
+ - regex: '(BREW);'
566
+ - regex: '(Brew MP|BMP)[ /](\d+)\.(\d+)\.(\d+)'
567
+ os_replacement: 'Brew MP'
568
+ - regex: 'BMP;'
569
+ os_replacement: 'Brew MP'
489
570
 
490
571
  ##########
491
572
  # Google TV
@@ -494,11 +575,14 @@ os_parsers:
494
575
  # Old style
495
576
  - regex: '(GoogleTV)\/\d+'
496
577
 
578
+ - regex: '(WebTV)/(\d+).(\d+)'
579
+
497
580
  ##########
498
581
  # Misc mobile
499
582
  ##########
500
- - regex: '(webOS|hpwOS)/(\d+)\.(\d+)(?:\.(\d+))?'
583
+ - regex: '(hpw|web)OS/(\d+)\.(\d+)(?:\.(\d+))?'
501
584
  os_replacement: 'webOS'
585
+ - regex: '(VRE);'
502
586
 
503
587
  ##########
504
588
  # Generic patterns
@@ -512,11 +596,13 @@ os_parsers:
512
596
 
513
597
  # first.second bits
514
598
  - regex: '(Ubuntu|Kindle|Bada|Lubuntu|BackTrack|Red Hat|Slackware)/(\d+)\.(\d+)'
515
- - regex: '(PlayStation Vita) (\d+)\.(\d+)'
516
599
 
517
600
  # just os
518
601
  - regex: '(Windows|OpenBSD|FreeBSD|NetBSD|Ubuntu|Kubuntu|Android|Arch Linux|CentOS|WeTab|Slackware)'
602
+ - regex: '(Linux)/(\d+)\.(\d+)'
519
603
  - regex: '(Linux|BSD)'
604
+ - regex: 'SunOS'
605
+ os_replacement: 'Solaris'
520
606
 
521
607
  device_parsers:
522
608
  ##########
@@ -547,16 +633,16 @@ device_parsers:
547
633
  device_replacement: 'HTC $1 (Sprint)'
548
634
  - regex: 'HTC ([A-Za-z0-9]+ [A-Z])'
549
635
  device_replacement: 'HTC $1'
550
- - regex: 'HTC-([A-Za-z0-9]+)'
551
- device_replacement: 'HTC $1'
552
- - regex: 'HTC_([A-Za-z0-9]+)'
553
- device_replacement: 'HTC $1'
554
- - regex: 'HTC ([A-Za-z0-9]+)'
636
+ - regex: 'HTC[-_/\s]([A-Za-z0-9]+)'
555
637
  device_replacement: 'HTC $1'
556
638
  - regex: '(ADR[A-Za-z0-9]+)'
557
639
  device_replacement: 'HTC $1'
558
640
  - regex: '(HTC)'
559
641
 
642
+ # Tesla Model S
643
+ - regex: '(QtCarBrowser)'
644
+ device_replacement: 'Tesla Model S'
645
+
560
646
  # Samsung
561
647
  - regex: '(SamsungSGHi560)'
562
648
  device_replacement: 'Samsung SGHi560'
@@ -567,14 +653,43 @@ device_parsers:
567
653
  - regex: 'SonyEricsson([A-Za-z0-9]+)/'
568
654
  device_replacement: 'Ericsson $1'
569
655
 
656
+ ##########
657
+ # PlayStation
658
+ # The Vita spoofs the Kindle
659
+ ##########
660
+ - regex: 'PLAYSTATION 3'
661
+ device_replacement: 'PlayStation 3'
662
+ - regex: '(PlayStation Portable)'
663
+ - regex: '(PlayStation Vita)'
664
+
665
+ ##########
666
+ # incomplete!
667
+ # Kindle
668
+ # http://amazonsilk.wordpress.com/useful-bits/silk-user-agent/
669
+ ##########
670
+ - regex: '(KFOT Build)'
671
+ device_replacement: 'Kindle Fire'
672
+ - regex: '(KFTT Build)'
673
+ device_replacement: 'Kindle Fire HD'
674
+ - regex: '(KFJWI Build)'
675
+ device_replacement: 'Kindle Fire HD 8.9" WiFi'
676
+ - regex: '(KFJWA Build)'
677
+ device_replacement: 'Kindle Fire HD 8.9" 4G'
678
+ - regex: '(Kindle Fire)'
679
+ - regex: '(Kindle)'
680
+ - regex: '(Silk)/(\d+)\.(\d+)(?:\.([0-9\-]+))?'
681
+ device_replacement: 'Kindle Fire'
682
+
570
683
  #########
571
684
  # Android General Device Matching (far from perfect)
572
685
  #########
573
- - regex: 'Android[\- ][\d]+\.[\d]+\; [A-Za-z]{2}\-[A-Za-z]{2}\; WOWMobile (.+) Build'
574
- - regex: 'Android[\- ][\d]+\.[\d]+\.[\d]+; [A-Za-z]{2}\-[A-Za-z]{2}\; (.+) Build'
575
- - regex: 'Android[\- ][\d]+\.[\d]+\-update1\; [A-Za-z]{2}\-[A-Za-z]{2}\; (.+) Build'
576
- - regex: 'Android[\- ][\d]+\.[\d]+\; [A-Za-z]{2}\-[A-Za-z]{2}\; (.+) Build'
686
+ - regex: 'Android[\- ][\d]+\.[\d]+; [A-Za-z]{2}\-[A-Za-z]{2}; WOWMobile (.+) Build'
687
+ - regex: 'Android[\- ][\d]+\.[\d]+\-update1; [A-Za-z]{2}\-[A-Za-z]{2}; (.+) Build'
688
+ - regex: 'Android[\- ][\d]+\.[\d]+\.[\d]+; [A-Za-z]{2}\-[A-Za-z]{2}; (.+) Build'
689
+ - regex: 'Android[\- ][\d]+\.[\d]+\.[\d]+;[A-Za-z]{2}\-[A-Za-z]{2};(.+) Build'
690
+ - regex: 'Android[\- ][\d]+\.[\d]+; [A-Za-z]{2}\-[A-Za-z]{2}; (.+) Build'
577
691
  - regex: 'Android[\- ][\d]+\.[\d]+\.[\d]+; (.+) Build'
692
+ - regex: 'Android[\- ][\d]+\.[\d]+; (.+) Build'
578
693
 
579
694
  ##########
580
695
  # NOKIA
@@ -582,6 +697,8 @@ device_parsers:
582
697
  ##########
583
698
  - regex: 'NokiaN([0-9]+)'
584
699
  device_replacement: 'Nokia N$1'
700
+ - regex: 'NOKIA([A-Za-z0-9\v-]+)'
701
+ device_replacement: 'Nokia $1'
585
702
  - regex: 'Nokia([A-Za-z0-9\v-]+)'
586
703
  device_replacement: 'Nokia $1'
587
704
  - regex: 'NOKIA ([A-Za-z0-9\-]+)'
@@ -594,14 +711,19 @@ device_parsers:
594
711
  device_replacement: 'Nokia'
595
712
 
596
713
  ##########
597
- # Blackberry
714
+ # BlackBerry
598
715
  # http://www.useragentstring.com/pages/BlackBerry/
599
716
  ##########
717
+ - regex: 'BB10; ([A-Za-z0-9\- ]+)\)'
718
+ device_replacement: 'BlackBerry $1'
600
719
  - regex: '(PlayBook).+RIM Tablet OS'
601
- device_replacement: 'Blackberry Playbook'
602
- - regex: '(Black[Bb]erry [0-9]+);'
720
+ device_replacement: 'BlackBerry Playbook'
721
+ - regex: 'Black[Bb]erry ([0-9]+);'
722
+ device_replacement: 'BlackBerry $1'
603
723
  - regex: 'Black[Bb]erry([0-9]+)'
604
724
  device_replacement: 'BlackBerry $1'
725
+ - regex: 'Black[Bb]erry;'
726
+ device_replacement: 'BlackBerry'
605
727
 
606
728
  ##########
607
729
  # PALM / HP
@@ -611,8 +733,8 @@ device_parsers:
611
733
  device_replacement: 'Palm Pre'
612
734
  - regex: '(Pixi)/(\d+)\.(\d+)'
613
735
  device_replacement: 'Palm Pixi'
614
- - regex: '(Touchpad)/(\d+)\.(\d+)'
615
- device_replacement: 'HP Touchpad'
736
+ - regex: '(Touch[Pp]ad)/(\d+)\.(\d+)'
737
+ device_replacement: 'HP TouchPad'
616
738
  - regex: 'HPiPAQ([A-Za-z0-9]+)/(\d+).(\d+)'
617
739
  device_replacement: 'HP iPAQ $1'
618
740
  - regex: 'Palm([A-Za-z0-9]+)'
@@ -622,21 +744,6 @@ device_parsers:
622
744
  - regex: 'webOS.*(P160UNA)/(\d+).(\d+)'
623
745
  device_replacement: 'HP Veer'
624
746
 
625
- ##########
626
- # PlayStation
627
- ##########
628
- - regex: '(PlayStation Portable)'
629
- - regex: '(PlayStation Vita)'
630
-
631
- ##########
632
- # incomplete!
633
- # Kindle
634
- ##########
635
- - regex: '(Kindle Fire)'
636
- - regex: '(Kindle)'
637
- - regex: '(Silk)/(\d+)\.(\d+)(?:\.([0-9\-]+))?'
638
- device_replacement: 'Kindle Fire'
639
-
640
747
  ##########
641
748
  # AppleTV
642
749
  # No built in browser that I can tell
@@ -665,6 +772,14 @@ device_parsers:
665
772
  - regex: 'acer_([A-Za-z0-9]+)_'
666
773
  device_replacement: 'Acer $1'
667
774
 
775
+ ##########
776
+ # Alcatel
777
+ ##########
778
+ - regex: 'ALCATEL-([A-Za-z0-9]+)'
779
+ device_replacement: 'Alcatel $1'
780
+ - regex: 'Alcatel-([A-Za-z0-9]+)'
781
+ device_replacement: 'Alcatel $1'
782
+
668
783
  ##########
669
784
  # Amoi
670
785
  ##########
@@ -702,14 +817,18 @@ device_parsers:
702
817
  ##########
703
818
  - regex: 'DoCoMo/2\.0 ([A-Za-z0-9]+)'
704
819
  device_replacement: 'DoCoMo $1'
705
- - regex: '([A-Za-z0-9]+)\_W\;FOMA'
820
+ - regex: '([A-Za-z0-9]+)_W\;FOMA'
706
821
  device_replacement: 'DoCoMo $1'
707
822
  - regex: '([A-Za-z0-9]+)\;FOMA'
708
823
  device_replacement: 'DoCoMo $1'
709
824
 
710
825
  ##########
711
- # Huawei Vodafone
826
+ # Huawei
712
827
  ##########
828
+ - regex: 'Huawei([A-Za-z0-9]+)'
829
+ device_replacement: 'Huawei $1'
830
+ - regex: 'HUAWEI-([A-Za-z0-9]+)'
831
+ device_replacement: 'Huawei $1'
713
832
  - regex: 'vodafone([A-Za-z0-9]+)'
714
833
  device_replacement: 'Huawei Vodafone $1'
715
834
 
@@ -732,7 +851,7 @@ device_parsers:
732
851
  ##########
733
852
  - regex: 'Lenovo\-([A-Za-z0-9]+)'
734
853
  device_replacement: 'Lenovo $1'
735
- - regex: 'Lenovo\_([A-Za-z0-9]+)'
854
+ - regex: 'Lenovo_([A-Za-z0-9]+)'
736
855
  device_replacement: 'Lenovo $1'
737
856
 
738
857
  ##########
@@ -773,7 +892,21 @@ device_parsers:
773
892
  device_replacement: 'Motorola $1'
774
893
  - regex: 'MOT\-([A-Za-z0-9]+)'
775
894
  device_replacement: 'Motorola $1'
895
+
896
+ ##########
897
+ # nintendo
898
+ ##########
899
+ - regex: '(Nintendo WiiU)'
900
+ device_replacement: 'Nintendo Wii U'
901
+ - regex: 'Nintendo (DS|3DS|DSi|Wii);'
902
+ device_replacement: 'Nintendo $1'
776
903
 
904
+ ##########
905
+ # pantech
906
+ ##########
907
+ - regex: 'Pantech([A-Za-z0-9]+)'
908
+ device_replacement: 'Pantech $1'
909
+
777
910
  ##########
778
911
  # philips
779
912
  ##########
@@ -790,6 +923,12 @@ device_parsers:
790
923
  - regex: 'SAMSUNG\; ([A-Za-z0-9\-]+)'
791
924
  device_replacement: 'Samsung $1'
792
925
 
926
+ ##########
927
+ # Sega
928
+ ##########
929
+ - regex: 'Dreamcast'
930
+ device_replacement: 'Sega Dreamcast'
931
+
793
932
  ##########
794
933
  # Softbank
795
934
  ##########
@@ -799,10 +938,9 @@ device_parsers:
799
938
  device_replacement: 'Softbank $1'
800
939
 
801
940
  ##########
802
- # Sony
941
+ # WebTV
803
942
  ##########
804
- - regex: '(PlayStation3 PPC)'
805
- device_replacement: 'Playstation 3'
943
+ - regex: '(WebTV)/(\d+).(\d+)'
806
944
 
807
945
  ##########
808
946
  # Generic Smart Phone
@@ -813,13 +951,13 @@ device_parsers:
813
951
  ##########
814
952
  # Generic Feature Phone
815
953
  ##########
816
- - regex: '^(1207|3gso|4thp|501i|502i|503i|504i|505i|506i|6310|6590|770s|802s|a wa|acer|acs\-|airn|alav|asus|attw|au\-m|aur |aus |abac|acoo|aiko|alco|alca|amoi|anex|anny|anyw|aptu|arch|argo|bell|bird|bw\-n|bw\-u|beck|benq|bilb|blac|c55/|cdm\-|chtm|capi|comp|cond|craw|dall|dbte|dc\-s|dica|ds\-d|ds12|dait|devi|dmob|doco|dopo|el49|erk0|esl8|ez40|ez60|ez70|ezos|ezze|elai|emul|eric|ezwa|fake|fly\-|fly\_|g\-mo|g1 u|g560|gf\-5|grun|gene|go.w|good|grad|hcit|hd\-m|hd\-p|hd\-t|hei\-|hp i|hpip|hs\-c|htc |htc\-|htca|htcg)'
954
+ - regex: '^(1207|3gso|4thp|501i|502i|503i|504i|505i|506i|6310|6590|770s|802s|a wa|acer|acs\-|airn|alav|asus|attw|au\-m|aur |aus |abac|acoo|aiko|alco|alca|amoi|anex|anny|anyw|aptu|arch|argo|bell|bird|bw\-n|bw\-u|beck|benq|bilb|blac|c55/|cdm\-|chtm|capi|comp|cond|craw|dall|dbte|dc\-s|dica|ds\-d|ds12|dait|devi|dmob|doco|dopo|el49|erk0|esl8|ez40|ez60|ez70|ezos|ezze|elai|emul|eric|ezwa|fake|fly\-|fly_|g\-mo|g1 u|g560|gf\-5|grun|gene|go.w|good|grad|hcit|hd\-m|hd\-p|hd\-t|hei\-|hp i|hpip|hs\-c|htc |htc\-|htca|htcg)'
817
955
  device_replacement: 'Generic Feature Phone'
818
- - regex: '^(htcp|htcs|htct|htc\_|haie|hita|huaw|hutc|i\-20|i\-go|i\-ma|i230|iac|iac\-|iac/|ig01|im1k|inno|iris|jata|java|kddi|kgt|kgt/|kpt |kwc\-|klon|lexi|lg g|lg\-a|lg\-b|lg\-c|lg\-d|lg\-f|lg\-g|lg\-k|lg\-l|lg\-m|lg\-o|lg\-p|lg\-s|lg\-t|lg\-u|lg\-w|lg/k|lg/l|lg/u|lg50|lg54|lge\-|lge/|lynx|leno|m1\-w|m3ga|m50/|maui|mc01|mc21|mcca|medi|meri|mio8|mioa|mo01|mo02|mode|modo|mot |mot\-|mt50|mtp1|mtv |mate|maxo|merc|mits|mobi|motv|mozz|n100|n101|n102|n202|n203|n300|n302|n500|n502|n505|n700|n701|n710|nec\-|nem\-|newg|neon)'
956
+ - regex: '^(htcp|htcs|htct|htc_|haie|hita|huaw|hutc|i\-20|i\-go|i\-ma|i230|iac|iac\-|iac/|ig01|im1k|inno|iris|jata|java|kddi|kgt|kgt/|kpt |kwc\-|klon|lexi|lg g|lg\-a|lg\-b|lg\-c|lg\-d|lg\-f|lg\-g|lg\-k|lg\-l|lg\-m|lg\-o|lg\-p|lg\-s|lg\-t|lg\-u|lg\-w|lg/k|lg/l|lg/u|lg50|lg54|lge\-|lge/|lynx|leno|m1\-w|m3ga|m50/|maui|mc01|mc21|mcca|medi|meri|mio8|mioa|mo01|mo02|mode|modo|mot |mot\-|mt50|mtp1|mtv |mate|maxo|merc|mits|mobi|motv|mozz|n100|n101|n102|n202|n203|n300|n302|n500|n502|n505|n700|n701|n710|nec\-|nem\-|newg|neon)'
819
957
  device_replacement: 'Generic Feature Phone'
820
958
  - regex: '^(netf|noki|nzph|o2 x|o2\-x|opwv|owg1|opti|oran|ot\-s|p800|pand|pg\-1|pg\-2|pg\-3|pg\-6|pg\-8|pg\-c|pg13|phil|pn\-2|pt\-g|palm|pana|pire|pock|pose|psio|qa\-a|qc\-2|qc\-3|qc\-5|qc\-7|qc07|qc12|qc21|qc32|qc60|qci\-|qwap|qtek|r380|r600|raks|rim9|rove|s55/|sage|sams|sc01|sch\-|scp\-|sdk/|se47|sec\-|sec0|sec1|semc|sgh\-|shar|sie\-|sk\-0|sl45|slid|smb3|smt5|sp01|sph\-|spv |spv\-|sy01|samm|sany|sava|scoo|send|siem|smar|smit|soft|sony|t\-mo|t218|t250|t600|t610|t618|tcl\-|tdg\-|telm|tim\-|ts70|tsm\-|tsm3|tsm5|tx\-9|tagt)'
821
959
  device_replacement: 'Generic Feature Phone'
822
- - regex: '^(talk|teli|topl|tosh|up.b|upg1|utst|v400|v750|veri|vk\-v|vk40|vk50|vk52|vk53|vm40|vx98|virg|vite|voda|vulc|w3c |w3c\-|wapj|wapp|wapu|wapm|wig |wapi|wapr|wapv|wapy|wapa|waps|wapt|winc|winw|wonu|x700|xda2|xdag|yas\-|your|zte\-|zeto|aste|audi|avan|blaz|brew|brvw|bumb|ccwa|cell|cldc|cmd\-|dang|eml2|fetc|hipt|http|ibro|idea|ikom|ipaq|jbro|jemu|jigs|keji|kyoc|kyok|libw|m\-cr|midp|mmef|moto|mwbp|mywa|newt|nok6|o2im|pant|pdxg|play|pluc|port|prox|rozo|sama|seri|smal|symb|treo|upsi|vx52|vx53|vx60|vx61|vx70|vx80|vx81|vx83|vx85|wap\-|webc|whit|wmlb|xda\-|xda\_)'
960
+ - regex: '^(talk|teli|topl|tosh|up.b|upg1|utst|v400|v750|veri|vk\-v|vk40|vk50|vk52|vk53|vm40|vx98|virg|vite|voda|vulc|w3c |w3c\-|wapj|wapp|wapu|wapm|wig |wapi|wapr|wapv|wapy|wapa|waps|wapt|winc|winw|wonu|x700|xda2|xdag|yas\-|your|zte\-|zeto|aste|audi|avan|blaz|brew|brvw|bumb|ccwa|cell|cldc|cmd\-|dang|eml2|fetc|hipt|http|ibro|idea|ikom|ipaq|jbro|jemu|jigs|keji|kyoc|kyok|libw|m\-cr|midp|mmef|moto|mwbp|mywa|newt|nok6|o2im|pant|pdxg|play|pluc|port|prox|rozo|sama|seri|smal|symb|treo|upsi|vx52|vx53|vx60|vx61|vx70|vx80|vx81|vx83|vx85|wap\-|webc|whit|wmlb|xda\-|xda_)'
823
961
  device_replacement: 'Generic Feature Phone'
824
962
 
825
963
  ##########
@@ -828,43 +966,3 @@ device_parsers:
828
966
  - regex: '(bot|borg|google(^tv)|yahoo|slurp|msnbot|msrbot|openbot|archiver|netresearch|lycos|scooter|altavista|teoma|gigabot|baiduspider|blitzbot|oegp|charlotte|furlbot|http%20client|polybot|htdig|ichiro|mogimogi|larbin|pompos|scrubby|searchsight|seekbot|semanticdiscovery|silk|snappy|speedy|spider|voila|vortex|voyager|zao|zeal|fast\-webcrawler|converacrawler|dataparksearch|findlinks)'
829
967
  device_replacement: 'Spider'
830
968
 
831
-
832
- mobile_user_agent_families:
833
- - 'Firefox Mobile'
834
- - 'Opera Mobile'
835
- - 'Opera Mini'
836
- - 'Mobile Safari'
837
- - 'webOS'
838
- - 'IE Mobile'
839
- - 'Playstation Portable'
840
- - 'Nokia'
841
- - 'Blackberry'
842
- - 'Palm'
843
- - 'Silk'
844
- - 'Android'
845
- - 'Maemo'
846
- - 'Obigo'
847
- - 'Netfront'
848
- - 'AvantGo'
849
- - 'Teleca'
850
- - 'SEMC-Browser'
851
- - 'Bolt'
852
- - 'Iris'
853
- - 'UP.Browser'
854
- - 'Symphony'
855
- - 'Minimo'
856
- - 'Bunjaloo'
857
- - 'Jasmine'
858
- - 'Dolfin'
859
- - 'Polaris'
860
- - 'BREW'
861
- - 'Chrome Mobile'
862
- - 'UC Browser'
863
- - 'Tizen Browser'
864
-
865
- # Some select mobile OSs report a desktop browser.
866
- # make sure we note they're mobile
867
- mobile_os_families:
868
- - 'Windows Phone 6.5'
869
- - 'Windows CE'
870
- - 'Symbian OS'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: user_agent_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-10 00:00:00.000000000 Z
12
+ date: 2013-02-18 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: A simple, comprehensive Ruby gem for parsing user agent strings with
15
15
  the help of BrowserScope's UA database
@@ -21,6 +21,7 @@ files:
21
21
  - MIT-LICENSE
22
22
  - Readme.md
23
23
  - lib/user_agent_parser.rb
24
+ - lib/user_agent_parser/device.rb
24
25
  - lib/user_agent_parser/operating_system.rb
25
26
  - lib/user_agent_parser/parser.rb
26
27
  - lib/user_agent_parser/user_agent.rb
@@ -38,7 +39,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
38
39
  requirements:
39
40
  - - ! '>='
40
41
  - !ruby/object:Gem::Version
41
- version: 1.9.2
42
+ version: 1.8.7
42
43
  required_rubygems_version: !ruby/object:Gem::Requirement
43
44
  none: false
44
45
  requirements: