browser 1.1.0 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/.bundle/config +2 -0
  3. data/.hound.yml +26 -6
  4. data/.rubocop.yml +26 -6
  5. data/CHANGELOG.md +10 -0
  6. data/README.md +177 -41
  7. data/Rakefile +1 -1
  8. data/bin/rake +16 -0
  9. data/bot_exceptions.yml +1 -0
  10. data/bots.yml +188 -192
  11. data/browser.gemspec +14 -28
  12. data/languages.yml +54 -54
  13. data/lib/browser.rb +82 -202
  14. data/lib/browser/accept_language.rb +47 -0
  15. data/lib/browser/action_controller.rb +1 -1
  16. data/lib/browser/base.rb +159 -0
  17. data/lib/browser/blackberry.rb +21 -0
  18. data/lib/browser/bot.rb +63 -0
  19. data/lib/browser/chrome.rb +23 -0
  20. data/lib/browser/detect_version.rb +19 -0
  21. data/lib/browser/device.rb +175 -0
  22. data/lib/browser/device/base.rb +11 -0
  23. data/lib/browser/device/blackberry_playbook.rb +17 -0
  24. data/lib/browser/device/ipad.rb +17 -0
  25. data/lib/browser/device/iphone.rb +17 -0
  26. data/lib/browser/device/ipod_touch.rb +17 -0
  27. data/lib/browser/device/kindle.rb +17 -0
  28. data/lib/browser/device/kindle_fire.rb +17 -0
  29. data/lib/browser/device/playstation3.rb +17 -0
  30. data/lib/browser/device/playstation4.rb +17 -0
  31. data/lib/browser/device/psp.rb +17 -0
  32. data/lib/browser/device/psvita.rb +17 -0
  33. data/lib/browser/device/surface.rb +23 -0
  34. data/lib/browser/device/tv.rb +17 -0
  35. data/lib/browser/device/unknown.rb +17 -0
  36. data/lib/browser/device/wii.rb +17 -0
  37. data/lib/browser/device/wiiu.rb +17 -0
  38. data/lib/browser/device/xbox_360.rb +17 -0
  39. data/lib/browser/device/xbox_one.rb +17 -0
  40. data/lib/browser/edge.rb +19 -0
  41. data/lib/browser/firefox.rb +19 -0
  42. data/lib/browser/generic.rb +32 -0
  43. data/lib/browser/internet_explorer.rb +61 -0
  44. data/lib/browser/meta/base.rb +1 -1
  45. data/lib/browser/meta/device.rb +9 -0
  46. data/lib/browser/meta/generic_browser.rb +1 -1
  47. data/lib/browser/meta/id.rb +1 -1
  48. data/lib/browser/meta/ie.rb +1 -1
  49. data/lib/browser/meta/ios.rb +2 -2
  50. data/lib/browser/meta/mobile.rb +2 -2
  51. data/lib/browser/meta/modern.rb +1 -1
  52. data/lib/browser/meta/platform.rb +2 -2
  53. data/lib/browser/meta/proxy.rb +1 -1
  54. data/lib/browser/meta/safari.rb +1 -1
  55. data/lib/browser/meta/tablet.rb +9 -0
  56. data/lib/browser/meta/webkit.rb +1 -1
  57. data/lib/browser/middleware.rb +1 -1
  58. data/lib/browser/middleware/context.rb +2 -2
  59. data/lib/browser/middleware/context/additions.rb +1 -1
  60. data/lib/browser/middleware/context/url_methods.rb +3 -3
  61. data/lib/browser/nokia.rb +19 -0
  62. data/lib/browser/opera.rb +19 -0
  63. data/lib/browser/phantom_js.rb +19 -0
  64. data/lib/browser/platform.rb +152 -0
  65. data/lib/browser/platform/adobe_air.rb +21 -0
  66. data/lib/browser/platform/android.rb +21 -0
  67. data/lib/browser/platform/base.rb +15 -0
  68. data/lib/browser/platform/blackberry.rb +21 -0
  69. data/lib/browser/platform/chrome_os.rb +21 -0
  70. data/lib/browser/platform/firefox_os.rb +21 -0
  71. data/lib/browser/platform/ios.rb +28 -0
  72. data/lib/browser/platform/linux.rb +21 -0
  73. data/lib/browser/platform/mac.rb +21 -0
  74. data/lib/browser/platform/other.rb +21 -0
  75. data/lib/browser/platform/windows.rb +21 -0
  76. data/lib/browser/platform/windows_mobile.rb +21 -0
  77. data/lib/browser/platform/windows_phone.rb +21 -0
  78. data/lib/browser/rails.rb +3 -2
  79. data/lib/browser/safari.rb +22 -0
  80. data/lib/browser/testing.rb +23 -0
  81. data/lib/browser/uc_browser.rb +19 -0
  82. data/lib/browser/version.rb +2 -7
  83. data/search_engines.yml +5 -5
  84. data/test/browser_test.rb +71 -172
  85. data/test/middleware_test.rb +7 -7
  86. data/test/sample_app.rb +3 -3
  87. data/test/test_helper.rb +11 -10
  88. data/test/ua.yml +15 -26
  89. data/test/ua_bots.yml +27 -0
  90. data/test/ua_search_engines.yml +6 -0
  91. data/test/unit/accept_language_test.rb +92 -0
  92. data/test/unit/adobe_air_test.rb +5 -10
  93. data/test/unit/android_test.rb +44 -49
  94. data/test/unit/blackberry_test.rb +55 -77
  95. data/test/unit/bots_test.rb +52 -84
  96. data/test/unit/chrome_test.rb +48 -42
  97. data/test/unit/console_test.rb +4 -62
  98. data/test/unit/device_test.rb +193 -0
  99. data/test/unit/firefox_test.rb +32 -27
  100. data/test/unit/ie_test.rb +223 -238
  101. data/test/unit/ios_app_test.rb +3 -3
  102. data/test/unit/ios_test.rb +87 -94
  103. data/test/unit/kindle_test.rb +10 -24
  104. data/test/unit/nokia_test.rb +3 -7
  105. data/test/unit/opera_test.rb +20 -30
  106. data/test/unit/platform_test.rb +161 -0
  107. data/test/unit/proxy_test.rb +3 -3
  108. data/test/unit/safari_test.rb +66 -0
  109. data/test/unit/uc_browser_test.rb +3 -7
  110. data/test/unit/windows_phone_test.rb +19 -40
  111. data/test/unit/windows_test.rb +32 -34
  112. metadata +73 -28
  113. data/lib/browser/methods/blackberry.rb +0 -51
  114. data/lib/browser/methods/bots.rb +0 -35
  115. data/lib/browser/methods/consoles.rb +0 -43
  116. data/lib/browser/methods/devices.rb +0 -41
  117. data/lib/browser/methods/ie.rb +0 -99
  118. data/lib/browser/methods/language.rb +0 -16
  119. data/lib/browser/methods/mobile.rb +0 -35
  120. data/lib/browser/methods/platform.rb +0 -152
  121. data/lib/browser/methods/proxy.rb +0 -8
  122. data/lib/browser/methods/tv.rb +0 -8
