Class: Brauser::Browser

Inherits:
Object
  • Object
show all
Defined in:
lib/brauser/browser.rb

Overview

This class represents a detection of the current user browser.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(agent = "", accept_language = "") ⇒ Browser

Creates a new browser.

Parameters:

  • agent (String) (defaults to: "")

    The User-Agent HTTP header.

  • accept_language (String) (defaults to: "")

    The Accept-Language HTTP header.



37
38
39
40
41
# File 'lib/brauser/browser.rb', line 37

def initialize(agent = "", accept_language = "")
  @agent = agent
  @accept_language = accept_language
  parse
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Boolean

Check if the browser is a specific one

Parameters:

  • method

    The browser engine to check.

  • args (Array)

    unused.

  • block (Proc)

    unused.

Returns:

  • (Boolean)

    true if browser match the engine, false otherwise.



109
110
111
# File 'lib/brauser/browser.rb', line 109

def method_missing(method, *args, &block)
  method.to_s =~ /(.+)\?$/ ? is?(name: Regexp.last_match[1]) : super(method, *args, &block)
end

Instance Attribute Details

#accept_languageString

Returns The raw Accept-Language HTTP header.

Returns:

  • (String)

    The raw Accept-Language HTTP header.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/brauser/browser.rb', line 28

class Browser
  attr_accessor :agent, :accept_language
  attr_reader :name, :human_name, :version, :platform, :human_platform, :languages, :human_languages
  alias_attribute :ua, :agent

  # Creates a new browser.
  #
  # @param agent [String] The User-Agent HTTP header.
  # @param accept_language [String] The Accept-Language HTTP header.
  def initialize(agent = "", accept_language = "")
    @agent = agent
    @accept_language = accept_language
    parse
  end

  # Returns an array of information about the browser. Information are strings which are suitable to use as CSS classes.
  #
  # For version, it will be included a class for every token of the version. For example, version `7.0.1.2` will return this:
  #
  # ```ruby
  # ["version-7", "version-7_0", "version-7_0_1", "version-7_0_1_2"]
  # ```
  #
  # If you provide a block (with accepts name, version and platform as arguments), it will be used for translating the name.
  #
  # @param join [String|NilClass] If non falsy, the separator to use to join information. If falsy, informations will be returned as array.
  # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
  # @param version [String|NilClass] If non falsy, the string to prepend to the version. If falsy, the version information will not be included.
  # @param platform [String|NilClass] If non falsy, the string to prepend to the platform. If falsy, the platform information will not be included.
  # @return [String|Array] CSS ready information of the current browser.
  def classes(join = " ", name: true, version: true, platform: true)
    rv = [name_to_str(name), version_to_str(version), platform_to_str(platform)].compact.uniq.flatten
    join ? rv.join(join) : rv
  end
  alias_method :meta, :classes
  alias_method :to_s, :classes

  # Checks if the browser accepts a specific language or languages.
  #
  # @param languages [Array] The list of languages.
  # @return [Boolean] `true` if at least one of requested languages is accepted, `false` otherwise.
  def accepts?(*languages)
    languages = normalize_query_arguments(languages)
    (@languages.keys & languages).present?
  end

  # Checks if the browser matches a specific query.
  #
  # @param name [Symbol|Array|NilClass] The list of names to check. Also, this meta-name is supported: `:tablet`.
  # @param engine [Symbol|Array|NilClass] Alias for `name`, **which has precedence over this.**
  # @param version [String|NilClass] The query to match the version.
  #   It must be a query in the form is `OPERATOR VALUE && ..`, where `OPERATOR` is one of `["<", "<=", "=", "==", ">=", ">"]`.
  #   You can also pass the value "capable", which will return true for Webkit browsers, IE 10 or above, Firefox 28 and above and Opera 15 and above.
  # @param platform [Symbol|Array|NilClass] The list of platforms to check.
  # @param languages [Symbol|Array|NilClass] The list of languages to check.
  # @return [Boolean] `true` if browser match the query, `false` otherwise.
  def is?(name: nil, engine: nil, version: nil, platform: nil, languages: nil)
    name ||= engine
    rv = name ? (@name == apply_aliases(normalize_query_arguments(name))) : true
    rv &&= query_version(version) if version
    rv &&= @platform == normalize_query_arguments(platform) if platform
    rv &&= accepts?(normalize_query_arguments(languages)) if languages
    rv
  end

  # Check if the browser is supported.
  #
  # @param browsers [Hash|String] A map of name and minimum supported major version, or a path to YAML file containing the map.
  # @return [Boolean] `true` if current browser is supported, `false` otherwise. If the name is not found in the map, `false` is returned.
  def supported?(browsers = {})
    browsers = YAML.load_file(browsers.to_s).symbolize_keys unless browsers.is_a?(Hash)
    minimum_version = browsers.with_indifferent_access[name.value]
    minimum_version ? is?(version: ">= #{minimum_version}") : false
  end

  # Check if the browser is a specific one
  #
  # @param method The browser engine to check.
  # @param args [Array] **unused.**
  # @param block [Proc] **unused.**
  # @return [Boolean] `true` if browser match the engine, `false` otherwise.
  def method_missing(method, *args, &block)
    method.to_s =~ /(.+)\?$/ ? is?(name: Regexp.last_match[1]) : super(method, *args, &block)
  end

  private

  # :nodoc:
  VERSION_TOKEN = /(?<operator>>=|<=|<|>|!=|(={1,2}))\s*(?<version>.+)/

  # :nodoc:
  def parse
    parser = Brauser::Parser.new
    parse_agent(parser)
    parse_languages(parser)
  end

  # :nodoc:
  def parse_agent(parser)
    agent = parser.parse_agent(@agent)

    if agent
      @name, @version, @platform = *agent
      @human_name = Brauser::Definitions.browsers[@name].try(:name) || "Unknown Browser"
      @human_platform = Brauser::Definitions.platforms[@platform].try(:name) || "Unknown Platform"
    else
      sanitize_agent
    end
  end

  # :nodoc:
  def sanitize_agent
    @name = @platform = Brauser::Value.new(:unknown)
    @human_name = @human_platform = Brauser::Value.new("Unknown")
    @version = Brauser::Value.new(Versionomy.parse("0.0"))
  end

  # :nodoc:
  def parse_languages(parser)
    languages = parser.parse_accept_language(@accept_language)
    @languages = languages
    @human_languages = languages.reduce({}) do |rv, (code, priority)|
      rv[Brauser::Definitions.languages[code].name] = priority
      rv
    end
  end

  # :nodoc:
  def name_to_str(name)
    if name
      name = "" if name.is_a?(TrueClass)
      rv = @name
      rv = [:msie_compatibility, :msie] if rv == :msie_compatibility
      rv.ensure_array(no_duplicates: true) { |n| "#{name}#{n}" }
    end
  end

  # :nodoc:
  def version_to_str(version)
    if version
      version = "version-" if version.is_a?(TrueClass)
      version_str = @version.values_array.reduce([]) do |rv, current|
        rv << [rv.last, current].compact.join("_")
        rv
      end

      version_str.map { |v| "#{version}#{v}" }
    end
  end

  # :nodoc:
  def platform_to_str(platform)
    return nil unless platform
    platform = "platform-" if platform.is_a?(TrueClass)
    "#{platform}#{@platform}"
  end

  # :nodoc:
  def normalize_query_arguments(arguments)
    sanitizer = ->(a) { a.ensure_string.downcase.gsub("_", "-").to_sym }
    arguments ? arguments.ensure_array(no_duplicates: true, compact: true, flatten: true, &sanitizer) : nil
  end

  # :nodoc:
  def apply_aliases(names)
    names << [:msie, :msie_compatibility] if (names & [:ie, :msie]).present?
    names << [:chromium] if names.include?(:chrome)
    names << [:ipad, :android, :kindle] if names.include?(:tablet)
    names.flatten.compact.uniq
  end

  # :nodoc:
  def query_version(version)
    version.ensure_string.strip.parameterize.to_sym == :capable ? check_capable_browser : check_version(version)
  end

  # :nodoc:
  def check_version(version)
    parser = StringScanner.new(version)
    rv = true

    until parser.eos?
      token = parser.scan_until(/(?=&&)|\Z/)
      parser.skip_until(/&&|\Z/)
      operator, version = parse_version_token(token)

      rv &&= @version.send(operator, Versionomy.parse(version))
      break unless rv
    end

    rv
  end

  # :nodoc:
  def check_capable_browser
    check_capable_browser_engines || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_engines
    chrome? || safari? || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_recents
    (firefox? && @version >= "28") || (msie? && @version >= "10") || (opera? && @version >= "15")
  end

  # :nodoc:
  def parse_version_token(token)
    mo = VERSION_TOKEN.match(token)
    raise ArgumentError, "Invalid version check: #{token}." unless mo
    [mo["operator"], mo["version"]].map(&:strip)
  end
end

#agentString

Returns The raw User-Agent HTTP header.

Returns:

  • (String)

    The raw User-Agent HTTP header.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/brauser/browser.rb', line 28