@@ -0,0 +1,47 @@
1
+ module Browser
2
+ class AcceptLanguage
3
+ def self.languages
4
+ @languages ||= YAML.load_file(Browser.root.join("languages.yml"))
5
+ end
6
+
7
+ def self.parse(accept_language)
8
+ return [] unless accept_language
9
+
10
+ accept_language
11
+ .split(",")
12
+ .map {|string| string.squeeze(" ").strip }
13
+ .map {|part| new(part) }
14
+ .sort_by(&:quality)
15
+ .reverse
16
+ end
17
+
18
+ attr_reader :part
19
+
20
+ def initialize(part)
21
+ @part = part
22
+ end
23
+
24
+ def full
25
+ @full ||= [code, region].compact.join("-")
26
+ end
27
+
28
+ def name
29
+ self.class.languages[full] || self.class.languages[code]
30
+ end
31
+
32
+ def code
33
+ @code ||= part[/\A([^-;]+)/, 1]
34
+ end
35
+
36
+ def region
37
+ @region ||= begin
38
+ region = part[/\A(?:.*?)-([^;-]+)/, 1]
39
+ region.upcase if region
40
+ end
41
+ end
42
+
43
+ def quality
44
+ @quality ||= Float(part[/;q=([\d.]+)/, 1] || 1.0)
45
+ end
46
+ end
47
+ end
@@ -1,6 +1,6 @@
1
1
  require "action_controller/railtie"
2
2
 
3
- class Browser
3
+ module Browser
4
4
  module ActionController
5
5
  extend ActiveSupport::Concern
6
6
 