class Browser
  attr_accessor :agent, :accept_language
  attr_reader :name, :human_name, :version, :platform, :human_platform, :languages, :human_languages
  alias_attribute :ua, :agent

  # Creates a new browser.
  #
  # @param agent [String] The User-Agent HTTP header.
  # @param accept_language [String] The Accept-Language HTTP header.
  def initialize(agent = "", accept_language = "")
    @agent = agent
    @accept_language = accept_language
    parse
  end

  # Returns an array of information about the browser. Information are strings which are suitable to use as CSS classes.
  #
  # For version, it will be included a class for every token of the version. For example, version `7.0.1.2` will return this:
  #
  # ```ruby
  # ["version-7", "version-7_0", "version-7_0_1", "version-7_0_1_2"]
  # ```
  #
  # If you provide a block (with accepts name, version and platform as arguments), it will be used for translating the name.
  #
  # @param join [String|NilClass] If non falsy, the separator to use to join information. If falsy, informations will be returned as array.
  # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
  # @param version [String|NilClass] If non falsy, the string to prepend to the version. If falsy, the version information will not be included.
  # @param platform [String|NilClass] If non falsy, the string to prepend to the platform. If falsy, the platform information will not be included.
  # @return [String|Array] CSS ready information of the current browser.
  def classes(join = " ", name: true, version: true, platform: true)
    rv = [name_to_str(name), version_to_str(version), platform_to_str(platform)].compact.uniq.flatten
    join ? rv.join(join) : rv
  end
  alias_method :meta, :classes
  alias_method :to_s, :classes

  # Checks if the browser accepts a specific language or languages.
  #
  # @param languages [Array] The list of languages.
  # @return [Boolean] `true` if at least one of requested languages is accepted, `false` otherwise.
  def accepts?(*languages)
    languages = normalize_query_arguments(languages)
    (@languages.keys & languages).present?
  end

  # Checks if the browser matches a specific query.
  #
  # @param name [Symbol|Array|NilClass] The list of names to check. Also, this meta-name is supported: `:tablet`.
  # @param engine [Symbol|Array|NilClass] Alias for `name`, **which has precedence over this.**
  # @param version [String|NilClass] The query to match the version.
  #   It must be a query in the form is `OPERATOR VALUE && ..`, where `OPERATOR` is one of `["<", "<=", "=", "==", ">=", ">"]`.
  #   You can also pass the value "capable", which will return true for Webkit browsers, IE 10 or above, Firefox 28 and above and Opera 15 and above.
  # @param platform [Symbol|Array|NilClass] The list of platforms to check.
  # @param languages [Symbol|Array|NilClass] The list of languages to check.
  # @return [Boolean] `true` if browser match the query, `false` otherwise.
  def is?(name: nil, engine: nil, version: nil, platform: nil, languages: nil)
    name ||= engine
    rv = name ? (@name == apply_aliases(normalize_query_arguments(name))) : true
    rv &&= query_version(version) if version
    rv &&= @platform == normalize_query_arguments(platform) if platform
    rv &&= accepts?(normalize_query_arguments(languages)) if languages
    rv
  end

  # Check if the browser is supported.
  #
  # @param browsers [Hash|String] A map of name and minimum supported major version, or a path to YAML file containing the map.
  # @return [Boolean] `true` if current browser is supported, `false` otherwise. If the name is not found in the map, `false` is returned.
  def supported?(browsers = {})
    browsers = YAML.load_file(browsers.to_s).symbolize_keys unless browsers.is_a?(Hash)
    minimum_version = browsers.with_indifferent_access[name.value]
    minimum_version ? is?(version: ">= #{minimum_version}") : false
  end

  # Check if the browser is a specific one
  #
  # @param method The browser engine to check.
  # @param args [Array] **unused.**
  # @param block [Proc] **unused.**
  # @return [Boolean] `true` if browser match the engine, `false` otherwise.
  def method_missing(method, *args, &block)
    method.to_s =~ /(.+)\?$/ ? is?(name: Regexp.last_match[1]) : super(method, *args, &block)
  end

  private

  # :nodoc:
  VERSION_TOKEN = /(?<operator>>=|<=|<|>|!=|(={1,2}))\s*(?<version>.+)/

  # :nodoc:
  def parse
    parser = Brauser::Parser.new
    parse_agent(parser)
    parse_languages(parser)
  end

  # :nodoc:
  def parse_agent(parser)
    agent = parser.parse_agent(@agent)

    if agent
      @name, @version, @platform = *agent
      @human_name = Brauser::Definitions.browsers[@name].try(:name) || "Unknown Browser"
      @human_platform = Brauser::Definitions.platforms[@platform].try(:name) || "Unknown Platform"
    else
      sanitize_agent
    end
  end

  # :nodoc:
  def sanitize_agent
    @name = @platform = Brauser::Value.new(:unknown)
    @human_name = @human_platform = Brauser::Value.new("Unknown")
    @version = Brauser::Value.new(Versionomy.parse("0.0"))
  end

  # :nodoc:
  def parse_languages(parser)
    languages = parser.parse_accept_language(@accept_language)
    @languages = languages
    @human_languages = languages.reduce({}) do |rv, (code, priority)|
      rv[Brauser::Definitions.languages[code].name] = priority
      rv
    end
  end

  # :nodoc:
  def name_to_str(name)
    if name
      name = "" if name.is_a?(TrueClass)
      rv = @name
      rv = [:msie_compatibility, :msie] if rv == :msie_compatibility
      rv.ensure_array(no_duplicates: true) { |n| "#{name}#{n}" }
    end
  end

  # :nodoc:
  def version_to_str(version)
    if version
      version = "version-" if version.is_a?(TrueClass)
      version_str = @version.values_array.reduce([]) do |rv, current|
        rv << [rv.last, current].compact.join("_")
        rv
      end

      version_str.map { |v| "#{version}#{v}" }
    end
  end

  # :nodoc:
  def platform_to_str(platform)
    return nil unless platform
    platform = "platform-" if platform.is_a?(TrueClass)
    "#{platform}#{@platform}"
  end

  # :nodoc:
  def normalize_query_arguments(arguments)
    sanitizer = ->(a) { a.ensure_string.downcase.gsub("_", "-").to_sym }
    arguments ? arguments.ensure_array(no_duplicates: true, compact: true, flatten: true, &sanitizer) : nil
  end

  # :nodoc:
  def apply_aliases(names)
    names << [:msie, :msie_compatibility] if (names & [:ie, :msie]).present?
    names << [:chromium] if names.include?(:chrome)
    names << [:ipad, :android, :kindle] if names.include?(:tablet)
    names.flatten.compact.uniq
  end

  # :nodoc:
  def query_version(version)
    version.ensure_string.strip.parameterize.to_sym == :capable ? check_capable_browser : check_version(version)
  end

  # :nodoc:
  def check_version(version)
    parser = StringScanner.new(version)
    rv = true

    until parser.eos?
      token = parser.scan_until(/(?=&&)|\Z/)
      parser.skip_until(/&&|\Z/)
      operator, version = parse_version_token(token)

      rv &&= @version.send(operator, Versionomy.parse(version))
      break unless rv
    end

    rv
  end

  # :nodoc:
  def check_capable_browser
    check_capable_browser_engines || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_engines
    chrome? || safari? || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_recents
    (firefox? && @version >= "28") || (msie? && @version >= "10") || (opera? && @version >= "15")
  end

  # :nodoc:
  def parse_version_token(token)
    mo = VERSION_TOKEN.match(token)
    raise ArgumentError, "Invalid version check: #{token}." unless mo
    [mo["operator"], mo["version"]].map(&:strip)
  end
end

#human_languagesHash (readonly)

Returns The human-readable list of accepted languages.

Returns:

  • (Hash)

    The human-readable list of accepted languages.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/brauser/browser.rb', line 28

class Browser
  attr_accessor :agent, :accept_language
  attr_reader :name, :human_name, :version, :platform, :human_platform, :languages, :human_languages
  alias_attribute :ua, :agent

  # Creates a new browser.
  #
  # @param agent [String] The User-Agent HTTP header.
  # @param accept_language [String] The Accept-Language HTTP header.
  def initialize(agent = "", accept_language = "")
    @agent = agent
    @accept_language = accept_language
    parse
  end

  # Returns an array of information about the browser. Information are strings which are suitable to use as CSS classes.
  #
  # For version, it will be included a class for every token of the version. For example, version `7.0.1.2` will return this:
  #
  # ```ruby
  # ["version-7", "version-7_0", "version-7_0_1", "version-7_0_1_2"]
  # ```
  #
  # If you provide a block (with accepts name, version and platform as arguments), it will be used for translating the name.
  #
  # @param join [String|NilClass] If non falsy, the separator to use to join information. If falsy, informations will be returned as array.
  # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
  # @param version [String|NilClass] If non falsy, the string to prepend to the version. If falsy, the version information will not be included.
  # @param platform [String|NilClass] If non falsy, the string to prepend to the platform. If falsy, the platform information will not be included.
  # @return [String|Array] CSS ready information of the current browser.
  def classes(join = " ", name: true, version: true, platform: true)
    rv = [name_to_str(name), version_to_str(version), platform_to_str(platform)].compact.uniq.flatten
    join ? rv.join(join) : rv
  end
  alias_method :meta, :classes
  alias_method :to_s, :classes

  # Checks if the browser accepts a specific language or languages.
  #
  # @param languages [Array] The list of languages.
  # @return [Boolean] `true` if at least one of requested languages is accepted, `false` otherwise.
  def accepts?(*languages)
    languages = normalize_query_arguments(languages)
    (@languages.keys & languages).present?
  end

  # Checks if the browser matches a specific query.
  #
  # @param name [Symbol|Array|NilClass] The list of names to check. Also, this meta-name is supported: `:tablet`.
  # @param engine [Symbol|Array|NilClass] Alias for `name`, **which has precedence over this.**
  # @param version [String|NilClass] The query to match the version.
  #   It must be a query in the form is `OPERATOR VALUE && ..`, where `OPERATOR` is one of `["<", "<=", "=", "==", ">=", ">"]`.
  #   You can also pass the value "capable", which will return true for Webkit browsers, IE 10 or above, Firefox 28 and above and Opera 15 and above.
  # @param platform [Symbol|Array|NilClass] The list of platforms to check.
  # @param languages [Symbol|Array|NilClass] The list of languages to check.
  # @return [Boolean] `true` if browser match the query, `false` otherwise.
  def is?(name: nil, engine: nil, version: nil, platform: nil, languages: nil)
    name ||= engine
    rv = name ? (@name == apply_aliases(normalize_query_arguments(name))) : true
    rv &&= query_version(version) if version
    rv &&= @platform == normalize_query_arguments(platform) if platform
    rv &&= accepts?(normalize_query_arguments(languages)) if languages
    rv
  end

  # Check if the browser is supported.
  #
  # @param browsers [Hash|String] A map of name and minimum supported major version, or a path to YAML file containing the map.
  # @return [Boolean] `true` if current browser is supported, `false` otherwise. If the name is not found in the map, `false` is returned.
  def supported?(browsers = {})
    browsers = YAML.load_file(browsers.to_s).symbolize_keys unless browsers.is_a?(Hash)
    minimum_version = browsers.with_indifferent_access[name.value]
    minimum_version ? is?(version: ">= #{minimum_version}") : false
  end

  # Check if the browser is a specific one
  #
  # @param method The browser engine to check.
  # @param args [Array] **unused.**
  # @param block [Proc] **unused.**
  # @return [Boolean] `true` if browser match the engine, `false` otherwise.
  def method_missing(method, *args, &block)
    method.to_s =~ /(.+)\?$/ ? is?(name: Regexp.last_match[1]) : super(method, *args, &block)
  end

  private

  # :nodoc:
  VERSION_TOKEN = /(?<operator>>=|<=|<|>|!=|(={1,2}))\s*(?<version>.+)/

  # :nodoc:
  def parse
    parser = Brauser::Parser.new
    parse_agent(parser)
    parse_languages(parser)
  end

  # :nodoc:
  def parse_agent(parser)
    agent = parser.parse_agent(@agent)

    if agent
      @name, @version, @platform = *agent
      @human_name = Brauser::Definitions.browsers[@name].try(:name) || "Unknown Browser"
      @human_platform = Brauser::Definitions.platforms[@platform].try(:name) || "Unknown Platform"
    else
      sanitize_agent
    end
  end

  # :nodoc:
  def sanitize_agent
    @name = @platform = Brauser::Value.new(:unknown)
    @human_name = @human_platform = Brauser::Value.new("Unknown")
    @version = Brauser::Value.new(Versionomy.parse("0.0"))
  end

  # :nodoc:
  def parse_languages(parser)
    languages = parser.parse_accept_language(@accept_language)
    @languages = languages
    @human_languages = languages.reduce({}) do |rv, (code, priority)|
      rv[Brauser::Definitions.languages[code].name] = priority
      rv
    end
  end

  # :nodoc:
  def name_to_str(name)
    if name
      name = "" if name.is_a?(TrueClass)
      rv = @name
      rv = [:msie_compatibility, :msie] if rv == :msie_compatibility
      rv.ensure_array(no_duplicates: true) { |n| "#{name}#{n}" }
    end
  end

  # :nodoc:
  def version_to_str(version)
    if version
      version = "version-" if version.is_a?(TrueClass)
      version_str = @version.values_array.reduce([]) do |rv, current|
        rv << [rv.last, current].compact.join("_")
        rv
      end

      version_str.map { |v| "#{version}#{v}" }
    end
  end

  # :nodoc:
  def platform_to_str(platform)
    return nil unless platform
    platform = "platform-" if platform.is_a?(TrueClass)
    "#{platform}#{@platform}"
  end

  # :nodoc:
  def normalize_query_arguments(arguments)
    sanitizer = ->(a) { a.ensure_string.downcase.gsub("_", "-").to_sym }
    arguments ? arguments.ensure_array(no_duplicates: true, compact: true, flatten: true, &sanitizer) : nil
  end

  # :nodoc:
  def apply_aliases(names)
    names << [:msie, :msie_compatibility] if (names & [:ie, :msie]).present?
    names << [:chromium] if names.include?(:chrome)
    names << [:ipad, :android, :kindle] if names.include?(:tablet)
    names.flatten.compact.uniq
  end

  # :nodoc:
  def query_version(version)
    version.ensure_string.strip.parameterize.to_sym == :capable ? check_capable_browser : check_version(version)
  end

  # :nodoc:
  def check_version(version)
    parser = StringScanner.new(version)
    rv = true

    until parser.eos?
      token = parser.scan_until(/(?=&&)|\Z/)
      parser.skip_until(/&&|\Z/)
      operator, version = parse_version_token(token)

      rv &&= @version.send(operator, Versionomy.parse(version))
      break unless rv
    end

    rv
  end

  # :nodoc:
  def check_capable_browser
    check_capable_browser_engines || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_engines
    chrome? || safari? || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_recents
    (firefox? && @version >= "28") || (msie? && @version >= "10") || (opera? && @version >= "15")
  end

  # :nodoc:
  def parse_version_token(token)
    mo = VERSION_TOKEN.match(token)
    raise ArgumentError, "Invalid version check: #{token}." unless mo
    [mo["operator"], mo["version"]].map(&:strip)
  end
end

#human_nameString (readonly)

Returns The human-readable current browser name.

Returns:

  • (String)

    The human-readable current browser name.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/brauser/browser.rb', line 28