@@ -0,0 +1,159 @@
1
+ module Browser
2
+ class Base
3
+ include DetectVersion
4
+
5
+ attr_reader :ua
6
+
7
+ # Return an array with all preferred languages that this browser accepts.
8
+ attr_reader :accept_language
9
+
10
+ def initialize(ua, accept_language: nil)
11
+ @ua = ua
12
+ @accept_language = AcceptLanguage.parse(accept_language)
13
+ end
14
+
15
+ # Return a meta info about this browser.
16
+ def meta
17
+ Meta.constants.each_with_object(Set.new) do |meta_name, meta|
18
+ meta_class = Meta.const_get(meta_name)
19
+ meta.merge(meta_class.new(self).to_a)
20
+ end.to_a
21
+ end
22
+
23
+ alias_method :to_a, :meta
24
+
25
+ # Return meta representation as string.
26
+ def to_s
27
+ meta.to_a.join(" ")
28
+ end
29
+
30
+ def version
31
+ full_version.split(".").first
32
+ end
33
+
34
+ # Return the platform.
35
+ def platform
36
+ @platform ||= Platform.new(ua)
37
+ end
38
+
39
+ # Return the bot info.
40
+ def bot
41
+ @bot ||= Bot.new(ua)
42
+ end
43
+
44
+ # Detect if current user agent is from a bot.
45
+ def bot?
46
+ bot.bot?
47
+ end
48
+
49
+ # Return the device info.
50
+ def device
51
+ @device ||= Device.new(ua)
52
+ end
53
+
54
+ # Return true if browser is modern (Webkit, Firefox 17+, IE9+, Opera 12+).
55
+ def modern?
56
+ Browser.modern_rules.any? {|rule| rule === self } # rubocop:disable Metrics/LineLength, Style/CaseEquality
57
+ end
58
+
59
+ # Detect if browser is Microsoft Internet Explorer.
60
+ def ie?(expected_version = nil)
61
+ InternetExplorer.new(ua).match? &&
62
+ detect_version?(version, expected_version)
63
+ end
64
+
65
+ # Detect if browser is Microsoft Edge.
66
+ def edge?
67
+ Edge.new(ua).match?
68
+ end
69
+
70
+ def compatibility_view?
71
+ false
72
+ end
73
+
74
+ def msie_full_version
75
+ "0.0"
76
+ end
77
+
78
+ def msie_version
79
+ "0"
80
+ end
81
+
82
+ # Detect if browser is WebKit-based.
83
+ def webkit?
84
+ ua =~ /AppleWebKit/i && !edge?
85
+ end
86
+
87
+ # Detect if browser is QuickTime
88
+ def quicktime?
89
+ ua =~ /QuickTime/i
90
+ end
91
+
92
+ # Detect if browser is Apple CoreMedia.
93
+ def core_media?
94
+ ua =~ /CoreMedia/
95
+ end
96
+
97
+ # Detect if browser is PhantomJS
98
+ def phantom_js?
99
+ PhantomJS.new(ua).match?
100
+ end
101
+
102
+ # Detect if browser is Safari.
103
+ def safari?(expected_version = nil)
104
+ Safari.new(ua).match? && detect_version?(version, expected_version)
105
+ end
106
+
107
+ def safari_webapp_mode?
108
+ (device.ipad? || device.iphone?) && ua =~ /AppleWebKit/
109
+ end
110
+
111
+ # Detect if browser is Firefox.
112
+ def firefox?
113
+ Firefox.new(ua).match?
114
+ end
115
+
116
+ # Detect if browser is Chrome.
117
+ def chrome?
118
+ Chrome.new(ua).match?
119
+ end
120
+
121
+ # Detect if browser is Opera.
122
+ def opera?
123
+ Opera.new(ua).match?
124
+ end
125
+
126
+ # Detect if browser is Yandex.
127
+ def yandex?
128
+ ua =~ /YaBrowser/
129
+ end
130
+
131
+ # Detect if browser is UCBrowser.
132
+ def uc_browser?
133
+ UCBrowser.new(ua).match?
134
+ end
135
+
136
+ # Detect if browser is Nokia S40 Ovi Browser.
137
+ def nokia?
138
+ Nokia.new(ua).match?
139
+ end
140
+
141
+ # Detect if browser is Opera Mini.
142
+ def opera_mini?
143
+ ua =~ /Opera Mini/
144
+ end
145
+
146
+ def webkit_full_version
147
+ ua[%r[AppleWebKit/([\d.]+)], 1] || "0.0"
148
+ end
149
+
150
+ def known?
151
+ id != :generic
152
+ end
153
+
154
+ # Detect if browser is a proxy browser.
155
+ def proxy?
156
+ nokia? || uc_browser? || opera_mini?
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,21 @@
1
+ module Browser
2
+ class BlackBerry < Base
3
+ def id
4
+ :blackberry
5
+ end
6
+
7
+ def name
8
+ "BlackBerry"
9
+ end
10
+
11
+ def full_version
12
+ ua[%r[BlackBerry[\da-z]+/([\d.]+)], 1] ||
13
+ ua[%r[Version/([\d.]+)], 1] ||
14
+ "0.0"
15
+ end
16
+
17
+ def match?
18
+ ua =~ /BlackBerry|BB10/
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,63 @@
1
+ module Browser
2
+ class Bot
3
+ def self.detect_empty_ua!
4
+ @detect_empty_ua = true
5
+ end
6
+
7
+ def self.detect_empty_ua?
8
+ !!@detect_empty_ua
9
+ end
10
+
11
+ def self.bots
12
+ @bots ||= YAML.load_file(Browser.root.join("bots.yml"))
13
+ end
14
+
15
+ def self.bot_exceptions
16
+ @bot_exceptions ||= YAML
17
+ .load_file(Browser.root.join("bot_exceptions.yml"))
18
+ end
19
+
20
+ def self.search_engines
21
+ @search_engines ||= YAML
22
+ .load_file(Browser.root.join("search_engines.yml"))
23
+ end
24
+
25
+ attr_reader :ua
26
+
27
+ def initialize(ua)
28
+ @ua = ua
29
+ end
30
+
31
+ def bot?
32
+ bot_with_empty_ua? || (!bot_exception? && detect_bot?)
33
+ end
34
+
35
+ def search_engine?
36
+ self.class.search_engines.any? {|key, _| downcased_ua.include?(key) }
37
+ end
38
+
39
+ def name
40
+ return unless bot?
41
+ return "Generic Bot" if bot_with_empty_ua?
42
+ self.class.bots.find {|key, _| downcased_ua.include?(key) }.last
43
+ end
44
+
45
+ private
46
+
47
+ def bot_with_empty_ua?
48
+ self.class.detect_empty_ua? && ua.strip == ""
49
+ end
50
+
51
+ def bot_exception?
52
+ self.class.bot_exceptions.any? {|key| downcased_ua.include?(key) }
53
+ end
54
+
55
+ def detect_bot?
56
+ self.class.bots.any? {|key, _| downcased_ua.include?(key) }
57
+ end
58
+
59
+ def downcased_ua
60
+ @downcased_ua ||= ua.downcase
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,23 @@
1
+ module Browser
2
+ class Chrome < Base
3
+ def id
4
+ :chrome
5
+ end
6
+
7
+ def name
8
+ "Chrome"
9
+ end
10
+
11
+ def full_version
12
+ # Each regex on its own line to enforce precedence.
13
+ ua[%r[Chrome/([\d.]+)], 1] ||
14
+ ua[%r[CriOS/([\d.]+)], 1] ||
15
+ ua[%r[Safari/([\d.]+)], 1] ||
16
+ ua[%r[AppleWebKit/([\d.]+)], 1]
17
+ end
18
+
19
+ def match?
20
+ ua =~ /Chrome|CriOS/ && !opera? && !edge?
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ module Browser
2
+ module DetectVersion
3
+ private
4
+
5
+ def detect_version?(actual_version, expected_version)
6
+ return true unless expected_version
7
+
8
+ expected_version = parse_version(expected_version)
9
+ actual_version = parse_version(actual_version)
10
+
11
+ Gem::Requirement.create(expected_version)
12
+ .satisfied_by?(Gem::Version.create(actual_version))
13
+ end
14
+
15
+ def parse_version(version)
16
+ version.kind_of?(Numeric) ? "#{version}" : version
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,175 @@
1
+ module Browser
2
+ class Device
3
+ attr_reader :ua
4
+
5
+ def initialize(ua)
6
+ @ua = ua
7
+ end
8
+
9
+ def subject
10
+ @subject ||= [
11
+ XboxOne.new(ua),
12
+ Xbox360.new(ua),
13
+ Surface.new(ua),
14
+ TV.new(ua),
15
+ BlackBerryPlaybook.new(ua),
16
+ WiiU.new(ua),
17
+ Wii.new(ua),
18
+ KindleFire.new(ua),
19
+ Kindle.new(ua),
20
+ PlayStation4.new(ua),
21
+ PlayStation3.new(ua),
22
+ PSVita.new(ua),
23
+ PSP.new(ua),
24
+ Iphone.new(ua),
25
+ Ipad.new(ua),
26
+ IpodTouch.new(ua),
27
+ Unknown.new(ua)
28
+ ].find(&:match?)
29
+ end
30
+
31
+ def id
32
+ subject.id
33
+ end
34
+
35
+ def name
36
+ subject.name
37
+ end
38
+
39
+ # Detect if browser is tablet (currently iPad, Android, Surface or
40
+ # Playbook).
41
+ def tablet?
42
+ !!(
43
+ ipad? ||
44
+ (platform.android? && !detect_mobile?) || surface? || playbook?
45
+ )
46
+ end
47
+
48
+ # Detect if browser is mobile.
49
+ def mobile?
50
+ detect_mobile? && !tablet?
51
+ end
52
+
53
+ def ipad?
54
+ id == :ipad
55
+ end
56
+
57
+ def unknown?
58
+ id == :unknown
59
+ end
60
+
61
+ def ipod_touch?
62
+ id == :ipod_touch
63
+ end
64
+ alias_method :ipod?, :ipod_touch?
65
+
66
+ def iphone?
67
+ id == :iphone
68
+ end
69
+
70
+ def ps3?
71
+ id == :ps3
72
+ end
73
+ alias_method :playstation3?, :ps3?
74
+
75
+ def ps4?
76
+ id == :ps4
77
+ end
78
+ alias_method :playstation4?, :ps4?
79
+ alias_method :playstation4?, :ps4?
80
+
81
+ def psp?
82
+ id == :psp
83
+ end
84
+
85
+ def playstation_vita?
86
+ id == :psvita
87
+ end
88
+ alias_method :vita?, :playstation_vita?
89
+ alias_method :psp_vita?, :playstation_vita?
90
+
91
+ def kindle?
92
+ id == :kindle || kindle_fire?
93
+ end
94
+
95
+ def kindle_fire?
96
+ id == :kindle_fire
97
+ end
98
+
99
+ def nintendo_wii?
100
+ id == :wii
101
+ end
102
+ alias_method :wii?, :nintendo_wii?
103
+
104
+ def nintendo_wiiu?
105
+ id == :wiiu
106
+ end
107
+ alias_method :wiiu?, :nintendo_wiiu?
108
+
109
+ def blackberry_playbook?
110
+ id == :playbook
111
+ end
112
+ alias_method :playbook?, :blackberry_playbook?
113
+
114
+ def surface?
115
+ id == :surface
116
+ end
117
+
118
+ def tv?
119
+ id == :tv
120
+ end
121
+
122
+ # Detect if browser is Silk.
123
+ def silk?
124
+ ua =~ /Silk/
125
+ end
126
+
127
+ # Detect if browser is running under Xbox.
128
+ def xbox?
129
+ ua =~ /Xbox/
130
+ end
131
+
132
+ # Detect if browser is running under Xbox 360.
133
+ def xbox_360?
134
+ id == :xbox_360
135
+ end
136
+
137
+ # Detect if browser is running under Xbox One.
138
+ def xbox_one?
139
+ id == :xbox_one
140
+ end
141
+
142
+ # Detect if browser is running under PlayStation.
143
+ def playstation?
144
+ ps3? || ps4?
145
+ end
146
+
147
+ # Detect if browser is Nintendo.
148
+ def nintendo?
149
+ wii? || wiiu?
150
+ end
151
+
152
+ # Detect if browser is console (currently Xbox, PlayStation, or Nintendo).
153
+ def console?
154
+ xbox? || playstation? || nintendo?
155
+ end
156
+
157
+ private
158
+
159
+ # Regex taken from http://detectmobilebrowsers.com
160
+ # rubocop:disable Metrics/LineLength
161
+ def detect_mobile?
162
+ !!(
163
+ psp? ||
164
+ /zunewp7/i.match(ua) ||
165
+ /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.match(ua) ||
166
+ /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.match(ua[0..3])
167
+ )
168
+ end
169
+ # rubocop:enable Metrics/LineLength
170
+
171
+ def platform
172
+ @platform ||= Platform.new(ua)
173
+ end
174
+ end
175
+ end