class Browser
  attr_accessor :agent, :accept_language
  attr_reader :name, :human_name, :version, :platform, :human_platform, :languages, :human_languages
  alias_attribute :ua, :agent

  # Creates a new browser.
  #
  # @param agent [String] The User-Agent HTTP header.
  # @param accept_language [String] The Accept-Language HTTP header.
  def initialize(agent = "", accept_language = "")
    @agent = agent
    @accept_language = accept_language
    parse
  end

  # Returns an array of information about the browser. Information are strings which are suitable to use as CSS classes.
  #
  # For version, it will be included a class for every token of the version. For example, version `7.0.1.2` will return this:
  #
  # ```ruby
  # ["version-7", "version-7_0", "version-7_0_1", "version-7_0_1_2"]
  # ```
  #
  # If you provide a block (with accepts name, version and platform as arguments), it will be used for translating the name.
  #
  # @param join [String|NilClass] If non falsy, the separator to use to join information. If falsy, informations will be returned as array.
  # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
  # @param version [String|NilClass] If non falsy, the string to prepend to the version. If falsy, the version information will not be included.
  # @param platform [String|NilClass] If non falsy, the string to prepend to the platform. If falsy, the platform information will not be included.
  # @return [String|Array] CSS ready information of the current browser.
  def classes(join = " ", name: true, version: true, platform: true)
    rv = [name_to_str(name), version_to_str(version), platform_to_str(platform)].compact.uniq.flatten
    join ? rv.join(join) : rv
  end
  alias_method :meta, :classes
  alias_method :to_s, :classes

  # Checks if the browser accepts a specific language or languages.
  #
  # @param languages [Array] The list of languages.
  # @return [Boolean] `true` if at least one of requested languages is accepted, `false` otherwise.
  def accepts?(*languages)
    languages = normalize_query_arguments(languages)
    (@languages.keys & languages).present?
  end

  # Checks if the browser matches a specific query.
  #
  # @param name [Symbol|Array|NilClass] The list of names to check. Also, this meta-name is supported: `:tablet`.
  # @param engine [Symbol|Array|NilClass] Alias for `name`, **which has precedence over this.**
  # @param version [String|NilClass] The query to match the version.
  #   It must be a query in the form is `OPERATOR VALUE && ..`, where `OPERATOR` is one of `["<", "<=", "=", "==", ">=", ">"]`.
  #   You can also pass the value "capable", which will return true for Webkit browsers, IE 10 or above, Firefox 28 and above and Opera 15 and above.
  # @param platform [Symbol|Array|NilClass] The list of platforms to check.
  # @param languages [Symbol|Array|NilClass] The list of languages to check.
  # @return [Boolean] `true` if browser match the query, `false` otherwise.
  def is?(name: nil, engine: nil, version: nil, platform: nil, languages: nil)
    name ||= engine
    rv = name ? (@name == apply_aliases(normalize_query_arguments(name))) : true
    rv &&= query_version(version) if version
    rv &&= @platform == normalize_query_arguments(platform) if platform
    rv &&= accepts?(normalize_query_arguments(languages)) if languages
    rv
  end

  # Check if the browser is supported.
  #
  # @param browsers [Hash|String] A map of name and minimum supported major version, or a path to YAML file containing the map.
  # @return [Boolean] `true` if current browser is supported, `false` otherwise. If the name is not found in the map, `false` is returned.
  def supported?(browsers = {})
    browsers = YAML.load_file(browsers.to_s).symbolize_keys unless browsers.is_a?(Hash)
    minimum_version = browsers.with_indifferent_access[name.value]
    minimum_version ? is?(version: ">= #{minimum_version}") : false
  end

  # Check if the browser is a specific one
  #
  # @param method The browser engine to check.
  # @param args [Array] **unused.**
  # @param block [Proc] **unused.**
  # @return [Boolean] `true` if browser match the engine, `false` otherwise.
  def method_missing(method, *args, &block)
    method.to_s =~ /(.+)\?$/ ? is?(name: Regexp.last_match[1]) : super(method, *args, &block)
  end

  private

  # :nodoc:
  VERSION_TOKEN = /(?<operator>>=|<=|<|>|!=|(={1,2}))\s*(?<version>.+)/

  # :nodoc:
  def parse
    parser = Brauser::Parser.new
    parse_agent(parser)
    parse_languages(parser)
  end

  # :nodoc:
  def parse_agent(parser)
    agent = parser.parse_agent(@agent)

    if agent
      @name, @version, @platform = *agent
      @human_name = Brauser::Definitions.browsers[@name].try(:name) || "Unknown Browser"
      @human_platform = Brauser::Definitions.platforms[@platform].try(:name) || "Unknown Platform"
    else
      sanitize_agent
    end
  end

  # :nodoc:
  def sanitize_agent
    @name = @platform = Brauser::Value.new(:unknown)
    @human_name = @human_platform = Brauser::Value.new("Unknown")
    @version = Brauser::Value.new(Versionomy.parse("0.0"))
  end

  # :nodoc:
  def parse_languages(parser)
    languages = parser.parse_accept_language(@accept_language)
    @languages = languages
    @human_languages = languages.reduce({}) do |rv, (code, priority)|
      rv[Brauser::Definitions.languages[code].name] = priority
      rv
    end
  end

  # :nodoc:
  def name_to_str(name)
    if name
      name = "" if name.is_a?(TrueClass)
      rv = @name
      rv = [:msie_compatibility, :msie] if rv == :msie_compatibility
      rv.ensure_array(no_duplicates: true) { |n| "#{name}#{n}" }
    end
  end

  # :nodoc:
  def version_to_str(version)
    if version
      version = "version-" if version.is_a?(TrueClass)
      version_str = @version.values_array.reduce([]) do |rv, current|
        rv << [rv.last, current].compact.join("_")
        rv
      end

      version_str.map { |v| "#{version}#{v}" }
    end
  end

  # :nodoc:
  def platform_to_str(platform)
    return nil unless platform
    platform = "platform-" if platform.is_a?(TrueClass)
    "#{platform}#{@platform}"
  end

  # :nodoc:
  def normalize_query_arguments(arguments)
    sanitizer = ->(a) { a.ensure_string.downcase.gsub("_", "-").to_sym }
    arguments ? arguments.ensure_array(no_duplicates: true, compact: true, flatten: true, &sanitizer) : nil
  end

  # :nodoc:
  def apply_aliases(names)
    names << [:msie, :msie_compatibility] if (names & [:ie, :msie]).present?
    names << [:chromium] if names.include?(:chrome)
    names << [:ipad, :android, :kindle] if names.include?(:tablet)
    names.flatten.compact.uniq
  end

  # :nodoc:
  def query_version(version)
    version.ensure_string.strip.parameterize.to_sym == :capable ? check_capable_browser : check_version(version)
  end

  # :nodoc:
  def check_version(version)
    parser = StringScanner.new(version)
    rv = true

    until parser.eos?
      token = parser.scan_until(/(?=&&)|\Z/)
      parser.skip_until(/&&|\Z/)
      operator, version = parse_version_token(token)

      rv &&= @version.send(operator, Versionomy.parse(version))
      break unless rv
    end

    rv
  end

  # :nodoc:
  def check_capable_browser
    check_capable_browser_engines || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_engines
    chrome? || safari? || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_recents
    (firefox? && @version >= "28") || (msie? && @version >= "10") || (opera? && @version >= "15")
  end

  # :nodoc:
  def parse_version_token(token)
    mo = VERSION_TOKEN.match(token)
    raise ArgumentError, "Invalid version check: #{token}." unless mo
    [mo["operator"], mo["version"]].map(&:strip)
  end
end

#human_platformString (readonly)

Returns The human-readable current browser platform.

Returns:

  • (String)

    The human-readable current browser platform.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/brauser/browser.rb', line 28

class Browser
  attr_accessor :agent, :accept_language
  attr_reader :name, :human_name, :version, :platform, :human_platform, :languages, :human_languages
  alias_attribute :ua, :agent

  # Creates a new browser.
  #
  # @param agent [String] The User-Agent HTTP header.
  # @param accept_language [String] The Accept-Language HTTP header.
  def initialize(agent = "", accept_language = "")
    @agent = agent
    @accept_language = accept_language
    parse
  end

  # Returns an array of information about the browser. Information are strings which are suitable to use as CSS classes.
  #
  # For version, it will be included a class for every token of the version. For example, version `7.0.1.2` will return this:
  #
  # ```ruby
  # ["version-7", "version-7_0", "version-7_0_1", "version-7_0_1_2"]
  # ```
  #
  # If you provide a block (with accepts name, version and platform as arguments), it will be used for translating the name.
  #
  # @param join [String|NilClass] If non falsy, the separator to use to join information. If falsy, informations will be returned as array.
  # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
  # @param version [String|NilClass] If non falsy, the string to prepend to the version. If falsy, the version information will not be included.
  # @param platform [String|NilClass] If non falsy, the string to prepend to the platform. If falsy, the platform information will not be included.
  # @return [String|Array] CSS ready information of the current browser.
  def classes(join = " ", name: true, version: true, platform: true)
    rv = [name_to_str(name), version_to_str(version), platform_to_str(platform)].compact.uniq.flatten
    join ? rv.join(join) : rv
  end
  alias_method :meta, :classes
  alias_method :to_s, :classes

  # Checks if the browser accepts a specific language or languages.
  #
  # @param languages [Array] The list of languages.
  # @return [Boolean] `true` if at least one of requested languages is accepted, `false` otherwise.
  def accepts?(*languages)
    languages = normalize_query_arguments(languages)
    (@languages.keys & languages).present?
  end

  # Checks if the browser matches a specific query.
  #
  # @param name [Symbol|Array|NilClass] The list of names to check. Also, this meta-name is supported: `:tablet`.
  # @param engine [Symbol|Array|NilClass] Alias for `name`, **which has precedence over this.**
  # @param version [String|NilClass] The query to match the version.
  #   It must be a query in the form is `OPERATOR VALUE && ..`, where `OPERATOR` is one of `["<", "<=", "=", "==", ">=", ">"]`.
  #   You can also pass the value "capable", which will return true for Webkit browsers, IE 10 or above, Firefox 28 and above and Opera 15 and above.
  # @param platform [Symbol|Array|NilClass] The list of platforms to check.
  # @param languages [Symbol|Array|NilClass] The list of languages to check.
  # @return [Boolean] `true` if browser match the query, `false` otherwise.
  def is?(name: nil, engine: nil, version: nil, platform: nil, languages: nil)
    name ||= engine
    rv = name ? (@name == apply_aliases(normalize_query_arguments(name))) : true
    rv &&= query_version(version) if version
    rv &&= @platform == normalize_query_arguments(platform) if platform
    rv &&= accepts?(normalize_query_arguments(languages)) if languages
    rv
  end

  # Check if the browser is supported.
  #
  # @param browsers [Hash|String] A map of name and minimum supported major version, or a path to YAML file containing the map.
  # @return [Boolean] `true` if current browser is supported, `false` otherwise. If the name is not found in the map, `false` is returned.
  def supported?(browsers = {})
    browsers = YAML.load_file(browsers.to_s).symbolize_keys unless browsers.is_a?(Hash)
    minimum_version = browsers.with_indifferent_access[name.value]
    minimum_version ? is?(version: ">= #{minimum_version}") : false
  end

  # Check if the browser is a specific one
  #
  # @param method The browser engine to check.
  # @param args [Array] **unused.**
  # @param block [Proc] **unused.**
  # @return [Boolean] `true` if browser match the engine, `false` otherwise.
  def method_missing(method, *args, &block)
    method.to_s =~ /(.+)\?$/ ? is?(name: Regexp.last_match[1]) : super(method, *args, &block)
  end

  private

  # :nodoc:
  VERSION_TOKEN = /(?<operator>>=|<=|<|>|!=|(={1,2}))\s*(?<version>.+)/

  # :nodoc:
  def parse
    parser = Brauser::Parser.new
    parse_agent(parser)
    parse_languages(parser)
  end

  # :nodoc:
  def parse_agent(parser)
    agent = parser.parse_agent(@agent)

    if agent
      @name, @version, @platform = *agent
      @human_name = Brauser::Definitions.browsers[@name].try(:name) || "Unknown Browser"
      @human_platform = Brauser::Definitions.platforms[@platform].try(:name) || "Unknown Platform"
    else
      sanitize_agent
    end
  end

  # :nodoc:
  def sanitize_agent
    @name = @platform = Brauser::Value.new(:unknown)
    @human_name = @human_platform = Brauser::Value.new("Unknown")
    @version = Brauser::Value.new(Versionomy.parse("0.0"))
  end

  # :nodoc:
  def parse_languages(parser)
    languages = parser.parse_accept_language(@accept_language)
    @languages = languages
    @human_languages = languages.reduce({}) do |rv, (code, priority)|
      rv[Brauser::Definitions.languages[code].name] = priority
      rv
    end
  end

  # :nodoc:
  def name_to_str(name)
    if name
      name = "" if name.is_a?(TrueClass)
      rv = @name
      rv = [:msie_compatibility, :msie] if rv == :msie_compatibility
      rv.ensure_array(no_duplicates: true) { |n| "#{name}#{n}" }
    end
  end

  # :nodoc:
  def version_to_str(version)
    if version
      version = "version-" if version.is_a?(TrueClass)
      version_str = @version.values_array.reduce([]) do |rv, current|
        rv << [rv.last, current].compact.join("_")
        rv
      end

      version_str.map { |v| "#{version}#{v}" }
    end
  end

  # :nodoc:
  def platform_to_str(platform)
    return nil unless platform
    platform = "platform-" if platform.is_a?(TrueClass)
    "#{platform}#{@platform}"
  end

  # :nodoc:
  def normalize_query_arguments(arguments)
    sanitizer = ->(a) { a.ensure_string.downcase.gsub("_", "-").to_sym }
    arguments ? arguments.ensure_array(no_duplicates: true, compact: true, flatten: true, &sanitizer) : nil
  end

  # :nodoc:
  def apply_aliases(names)
    names << [:msie, :msie_compatibility] if (names & [:ie, :msie]).present?
    names << [:chromium] if names.include?(:chrome)
    names << [:ipad, :android, :kindle] if names.include?(:tablet)
    names.flatten.compact.uniq
  end

  # :nodoc:
  def query_version(version)
    version.ensure_string.strip.parameterize.to_sym == :capable ? check_capable_browser : check_version(version)
  end

  # :nodoc:
  def check_version(version)
    parser = StringScanner.new(version)
    rv = true

    until parser.eos?
      token = parser.scan_until(/(?=&&)|\Z/)
      parser.skip_until(/&&|\Z/)
      operator, version = parse_version_token(token)

      rv &&= @version.send(operator, Versionomy.parse(version))
      break unless rv
    end

    rv
  end

  # :nodoc:
  def check_capable_browser
    check_capable_browser_engines || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_engines
    chrome? || safari? || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_recents
    (firefox? && @version >= "28") || (msie? && @version >= "10") || (opera? && @version >= "15")
  end

  # :nodoc:
  def parse_version_token(token)
    mo = VERSION_TOKEN.match(token)
    raise ArgumentError, "Invalid version check: #{token}." unless mo
    [mo["operator"], mo["version"]].map(&:strip)
  end
end

#languagesHash (readonly)

Returns The accepted languages.

Returns:

  • (Hash)

    The accepted languages.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/brauser/browser.rb', line 28

class Browser
  attr_accessor :agent, :accept_language
  attr_reader :name, :human_name, :version, :platform, :human_platform, :languages, :human_languages
  alias_attribute :ua, :agent

  # Creates a new browser.
  #
  # @param agent [String] The User-Agent HTTP header.
  # @param accept_language [String] The Accept-Language HTTP header.
  def initialize(agent = "", accept_language = "")
    @agent = agent
    @accept_language = accept_language
    parse
  end

  # Returns an array of information about the browser. Information are strings which are suitable to use as CSS classes.
  #
  # For version, it will be included a class for every token of the version. For example, version `7.0.1.2` will return this:
  #
  # ```ruby
  # ["version-7", "version-7_0", "version-7_0_1", "version-7_0_1_2"]
  # ```
  #
  # If you provide a block (with accepts name, version and platform as arguments), it will be used for translating the name.
  #
  # @param join [String|NilClass] If non falsy, the separator to use to join information. If falsy, informations will be returned as array.
  # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
  # @param version [String|NilClass] If non falsy, the string to prepend to the version. If falsy, the version information will not be included.
  # @param platform [String|NilClass] If non falsy, the string to prepend to the platform. If falsy, the platform information will not be included.
  # @return [String|Array] CSS ready information of the current browser.
  def classes(join = " ", name: true, version: true, platform: true)
    rv = [name_to_str(name), version_to_str(version), platform_to_str(platform)].compact.uniq.flatten
    join ? rv.join(join) : rv
  end
  alias_method :meta, :classes
  alias_method :to_s, :classes

  # Checks if the browser accepts a specific language or languages.
  #
  # @param languages [Array] The list of languages.
  # @return [Boolean] `true` if at least one of requested languages is accepted, `false` otherwise.
  def accepts?(*languages)
    languages = normalize_query_arguments(languages)
    (@languages.keys & languages).present?
  end

  # Checks if the browser matches a specific query.
  #
  # @param name [Symbol|Array|NilClass] The list of names to check. Also, this meta-name is supported: `:tablet`.
  # @param engine [Symbol|Array|NilClass] Alias for `name`, **which has precedence over this.**
  # @param version [String|NilClass] The query to match the version.
  #   It must be a query in the form is `OPERATOR VALUE && ..`, where `OPERATOR` is one of `["<", "<=", "=", "==", ">=", ">"]`.
  #   You can also pass the value "capable", which will return true for Webkit browsers, IE 10 or above, Firefox 28 and above and Opera 15 and above.
  # @param platform [Symbol|Array|NilClass] The list of platforms to check.
  # @param languages [Symbol|Array|NilClass] The list of languages to check.
  # @return [Boolean] `true` if browser match the query, `false` otherwise.
  def is?(name: nil, engine: nil, version: nil, platform: nil, languages: nil)
    name ||= engine
    rv = name ? (@name == apply_aliases(normalize_query_arguments(name))) : true
    rv &&= query_version(version) if version
    rv &&= @platform == normalize_query_arguments(platform) if platform
    rv &&= accepts?(normalize_query_arguments(languages)) if languages
    rv
  end

  # Check if the browser is supported.
  #
  # @param browsers [Hash|String] A map of name and minimum supported major version, or a path to YAML file containing the map.
  # @return [Boolean] `true` if current browser is supported, `false` otherwise. If the name is not found in the map, `false` is returned.
  def supported?(browsers = {})
    browsers = YAML.load_file(browsers.to_s).symbolize_keys unless browsers.is_a?(Hash)
    minimum_version = browsers.with_indifferent_access[name.value]
    minimum_version ? is?(version: ">= #{minimum_version}") : false
  end

  # Check if the browser is a specific one
  #
  # @param method The browser engine to check.
  # @param args [Array] **unused.**
  # @param block [Proc] **unused.**
  # @return [Boolean] `true` if browser match the engine, `false` otherwise.
  def method_missing(method, *args, &block)
    method.to_s =~ /(.+)\?$/ ? is?(name: Regexp.last_match[1]) : super(method, *args, &block)
  end

  private

  # :nodoc:
  VERSION_TOKEN = /(?<operator>>=|<=|<|>|!=|(={1,2}))\s*(?<version>.+)/

  # :nodoc:
  def parse
    parser = Brauser::Parser.new
    parse_agent(parser)
    parse_languages(parser)
  end

  # :nodoc:
  def parse_agent(parser)
    agent = parser.parse_agent(@agent)

    if agent
      @name, @version, @platform = *agent
      @human_name = Brauser::Definitions.browsers[@name].try(:name) || "Unknown Browser"
      @human_platform = Brauser::Definitions.platforms[@platform].try(:name) || "Unknown Platform"
    else
      sanitize_agent
    end
  end

  # :nodoc:
  def sanitize_agent
    @name = @platform = Brauser::Value.new(:unknown)
    @human_name = @human_platform = Brauser::Value.new("Unknown")
    @version = Brauser::Value.new(Versionomy.parse("0.0"))
  end

  # :nodoc:
  def parse_languages(parser)
    languages = parser.parse_accept_language(@accept_language)
    @languages = languages
    @human_languages = languages.reduce({}) do |rv, (code, priority)|
      rv[Brauser::Definitions.languages[code].name] = priority
      rv
    end
  end

  # :nodoc:
  def name_to_str(name)
    if name
      name = "" if name.is_a?(TrueClass)
      rv = @name
      rv = [:msie_compatibility, :msie] if rv == :msie_compatibility
      rv.ensure_array(no_duplicates: true) { |n| "#{name}#{n}" }
    end
  end

  # :nodoc:
  def version_to_str(version)
    if version
      version = "version-" if version.is_a?(TrueClass)
      version_str = @version.values_array.reduce([]) do |rv, current|
        rv << [rv.last, current].compact.join("_")
        rv
      end

      version_str.map { |v| "#{version}#{v}" }
    end
  end

  # :nodoc:
  def platform_to_str(platform)
    return nil unless platform
    platform = "platform-" if platform.is_a?(TrueClass)
    "#{platform}#{@platform}"
  end

  # :nodoc:
  def normalize_query_arguments(arguments)
    sanitizer = ->(a) { a.ensure_string.downcase.gsub("_", "-").to_sym }
    arguments ? arguments.ensure_array(no_duplicates: true, compact: true, flatten: true, &sanitizer) : nil
  end

  # :nodoc:
  def apply_aliases(names)
    names << [:msie, :msie_compatibility] if (names & [:ie, :msie]).present?
    names << [:chromium] if names.include?(:chrome)
    names << [:ipad, :android, :kindle] if names.include?(:tablet)
    names.flatten.compact.uniq
  end

  # :nodoc:
  def query_version(version)
    version.ensure_string.strip.parameterize.to_sym == :capable ? check_capable_browser : check_version(version)
  end

  # :nodoc:
  def check_version(version)
    parser = StringScanner.new(version)
    rv = true

    until parser.eos?
      token = parser.scan_until(/(?=&&)|\Z/)
      parser.skip_until(/&&|\Z/)
      operator, version = parse_version_token(token)

      rv &&= @version.send(operator, Versionomy.parse(version))
      break unless rv
    end

    rv
  end

  # :nodoc:
  def check_capable_browser
    check_capable_browser_engines || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_engines
    chrome? || safari? || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_recents
    (firefox? && @version >= "28") || (msie? && @version >= "10") || (opera? && @version >= "15")
  end

  # :nodoc:
  def parse_version_token(token)
    mo = VERSION_TOKEN.match(token)
    raise ArgumentError, "Invalid version check: #{token}." unless mo
    [mo["operator"], mo["version"]].map(&:strip)
  end
end

#nameSymbol (readonly)

Returns The current browser name.

Returns:

  • (Symbol)

    The current browser name.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/brauser/browser.rb', line 28

class Browser
  attr_accessor :agent, :accept_language
  attr_reader :name, :human_name, :version, :platform, :human_platform, :languages, :human_languages
  alias_attribute :ua, :agent

  # Creates a new browser.
  #
  # @param agent [String] The User-Agent HTTP header.
  # @param accept_language [String] The Accept-Language HTTP header.
  def initialize(agent = "", accept_language = "")
    @agent = agent
    @accept_language = accept_language
    parse
  end

  # Returns an array of information about the browser. Information are strings which are suitable to use as CSS classes.
  #
  # For version, it will be included a class for every token of the version. For example, version `7.0.1.2` will return this:
  #
  # ```ruby
  # ["version-7", "version-7_0", "version-7_0_1", "version-7_0_1_2"]
  # ```
  #
  # If you provide a block (with accepts name, version and platform as arguments), it will be used for translating the name.
  #
  # @param join [String|NilClass] If non falsy, the separator to use to join information. If falsy, informations will be returned as array.
  # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
  # @param version [String|NilClass] If non falsy, the string to prepend to the version. If falsy, the version information will not be included.
  # @param platform [String|NilClass] If non falsy, the string to prepend to the platform. If falsy, the platform information will not be included.
  # @return [String|Array] CSS ready information of the current browser.
  def classes(join = " ", name: true, version: true, platform: true)
    rv = [name_to_str(name), version_to_str(version), platform_to_str(platform)].compact.uniq.flatten
    join ? rv.join(join) : rv
  end
  alias_method :meta, :classes
  alias_method :to_s, :classes

  # Checks if the browser accepts a specific language or languages.
  #
  # @param languages [Array] The list of languages.
  # @return [Boolean] `true` if at least one of requested languages is accepted, `false` otherwise.
  def accepts?(*languages)
    languages = normalize_query_arguments(languages)
    (@languages.keys & languages).present?
  end

  # Checks if the browser matches a specific query.
  #
  # @param name [Symbol|Array|NilClass] The list of names to check. Also, this meta-name is supported: `:tablet`.
  # @param engine [Symbol|Array|NilClass] Alias for `name`, **which has precedence over this.**
  # @param version [String|NilClass] The query to match the version.
  #   It must be a query in the form is `OPERATOR VALUE && ..`, where `OPERATOR` is one of `["<", "<=", "=", "==", ">=", ">"]`.
  #   You can also pass the value "capable", which will return true for Webkit browsers, IE 10 or above, Firefox 28 and above and Opera 15 and above.
  # @param platform [Symbol|Array|NilClass] The list of platforms to check.
  # @param languages [Symbol|Array|NilClass] The list of languages to check.
  # @return [Boolean] `true` if browser match the query, `false` otherwise.
  def is?(name: nil, engine: nil, version: nil, platform: nil, languages: nil)
    name ||= engine
    rv = name ? (@name == apply_aliases(normalize_query_arguments(name))) : true
    rv &&= query_version(version) if version
    rv &&= @platform == normalize_query_arguments(platform) if platform
    rv &&= accepts?(normalize_query_arguments(languages)) if languages
    rv
  end

  # Check if the browser is supported.
  #
  # @param browsers [Hash|String] A map of name and minimum supported major version, or a path to YAML file containing the map.
  # @return [Boolean] `true` if current browser is supported, `false` otherwise. If the name is not found in the map, `false` is returned.
  def supported?(browsers = {})
    browsers = YAML.load_file(browsers.to_s).symbolize_keys unless browsers.is_a?(Hash)
    minimum_version = browsers.with_indifferent_access[name.value]
    minimum_version ? is?(version: ">= #{minimum_version}") : false
  end

  # Check if the browser is a specific one
  #
  # @param method The browser engine to check.
  # @param args [Array] **unused.**
  # @param block [Proc] **unused.**
  # @return [Boolean] `true` if browser match the engine, `false` otherwise.
  def method_missing(method, *args, &block)
    method.to_s =~ /(.+)\?$/ ? is?(name: Regexp.last_match[1]) : super(method, *args, &block)
  end

  private

  # :nodoc:
  VERSION_TOKEN = /(?<operator>>=|<=|<|>|!=|(={1,2}))\s*(?<version>.+)/

  # :nodoc:
  def parse
    parser = Brauser::Parser.new
    parse_agent(parser)
    parse_languages(parser)
  end

  # :nodoc:
  def parse_agent(parser)
    agent = parser.parse_agent(@agent)

    if agent
      @name, @version, @platform = *agent
      @human_name = Brauser::Definitions.browsers[@name].try(:name) || "Unknown Browser"
      @human_platform = Brauser::Definitions.platforms[@platform].try(:name) || "Unknown Platform"
    else
      sanitize_agent
    end
  end

  # :nodoc:
  def sanitize_agent
    @name = @platform = Brauser::Value.new(:unknown)
    @human_name = @human_platform = Brauser::Value.new("Unknown")
    @version = Brauser::Value.new(Versionomy.parse("0.0"))
  end

  # :nodoc:
  def parse_languages(parser)
    languages = parser.parse_accept_language(@accept_language)
    @languages = languages
    @human_languages = languages.reduce({}) do |rv, (code, priority)|
      rv[Brauser::Definitions.languages[code].name] = priority
      rv
    end
  end

  # :nodoc:
  def name_to_str(name)
    if name
      name = "" if name.is_a?(TrueClass)
      rv = @name
      rv = [:msie_compatibility, :msie] if rv == :msie_compatibility
      rv.ensure_array(no_duplicates: true) { |n| "#{name}#{n}" }
    end
  end

  # :nodoc:
  def version_to_str(version)
    if version
      version = "version-" if version.is_a?(TrueClass)
      version_str = @version.values_array.reduce([]) do |rv, current|
        rv << [rv.last, current].compact.join("_")
        rv
      end

      version_str.map { |v| "#{version}#{v}" }
    end
  end

  # :nodoc:
  def platform_to_str(platform)
    return nil unless platform
    platform = "platform-" if platform.is_a?(TrueClass)
    "#{platform}#{@platform}"
  end

  # :nodoc:
  def normalize_query_arguments(arguments)
    sanitizer = ->(a) { a.ensure_string.downcase.gsub("_", "-").to_sym }
    arguments ? arguments.ensure_array(no_duplicates: true, compact: true, flatten: true, &sanitizer) : nil
  end

  # :nodoc:
  def apply_aliases(names)
    names << [:msie, :msie_compatibility] if (names & [:ie, :msie]).present?
    names << [:chromium] if names.include?(:chrome)
    names << [:ipad, :android, :kindle] if names.include?(:tablet)
    names.flatten.compact.uniq
  end

  # :nodoc:
  def query_version(version)
    version.ensure_string.strip.parameterize.to_sym == :capable ? check_capable_browser : check_version(version)
  end

  # :nodoc:
  def check_version(version)
    parser = StringScanner.new(version)
    rv = true

    until parser.eos?
      token = parser.scan_until(/(?=&&)|\Z/)
      parser.skip_until(/&&|\Z/)
      operator, version = parse_version_token(token)

      rv &&= @version.send(operator, Versionomy.parse(version))
      break unless rv
    end

    rv
  end

  # :nodoc:
  def check_capable_browser
    check_capable_browser_engines || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_engines
    chrome? || safari? || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_recents
    (firefox? && @version >= "28") || (msie? && @version >= "10") || (opera? && @version >= "15")
  end

  # :nodoc:
  def parse_version_token(token)
    mo = VERSION_TOKEN.match(token)
    raise ArgumentError, "Invalid version check: #{token}." unless mo
    [mo["operator"], mo["version"]].map(&:strip)
  end
end

#platformSymbol (readonly)

Returns The current browser platform.

Returns:

  • (Symbol)

    The current browser platform.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/brauser/browser.rb', line 28

class Browser
  attr_accessor :agent, :accept_language
  attr_reader :name, :human_name, :version, :platform, :human_platform, :languages, :human_languages
  alias_attribute :ua, :agent

  # Creates a new browser.
  #
  # @param agent [String] The User-Agent HTTP header.
  # @param accept_language [String] The Accept-Language HTTP header.
  def initialize(agent = "", accept_language = "")
    @agent = agent
    @accept_language = accept_language
    parse
  end

  # Returns an array of information about the browser. Information are strings which are suitable to use as CSS classes.
  #
  # For version, it will be included a class for every token of the version. For example, version `7.0.1.2` will return this:
  #
  # ```ruby
  # ["version-7", "version-7_0", "version-7_0_1", "version-7_0_1_2"]
  # ```
  #
  # If you provide a block (with accepts name, version and platform as arguments), it will be used for translating the name.
  #
  # @param join [String|NilClass] If non falsy, the separator to use to join information. If falsy, informations will be returned as array.
  # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
  # @param version [String|NilClass] If non falsy, the string to prepend to the version. If falsy, the version information will not be included.
  # @param platform [String|NilClass] If non falsy, the string to prepend to the platform. If falsy, the platform information will not be included.
  # @return [String|Array] CSS ready information of the current browser.
  def classes(join = " ", name: true, version: true, platform: true)
    rv = [name_to_str(name), version_to_str(version), platform_to_str(platform)].compact.uniq.flatten
    join ? rv.join(join) : rv
  end
  alias_method :meta, :classes
  alias_method :to_s, :classes

  # Checks if the browser accepts a specific language or languages.
  #
  # @param languages [Array] The list of languages.
  # @return [Boolean] `true` if at least one of requested languages is accepted, `false` otherwise.
  def accepts?(*languages)
    languages = normalize_query_arguments(languages)
    (@languages.keys & languages).present?
  end

  # Checks if the browser matches a specific query.
  #
  # @param name [Symbol|Array|NilClass] The list of names to check. Also, this meta-name is supported: `:tablet`.
  # @param engine [Symbol|Array|NilClass] Alias for `name`, **which has precedence over this.**
  # @param version [String|NilClass] The query to match the version.
  #   It must be a query in the form is `OPERATOR VALUE && ..`, where `OPERATOR` is one of `["<", "<=", "=", "==", ">=", ">"]`.
  #   You can also pass the value "capable", which will return true for Webkit browsers, IE 10 or above, Firefox 28 and above and Opera 15 and above.
  # @param platform [Symbol|Array|NilClass] The list of platforms to check.
  # @param languages [Symbol|Array|NilClass] The list of languages to check.
  # @return [Boolean] `true` if browser match the query, `false` otherwise.
  def is?(name: nil, engine: nil, version: nil, platform: nil, languages: nil)
    name ||= engine
    rv = name ? (@name == apply_aliases(normalize_query_arguments(name))) : true
    rv &&= query_version(version) if version
    rv &&= @platform == normalize_query_arguments(platform) if platform
    rv &&= accepts?(normalize_query_arguments(languages)) if languages
    rv
  end

  # Check if the browser is supported.
  #
  # @param browsers [Hash|String] A map of name and minimum supported major version, or a path to YAML file containing the map.
  # @return [Boolean] `true` if current browser is supported, `false` otherwise. If the name is not found in the map, `false` is returned.
  def supported?(browsers = {})
    browsers = YAML.load_file(browsers.to_s).symbolize_keys unless browsers.is_a?(Hash)
    minimum_version = browsers.with_indifferent_access[name.value]
    minimum_version ? is?(version: ">= #{minimum_version}") : false
  end

  # Check if the browser is a specific one
  #
  # @param method The browser engine to check.
  # @param args [Array] **unused.**
  # @param block [Proc] **unused.**
  # @return [Boolean] `true` if browser match the engine, `false` otherwise.
  def method_missing(method, *args, &block)
    method.to_s =~ /(.+)\?$/ ? is?(name: Regexp.last_match[1]) : super(method, *args, &block)
  end

  private

  # :nodoc:
  VERSION_TOKEN = /(?<operator>>=|<=|<|>|!=|(={1,2}))\s*(?<version>.+)/

  # :nodoc:
  def parse
    parser = Brauser::Parser.new
    parse_agent(parser)
    parse_languages(parser)
  end

  # :nodoc:
  def parse_agent(parser)
    agent = parser.parse_agent(@agent)

    if agent
      @name, @version, @platform = *agent
      @human_name = Brauser::Definitions.browsers[@name].try(:name) || "Unknown Browser"
      @human_platform = Brauser::Definitions.platforms[@platform].try(:name) || "Unknown Platform"
    else
      sanitize_agent
    end
  end

  # :nodoc:
  def sanitize_agent
    @name = @platform = Brauser::Value.new(:unknown)
    @human_name = @human_platform = Brauser::Value.new("Unknown")
    @version = Brauser::Value.new(Versionomy.parse("0.0"))
  end

  # :nodoc:
  def parse_languages(parser)
    languages = parser.parse_accept_language(@accept_language)
    @languages = languages
    @human_languages = languages.reduce({}) do |rv, (code, priority)|
      rv[Brauser::Definitions.languages[code].name] = priority
      rv
    end
  end

  # :nodoc:
  def name_to_str(name)
    if name
      name = "" if name.is_a?(TrueClass)
      rv = @name
      rv = [:msie_compatibility, :msie] if rv == :msie_compatibility
      rv.ensure_array(no_duplicates: true) { |n| "#{name}#{n}" }
    end
  end

  # :nodoc:
  def version_to_str(version)
    if version
      version = "version-" if version.is_a?(TrueClass)
      version_str = @version.values_array.reduce([]) do |rv, current|
        rv << [rv.last, current].compact.join("_")
        rv
      end

      version_str.map { |v| "#{version}#{v}" }
    end
  end

  # :nodoc:
  def platform_to_str(platform)
    return nil unless platform
    platform = "platform-" if platform.is_a?(TrueClass)
    "#{platform}#{@platform}"
  end

  # :nodoc:
  def normalize_query_arguments(arguments)
    sanitizer = ->(a) { a.ensure_string.downcase.gsub("_", "-").to_sym }
    arguments ? arguments.ensure_array(no_duplicates: true, compact: true, flatten: true, &sanitizer) : nil
  end

  # :nodoc:
  def apply_aliases(names)
    names << [:msie, :msie_compatibility] if (names & [:ie, :msie]).present?
    names << [:chromium] if names.include?(:chrome)
    names << [:ipad, :android, :kindle] if names.include?(:tablet)
    names.flatten.compact.uniq
  end

  # :nodoc:
  def query_version(version)
    version.ensure_string.strip.parameterize.to_sym == :capable ? check_capable_browser : check_version(version)
  end

  # :nodoc:
  def check_version(version)
    parser = StringScanner.new(version)
    rv = true

    until parser.eos?
      token = parser.scan_until(/(?=&&)|\Z/)
      parser.skip_until(/&&|\Z/)
      operator, version = parse_version_token(token)

      rv &&= @version.send(operator, Versionomy.parse(version))
      break unless rv
    end

    rv
  end

  # :nodoc:
  def check_capable_browser
    check_capable_browser_engines || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_engines
    chrome? || safari? || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_recents
    (firefox? && @version >= "28") || (msie? && @version >= "10") || (opera? && @version >= "15")
  end

  # :nodoc:
  def parse_version_token(token)
    mo = VERSION_TOKEN.match(token)
    raise ArgumentError, "Invalid version check: #{token}." unless mo
    [mo["operator"], mo["version"]].map(&:strip)
  end
end

#versionVersionomy::Value (readonly)

Returns The current browser version.

Returns:

  • (Versionomy::Value)

    The current browser version.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/brauser/browser.rb', line 28

class Browser
  attr_accessor :agent, :accept_language
  attr_reader :name, :human_name, :version, :platform, :human_platform, :languages, :human_languages
  alias_attribute :ua, :agent

  # Creates a new browser.
  #
  # @param agent [String] The User-Agent HTTP header.
  # @param accept_language [String] The Accept-Language HTTP header.
  def initialize(agent = "", accept_language = "")
    @agent = agent
    @accept_language = accept_language
    parse
  end

  # Returns an array of information about the browser. Information are strings which are suitable to use as CSS classes.
  #
  # For version, it will be included a class for every token of the version. For example, version `7.0.1.2` will return this:
  #
  # ```ruby
  # ["version-7", "version-7_0", "version-7_0_1", "version-7_0_1_2"]
  # ```
  #
  # If you provide a block (with accepts name, version and platform as arguments), it will be used for translating the name.
  #
  # @param join [String|NilClass] If non falsy, the separator to use to join information. If falsy, informations will be returned as array.
  # @param name [Boolean] If non falsy, the string to prepend to the name. If falsy, the name information will not be included.
  # @param version [String|NilClass] If non falsy, the string to prepend to the version. If falsy, the version information will not be included.
  # @param platform [String|NilClass] If non falsy, the string to prepend to the platform. If falsy, the platform information will not be included.
  # @return [String|Array] CSS ready information of the current browser.
  def classes(join = " ", name: true, version: true, platform: true)
    rv = [name_to_str(name), version_to_str(version), platform_to_str(platform)].compact.uniq.flatten
    join ? rv.join(join) : rv
  end
  alias_method :meta, :classes
  alias_method :to_s, :classes

  # Checks if the browser accepts a specific language or languages.
  #
  # @param languages [Array] The list of languages.
  # @return [Boolean] `true` if at least one of requested languages is accepted, `false` otherwise.
  def accepts?(*languages)
    languages = normalize_query_arguments(languages)
    (@languages.keys & languages).present?
  end

  # Checks if the browser matches a specific query.
  #
  # @param name [Symbol|Array|NilClass] The list of names to check. Also, this meta-name is supported: `:tablet`.
  # @param engine [Symbol|Array|NilClass] Alias for `name`, **which has precedence over this.**
  # @param version [String|NilClass] The query to match the version.
  #   It must be a query in the form is `OPERATOR VALUE && ..`, where `OPERATOR` is one of `["<", "<=", "=", "==", ">=", ">"]`.
  #   You can also pass the value "capable", which will return true for Webkit browsers, IE 10 or above, Firefox 28 and above and Opera 15 and above.
  # @param platform [Symbol|Array|NilClass] The list of platforms to check.
  # @param languages [Symbol|Array|NilClass] The list of languages to check.
  # @return [Boolean] `true` if browser match the query, `false` otherwise.
  def is?(name: nil, engine: nil, version: nil, platform: nil, languages: nil)
    name ||= engine
    rv = name ? (@name == apply_aliases(normalize_query_arguments(name))) : true
    rv &&= query_version(version) if version
    rv &&= @platform == normalize_query_arguments(platform) if platform
    rv &&= accepts?(normalize_query_arguments(languages)) if languages
    rv
  end

  # Check if the browser is supported.
  #
  # @param browsers [Hash|String] A map of name and minimum supported major version, or a path to YAML file containing the map.
  # @return [Boolean] `true` if current browser is supported, `false` otherwise. If the name is not found in the map, `false` is returned.
  def supported?(browsers = {})
    browsers = YAML.load_file(browsers.to_s).symbolize_keys unless browsers.is_a?(Hash)
    minimum_version = browsers.with_indifferent_access[name.value]
    minimum_version ? is?(version: ">= #{minimum_version}") : false
  end

  # Check if the browser is a specific one
  #
  # @param method The browser engine to check.
  # @param args [Array] **unused.**
  # @param block [Proc] **unused.**
  # @return [Boolean] `true` if browser match the engine, `false` otherwise.
  def method_missing(method, *args, &block)
    method.to_s =~ /(.+)\?$/ ? is?(name: Regexp.last_match[1]) : super(method, *args, &block)
  end

  private

  # :nodoc:
  VERSION_TOKEN = /(?<operator>>=|<=|<|>|!=|(={1,2}))\s*(?<version>.+)/

  # :nodoc:
  def parse
    parser = Brauser::Parser.new
    parse_agent(parser)
    parse_languages(parser)
  end

  # :nodoc:
  def parse_agent(parser)
    agent = parser.parse_agent(@agent)

    if agent
      @name, @version, @platform = *agent
      @human_name = Brauser::Definitions.browsers[@name].try(:name) || "Unknown Browser"
      @human_platform = Brauser::Definitions.platforms[@platform].try(:name) || "Unknown Platform"
    else
      sanitize_agent
    end
  end

  # :nodoc:
  def sanitize_agent
    @name = @platform = Brauser::Value.new(:unknown)
    @human_name = @human_platform = Brauser::Value.new("Unknown")
    @version = Brauser::Value.new(Versionomy.parse("0.0"))
  end

  # :nodoc:
  def parse_languages(parser)
    languages = parser.parse_accept_language(@accept_language)
    @languages = languages
    @human_languages = languages.reduce({}) do |rv, (code, priority)|
      rv[Brauser::Definitions.languages[code].name] = priority
      rv
    end
  end

  # :nodoc:
  def name_to_str(name)
    if name
      name = "" if name.is_a?(TrueClass)
      rv = @name
      rv = [:msie_compatibility, :msie] if rv == :msie_compatibility
      rv.ensure_array(no_duplicates: true) { |n| "#{name}#{n}" }
    end
  end

  # :nodoc:
  def version_to_str(version)
    if version
      version = "version-" if version.is_a?(TrueClass)
      version_str = @version.values_array.reduce([]) do |rv, current|
        rv << [rv.last, current].compact.join("_")
        rv
      end

      version_str.map { |v| "#{version}#{v}" }
    end
  end

  # :nodoc:
  def platform_to_str(platform)
    return nil unless platform
    platform = "platform-" if platform.is_a?(TrueClass)
    "#{platform}#{@platform}"
  end

  # :nodoc:
  def normalize_query_arguments(arguments)
    sanitizer = ->(a) { a.ensure_string.downcase.gsub("_", "-").to_sym }
    arguments ? arguments.ensure_array(no_duplicates: true, compact: true, flatten: true, &sanitizer) : nil
  end

  # :nodoc:
  def apply_aliases(names)
    names << [:msie, :msie_compatibility] if (names & [:ie, :msie]).present?
    names << [:chromium] if names.include?(:chrome)
    names << [:ipad, :android, :kindle] if names.include?(:tablet)
    names.flatten.compact.uniq
  end

  # :nodoc:
  def query_version(version)
    version.ensure_string.strip.parameterize.to_sym == :capable ? check_capable_browser : check_version(version)
  end

  # :nodoc:
  def check_version(version)
    parser = StringScanner.new(version)
    rv = true

    until parser.eos?
      token = parser.scan_until(/(?=&&)|\Z/)
      parser.skip_until(/&&|\Z/)
      operator, version = parse_version_token(token)

      rv &&= @version.send(operator, Versionomy.parse(version))
      break unless rv
    end

    rv
  end

  # :nodoc:
  def check_capable_browser
    check_capable_browser_engines || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_engines
    chrome? || safari? || check_capable_browser_recents
  end

  # :nodoc:
  def check_capable_browser_recents
    (firefox? && @version >= "28") || (msie? && @version >= "10") || (opera? && @version >= "15")
  end

  # :nodoc:
  def parse_version_token(token)
    mo = VERSION_TOKEN.match(token)
    raise ArgumentError, "Invalid version check: #{token}." unless mo
    [mo["operator"], mo["version"]].map(&:strip)
  end
end

Instance Method Details

#accepts?(*languages) ⇒ Boolean

Checks if the browser accepts a specific language or languages.

Parameters:

  • languages (Array)

    The list of languages.

Returns:

  • (Boolean)

    true if at least one of requested languages is accepted, false otherwise.



69
70
71
72
# File 'lib/brauser/browser.rb', line 69

def accepts?(*languages)
  languages = normalize_query_arguments(languages)
  (@languages.keys & languages).present?
end

#classes(join = " ", name: true, version: true, platform: true) ⇒ String|Array Also known as: meta, to_s

Returns an array of information about the browser. Information are strings which are suitable to use as CSS classes.

For version, it will be included a class for every token of the version. For example, version 7.0.1.2 will return this:

["version-7", "version-7_0", "version-7_0_1", "version-7_0_1_2"]

If you provide a block (with accepts name, version and platform as arguments), it will be used for translating the name.

Parameters:

  • join (String|NilClass) (defaults to: " ")

    If non falsy, the separator to use to join information. If falsy, informations will be returned as array.

  • name (Boolean) (defaults to: true)

    If non falsy, the string to prepend to the name. If falsy, the name information will not be included.

  • version (String|NilClass) (defaults to: true)

    If non falsy, the string to prepend to the version. If falsy, the version information will not be included.

  • platform (String|NilClass) (defaults to: true)

    If non falsy, the string to prepend to the platform. If falsy, the platform information will not be included.

Returns:

  • (String|Array)

    CSS ready information of the current browser.



58
59
60
61
# File 'lib/brauser/browser.rb', line 58

def classes(join = " ", name: true, version: true, platform: true)
  rv = [name_to_str(name), version_to_str(version), platform_to_str(platform)].compact.uniq.flatten
  join ? rv.join(join) : rv
end

#is?(name: nil, engine: nil, version: nil, platform: nil, languages: nil) ⇒ Boolean

Checks if the browser matches a specific query.

Parameters:

  • name (Symbol|Array|NilClass) (defaults to: nil)

    The list of names to check. Also, this meta-name is supported: :tablet.

  • engine (Symbol|Array|NilClass) (defaults to: nil)

    Alias for name, which has precedence over this.

  • version (String|NilClass) (defaults to: nil)

    The query to match the version. It must be a query in the form is OPERATOR VALUE && .., where OPERATOR is one of ["<", "<=", "=", "==", ">=", ">"]. You can also pass the value "capable", which will return true for Webkit browsers, IE 10 or above, Firefox 28 and above and Opera 15 and above.

  • platform (Symbol|Array|NilClass) (defaults to: nil)

    The list of platforms to check.

  • languages (Symbol|Array|NilClass) (defaults to: nil)

    The list of languages to check.

Returns:

  • (Boolean)

    true if browser match the query, false otherwise.



84
85
86
87
88
89
90
91
# File 'lib/brauser/browser.rb', line 84

def is?(name: nil, engine: nil, version: nil, platform: nil, languages: nil)
  name ||= engine
  rv = name ? (@name == apply_aliases(normalize_query_arguments(name))) : true
  rv &&= query_version(version) if version
  rv &&= @platform == normalize_query_arguments(platform) if platform
  rv &&= accepts?(normalize_query_arguments(languages)) if languages
  rv
end

#supported?(browsers = {}) ⇒ Boolean

Check if the browser is supported.

Parameters:

  • browsers (Hash|String) (defaults to: {})

    A map of name and minimum supported major version, or a path to YAML file containing the map.

Returns:

  • (Boolean)

    true if current browser is supported, false otherwise. If the name is not found in the map, false is returned.



97
98
99
100
101
# File 'lib/brauser/browser.rb', line 97

def supported?(browsers = {})
  browsers = YAML.load_file(browsers.to_s).symbolize_keys unless browsers.is_a?(Hash)
  minimum_version = browsers.with_indifferent_access[name.value]
  minimum_version ? is?(version: ">= #{minimum_version}") : false
end