css_parser 1.5.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6f37de2f47134897299b6ff54a77057b97fa0723
4
- data.tar.gz: 4f0a21a5156634d56a97acd198a98af458d05c4d
2
+ SHA256:
3
+ metadata.gz: ecd734b97658689e087ca19039ea51377c23d9e1c4be6099b084006150255fce
4
+ data.tar.gz: d5a326fac6e3b57fe75fa3bc664ae8511a0e0dcf34f80dd5736ee8cc9709f5ee
5
5
  SHA512:
6
- metadata.gz: e3e9cf6debd7bf20cf7121aa949a7700ecd1bf5704c9324cf7fb078741f8793423acf7a199b32bc3222e4d60b03f23094184a8b3e00b6a5ae32184e0734ddb09
7
- data.tar.gz: ede24d16d33effc05b2b0d55fb61630f224729430256928685210a3ea75aaf1de7c1ac61aa47e58931b496d04a444f8a00490b80677362c0a86177325ec7d98b
6
+ metadata.gz: ca0d59324036c9071c5f957235f10022d3db4841d4e4782e147f30e2cde1a1cd46873ede55db01b260676fc7289c0e7c4ad7e0e6ac7dbd0b38a1de8aa13333a1
7
+ data.tar.gz: da5fb20229ecfac93b0436a0ed6954c109908429628cd635751c58fd9b3d029227da06a60bb80f5f4bd2fac55851ac258e7ff59d09fea59e93157f38d21c0fac
data/lib/css_parser.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'addressable/uri'
2
4
  require 'uri'
3
5
  require 'net/https'
@@ -12,7 +14,6 @@ require 'css_parser/regexps'
12
14
  require 'css_parser/parser'
13
15
 
14
16
  module CssParser
15
-
16
17
  # Merge multiple CSS RuleSets by cascading according to the CSS 2.1 cascading rules
17
18
  # (http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order).
18
19
  #
@@ -55,10 +56,10 @@ module CssParser
55
56
  @folded_declaration_cache = {}
56
57
 
57
58
  # in case called like CssParser.merge([rule_set, rule_set])
58
- rule_sets.flatten! if rule_sets[0].kind_of?(Array)
59
+ rule_sets.flatten! if rule_sets[0].is_a?(Array)
59
60
 
60
- unless rule_sets.all? {|rs| rs.kind_of?(CssParser::RuleSet)}
61
- raise ArgumentError, "all parameters must be CssParser::RuleSets."
61
+ unless rule_sets.all? { |rs| rs.is_a?(CssParser::RuleSet) }
62
+ raise ArgumentError, 'all parameters must be CssParser::RuleSets.'
62
63
  end
63
64
 
64
65
  return rule_sets[0] if rule_sets.length == 1
@@ -70,38 +71,27 @@ module CssParser
70
71
  rule_set.expand_shorthand!
71
72
 
72
73
  specificity = rule_set.specificity
73
- unless specificity
74
- if rule_set.selectors.length == 0
75
- specificity = 0
76
- else
77
- specificity = rule_set.selectors.map { |s| calculate_specificity(s) }.compact.max || 0
78
- end
79
- end
74
+ specificity ||= rule_set.selectors.map { |s| calculate_specificity(s) }.compact.max || 0
80
75
 
81
76
  rule_set.each_declaration do |property, value, is_important|
82
77
  # Add the property to the list to be folded per http://www.w3.org/TR/CSS21/cascade.html#cascading-order
83
- if not properties.has_key?(property)
84
- properties[property] = {:value => value, :specificity => specificity, :is_important => is_important}
78
+ if not properties.key?(property)
79
+ properties[property] = {value: value, specificity: specificity, is_important: is_important}
85
80
  elsif is_important
86
81
  if not properties[property][:is_important] or properties[property][:specificity] <= specificity
87
- properties[property] = {:value => value, :specificity => specificity, :is_important => is_important}
82
+ properties[property] = {value: value, specificity: specificity, is_important: is_important}
88
83
  end
89
84
  elsif properties[property][:specificity] < specificity or properties[property][:specificity] == specificity
90
85
  unless properties[property][:is_important]
91
- properties[property] = {:value => value, :specificity => specificity, :is_important => is_important}
86
+ properties[property] = {value: value, specificity: specificity, is_important: is_important}
92
87
  end
93
88
  end
94
- end
89
+ end
95
90
  end
96
91
 
97
- merged = RuleSet.new(nil, nil)
98
-
99
- properties.each do |property, details|
100
- if details[:is_important]
101
- merged[property.strip] = details[:value].strip.gsub(/\;\Z/, '') + '!important'
102
- else
103
- merged[property.strip] = details[:value].strip
104
- end
92
+ merged = properties.each_with_object(RuleSet.new(nil, nil)) do |(property, details), rule_set|
93
+ value = details[:value].strip
94
+ rule_set[property.strip] = details[:is_important] ? "#{value.gsub(/;\Z/, '')}!important" : value
105
95
  end
106
96
 
107
97
  merged.create_shorthand!
@@ -122,12 +112,12 @@ module CssParser
122
112
  def self.calculate_specificity(selector)
123
113
  a = 0
124
114
  b = selector.scan(/\#/).length
125
- c = selector.scan(NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX).length
126
- d = selector.scan(ELEMENTS_AND_PSEUDO_ELEMENTS_RX).length
115
+ c = selector.scan(NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX_NC).length
116
+ d = selector.scan(ELEMENTS_AND_PSEUDO_ELEMENTS_RX_NC).length
127
117
 
128
- (a.to_s + b.to_s + c.to_s + d.to_s).to_i
118
+ "#{a}#{b}#{c}#{d}".to_i
129
119
  rescue
130
- return 0
120
+ 0
131
121
  end
132
122
 
133
123
  # Make <tt>url()</tt> links absolute.
@@ -144,23 +134,25 @@ module CssParser
144
134
  # "http://example.org/style/basic.css").inspect
145
135
  # => "body { background: url('http://example.org/style/yellow.png?abc=123') };"
146
136
  def self.convert_uris(css, base_uri)
147
- base_uri = Addressable::URI.parse(base_uri) unless base_uri.kind_of?(Addressable::URI)
137
+ base_uri = Addressable::URI.parse(base_uri) unless base_uri.is_a?(Addressable::URI)
148
138
 
149
139
  css.gsub(URI_RX) do
150
- uri = $1.to_s
151
- uri.gsub!(/["']+/, '')
140
+ uri = Regexp.last_match(1).to_s.gsub(/["']+/, '')
152
141
  # Don't process URLs that are already absolute
153
- unless uri =~ /^[a-z]+\:\/\//i
142
+ unless uri.match(%r{^[a-z]+://}i)
154
143
  begin
155
- uri = base_uri + uri
156
- rescue; end
144
+ uri = base_uri.join(uri)
145
+ rescue
146
+ nil
147
+ end
157
148
  end
158
- "url('#{uri.to_s}')"
149
+ "url('#{uri}')"
159
150
  end
160
151
  end
161
152
 
162
153
  def self.sanitize_media_query(raw)
163
- mq = raw.to_s.gsub(/[\s]+/, ' ').strip
154
+ mq = raw.to_s.gsub(/\s+/, ' ')
155
+ mq.strip!
164
156
  mq = 'all' if mq.empty?
165
157
  mq.to_sym
166
158
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CssParser
2
4
  # Exception class used for any errors encountered while downloading remote files.
3
5
  class RemoteFileError < IOError; end
@@ -14,13 +16,13 @@ module CssParser
14
16
  # [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
15
17
  # [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
16
18
  class Parser
17
- USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)"
19
+ USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)"
18
20
 
19
- STRIP_CSS_COMMENTS_RX = /\/\*.*?\*\//m
20
- STRIP_HTML_COMMENTS_RX = /\<\!\-\-|\-\-\>/m
21
+ STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
22
+ STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
21
23
 
22
24
  # Initial parsing
23
- RE_AT_IMPORT_RULE = /\@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s\)]*)["']?\)?([\w\s\,^\]\(\)]*)\)?[;\n]?/
25
+ RE_AT_IMPORT_RULE = /@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s)]*)["']?\)?([\w\s,^\]()]*)\)?[;\n]?/.freeze
24
26
 
25
27
  MAX_REDIRECTS = 3
26
28
 
@@ -34,10 +36,10 @@ module CssParser
34
36
  class << self; attr_reader :folded_declaration_cache; end
35
37
 
36
38
  def initialize(options = {})
37
- @options = {:absolute_paths => false,
38
- :import => true,
39
- :io_exceptions => true,
40
- :capture_offsets => false}.merge(options)
39
+ @options = {absolute_paths: false,
40
+ import: true,
41
+ io_exceptions: true,
42
+ capture_offsets: false}.merge(options)
41
43
 
42
44
  # array of RuleSets
43
45
  @rules = []
@@ -69,21 +71,20 @@ module CssParser
69
71
  # Returns an array of declarations.
70
72
  def find_by_selector(selector, media_types = :all)
71
73
  out = []
72
- each_selector(media_types) do |sel, dec, spec|
74
+ each_selector(media_types) do |sel, dec, _spec|
73
75
  out << dec if sel.strip == selector.strip
74
76
  end
75
77
  out
76
78
  end
77
- alias_method :[], :find_by_selector
79
+ alias [] find_by_selector
78
80
 
79
81
  # Finds the rule sets that match the given selectors
80
82
  def find_rule_sets(selectors, media_types = :all)
81
83
  rule_sets = []
82
84
 
83
85
  selectors.each do |selector|
84
- selector.gsub!(/\s+/, ' ')
85
- selector.strip!
86
- each_rule_set(media_types) do |rule_set, media_type|
86
+ selector = selector.gsub(/\s+/, ' ').strip
87
+ each_rule_set(media_types) do |rule_set, _media_type|
87
88
  if !rule_sets.member?(rule_set) && rule_set.selectors.member?(selector)
88
89
  rule_sets << rule_set
89
90
  end
@@ -114,9 +115,9 @@ module CssParser
114
115
  # parser = CssParser::Parser.new
115
116
  # parser.add_block!(css)
116
117
  def add_block!(block, options = {})
117
- options = {:base_uri => nil, :base_dir => nil, :charset => nil, :media_types => :all, :only_media_types => :all}.merge(options)
118
- options[:media_types] = [options[:media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
119
- options[:only_media_types] = [options[:only_media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
118
+ options = {base_uri: nil, base_dir: nil, charset: nil, media_types: :all, only_media_types: :all}.merge(options)
119
+ options[:media_types] = [options[:media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
120
+ options[:only_media_types] = [options[:only_media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
120
121
 
121
122
  block = cleanup_block(block, options)
122
123
 
@@ -128,19 +129,19 @@ module CssParser
128
129
  if @options[:import]
129
130
  block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
130
131
  media_types = []
131
- if media_string = import_rule[-1]
132
- media_string.split(/[,]/).each do |t|
132
+ if (media_string = import_rule[-1])
133
+ media_string.split(/,/).each do |t|
133
134
  media_types << CssParser.sanitize_media_query(t) unless t.empty?
134
135
  end
135
136
  else
136
137
  media_types = [:all]
137
138
  end
138
139
 
139
- next unless options[:only_media_types].include?(:all) or media_types.length < 1 or (media_types & options[:only_media_types]).length > 0
140
+ next unless options[:only_media_types].include?(:all) or media_types.empty? or !(media_types & options[:only_media_types]).empty?
140
141
 
141
142
  import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
142
143
 
143
- import_options = { :media_types => media_types }
144
+ import_options = {media_types: media_types}
144
145
  import_options[:capture_offsets] = true if options[:capture_offsets]
145
146
 
146
147
  if options[:base_uri]
@@ -182,20 +183,21 @@ module CssParser
182
183
  #
183
184
  # +media_types+ can be a symbol or an array of symbols.
184
185
  def add_rule_set!(ruleset, media_types = :all)
185
- raise ArgumentError unless ruleset.kind_of?(CssParser::RuleSet)
186
+ raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
186
187
 
187
- media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
188
+ media_types = [media_types] unless media_types.is_a?(Array)
189
+ media_types = media_types.flat_map { |mt| CssParser.sanitize_media_query(mt) }
188
190
 
189
- @rules << {:media_types => media_types, :rules => ruleset}
191
+ @rules << {media_types: media_types, rules: ruleset}
190
192
  end
191
193
 
192
194
  # Remove a CssParser RuleSet object.
193
195
  #
194
196
  # +media_types+ can be a symbol or an array of symbols.
195
197
  def remove_rule_set!(ruleset, media_types = :all)
196
- raise ArgumentError unless ruleset.kind_of?(CssParser::RuleSet)
198
+ raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
197
199
 
198
- media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
200
+ media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
199
201
 
200
202
  @rules.reject! do |rule|
201
203
  rule[:media_types] == media_types && rule[:rules].to_s == ruleset.to_s
@@ -207,7 +209,7 @@ module CssParser
207
209
  # +media_types+ can be a symbol or an array of symbols.
208
210
  def each_rule_set(media_types = :all) # :yields: rule_set, media_types
209
211
  media_types = [:all] if media_types.nil?
210
- media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
212
+ media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
211
213
 
212
214
  @rules.each do |block|
213
215
  if media_types.include?(:all) or block[:media_types].any? { |mt| media_types.include?(mt) }
@@ -220,7 +222,7 @@ module CssParser
220
222
  def to_h(which_media = :all)
221
223
  out = {}
222
224
  styles_by_media_types = {}
223
- each_selector(which_media) do |selectors, declarations, specificity, media_types|
225
+ each_selector(which_media) do |selectors, declarations, _specificity, media_types|
224
226
  media_types.each do |media_type|
225
227
  styles_by_media_types[media_type] ||= []
226
228
  styles_by_media_types[media_type] << [selectors, declarations]
@@ -242,6 +244,8 @@ module CssParser
242
244
  # +media_types+ can be a symbol or an array of symbols.
243
245
  # See RuleSet#each_selector for +options+.
244
246
  def each_selector(all_media_types = :all, options = {}) # :yields: selectors, declarations, specificity, media_types
247
+ return to_enum(__method__, all_media_types, options) unless block_given?
248
+
245
249
  each_rule_set(all_media_types) do |rule_set, media_types|
246
250
  rule_set.each_selector(options) do |selectors, declarations, specificity|
247
251
  yield selectors, declarations, specificity, media_types
@@ -251,9 +255,10 @@ module CssParser
251
255
 
252
256
  # Output all CSS rules as a single stylesheet.
253
257
  def to_s(which_media = :all)
254
- out = ''
258
+ out = []
255
259
  styles_by_media_types = {}
256
- each_selector(which_media) do |selectors, declarations, specificity, media_types|
260
+
261
+ each_selector(which_media) do |selectors, declarations, _specificity, media_types|
257
262
  media_types.each do |media_type|
258
263
  styles_by_media_types[media_type] ||= []
259
264
  styles_by_media_types[media_type] << [selectors, declarations]
@@ -262,20 +267,21 @@ module CssParser
262
267
 
263
268
  styles_by_media_types.each_pair do |media_type, media_styles|
264
269
  media_block = (media_type != :all)
265
- out += "@media #{media_type} {\n" if media_block
270
+ out << "@media #{media_type} {" if media_block
266
271
 
267
272
  media_styles.each do |media_style|
268
273
  if media_block
269
- out += " #{media_style[0]} {\n #{media_style[1]}\n }\n"
274
+ out.push(" #{media_style[0]} {\n #{media_style[1]}\n }")
270
275
  else
271
- out += "#{media_style[0]} {\n#{media_style[1]}\n}\n"
276
+ out.push("#{media_style[0]} {\n#{media_style[1]}\n}")
272
277
  end
273
278
  end
274
279
 
275
- out += "}\n" if media_block
280
+ out << '}' if media_block
276
281
  end
277
282
 
278
- out
283
+ out << ''
284
+ out.join("\n")
279
285
  end
280
286
 
281
287
  # A hash of { :media_query => rule_sets }
@@ -283,7 +289,7 @@ module CssParser
283
289
  rules_by_media = {}
284
290
  @rules.each do |block|
285
291
  block[:media_types].each do |mt|
286
- unless rules_by_media.has_key?(mt)
292
+ unless rules_by_media.key?(mt)
287
293
  rules_by_media[mt] = []
288
294
  end
289
295
  rules_by_media[mt] << block[:rules]
@@ -295,15 +301,13 @@ module CssParser
295
301
 
296
302
  # Merge declarations with the same selector.
297
303
  def compact! # :nodoc:
298
- compacted = []
299
-
300
- compacted
304
+ []
301
305
  end
302
306
 
303
307
  def parse_block_into_rule_sets!(block, options = {}) # :nodoc:
304
308
  current_media_queries = [:all]
305
309
  if options[:media_types]
306
- current_media_queries = options[:media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
310
+ current_media_queries = options[:media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
307
311
  end
308
312
 
309
313
  in_declarations = 0
@@ -314,44 +318,43 @@ module CssParser
314
318
  in_at_media_rule = false
315
319
  in_media_block = false
316
320
 
317
- current_selectors = ''
318
- current_media_query = ''
319
- current_declarations = ''
321
+ current_selectors = String.new
322
+ current_media_query = String.new
323
+ current_declarations = String.new
320
324
 
321
325
  # once we are in a rule, we will use this to store where we started if we are capturing offsets
322
326
  rule_start = nil
323
327
  offset = nil
324
328
 
325
- block.scan(/(([\\]{2,})|([\\]?[{}\s"])|(.[^\s"{}\\]*))/) do |matches|
326
- token = matches[0]
327
-
329
+ block.scan(/\s+|\\{2,}|\\?[{}\s"]|.[^\s"{}\\]*/) do |token|
328
330
  # save the regex offset so that we know where in the file we are
329
331
  offset = Regexp.last_match.offset(0) if options[:capture_offsets]
330
332
 
331
- if token =~ /\A"/ # found un-escaped double quote
333
+ if token.start_with?('"') # found un-escaped double quote
332
334
  in_string = !in_string
333
335
  end
334
336
 
335
337
  if in_declarations > 0
336
338
  # too deep, malformed declaration block
337
339
  if in_declarations > 1
338
- in_declarations -= 1 if token =~ /\}/
340
+ in_declarations -= 1 if token.include?('}')
339
341
  next
340
342
  end
341
343
 
342
- if token =~ /\{/ and not in_string
344
+ if !in_string && token.include?('{')
343
345
  in_declarations += 1
344
346
  next
345
347
  end
346
348
 
347
- current_declarations += token
349
+ current_declarations << token
348
350
 
349
- if token =~ /\}/ and not in_string
350
- current_declarations.gsub!(/\}[\s]*$/, '')
351
+ if !in_string && token.include?('}')
352
+ current_declarations.gsub!(/\}\s*$/, '')
351
353
 
352
354
  in_declarations -= 1
355
+ current_declarations.strip!
353
356
 
354
- unless current_declarations.strip.empty?
357
+ unless current_declarations.empty?
355
358
  if options[:capture_offsets]
356
359
  add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
357
360
  else
@@ -359,8 +362,8 @@ module CssParser
359
362
  end
360
363
  end
361
364
 
362
- current_selectors = ''
363
- current_declarations = ''
365
+ current_selectors = String.new
366
+ current_declarations = String.new
364
367
 
365
368
  # restart our search for selectors and declarations
366
369
  rule_start = nil if options[:capture_offsets]
@@ -370,56 +373,54 @@ module CssParser
370
373
  in_at_media_rule = true
371
374
  current_media_queries = []
372
375
  elsif in_at_media_rule
373
- if token =~ /\{/
374
- block_depth = block_depth + 1
376
+ if token.include?('{')
377
+ block_depth += 1
375
378
  in_at_media_rule = false
376
379
  in_media_block = true
377
380
  current_media_queries << CssParser.sanitize_media_query(current_media_query)
378
- current_media_query = ''
379
- elsif token =~ /[,]/
381
+ current_media_query = String.new
382
+ elsif token.include?(',')
380
383
  # new media query begins
381
- token.gsub!(/[,]/, ' ')
382
- current_media_query += token.strip + ' '
384
+ token.tr!(',', ' ')
385
+ token.strip!
386
+ current_media_query << token << ' '
383
387
  current_media_queries << CssParser.sanitize_media_query(current_media_query)
384
- current_media_query = ''
388
+ current_media_query = String.new
385
389
  else
386
- current_media_query += token.strip + ' '
390
+ token.strip!
391
+ current_media_query << token << ' '
387
392
  end
388
393
  elsif in_charset or token =~ /@charset/i
389
394
  # iterate until we are out of the charset declaration
390
- in_charset = (token =~ /;/ ? false : true)
395
+ in_charset = !token.include?(';')
396
+ elsif !in_string && token.include?('}')
397
+ block_depth -= 1
398
+
399
+ # reset the current media query scope
400
+ if in_media_block
401
+ current_media_queries = [:all]
402
+ in_media_block = false
403
+ end
404
+ elsif !in_string && token.include?('{')
405
+ current_selectors.strip!
406
+ in_declarations += 1
391
407
  else
392
- if token =~ /\}/ and not in_string
393
- block_depth = block_depth - 1
408
+ # if we are in a selector, add the token to the current selectors
409
+ current_selectors << token
394
410
 
395
- # reset the current media query scope
396
- if in_media_block
397
- current_media_queries = [:all]
398
- in_media_block = false
399
- end
400
- else
401
- if token =~ /\{/ and not in_string
402
- current_selectors.strip!
403
- in_declarations += 1
404
- else
405
- # if we are in a selector, add the token to the current selectors
406
- current_selectors += token
407
-
408
- # mark this as the beginning of the selector unless we have already marked it
409
- rule_start = offset.first if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
410
- end
411
- end
411
+ # mark this as the beginning of the selector unless we have already marked it
412
+ rule_start = offset.first if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
412
413
  end
413
414
  end
414
415
 
415
416
  # check for unclosed braces
416
- if in_declarations > 0
417
- if options[:capture_offsets]
418
- add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
419
- else
420
- add_rule!(current_selectors, current_declarations, current_media_queries)
421
- end
417
+ return unless in_declarations > 0
418
+
419
+ unless options[:capture_offsets]
420
+ return add_rule!(current_selectors, current_declarations, current_media_queries)
422
421
  end
422
+
423
+ add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
423
424
  end
424
425
 
425
426
  # Load a remote CSS file.
@@ -432,7 +433,7 @@ module CssParser
432
433
  def load_uri!(uri, options = {}, deprecated = nil)
433
434
  uri = Addressable::URI.parse(uri) unless uri.respond_to? :scheme
434
435
 
435
- opts = {:base_uri => nil, :media_types => :all}
436
+ opts = {base_uri: nil, media_types: :all}
436
437
 
437
438
  if options.is_a? Hash
438
439
  opts.merge!(options)
@@ -452,14 +453,13 @@ module CssParser
452
453
  opts[:filename] = uri.to_s if opts[:capture_offsets]
453
454
 
454
455
  src, = read_remote_file(uri) # skip charset
455
- if src
456
- add_block!(src, opts)
457
- end
456
+
457
+ add_block!(src, opts) if src
458
458
  end
459
459
 
460
460
  # Load a local CSS file.
461
461
  def load_file!(file_name, options = {}, deprecated = nil)
462
- opts = {:base_dir => nil, :media_types => :all}
462
+ opts = {base_dir: nil, media_types: :all}
463
463
 
464
464
  if options.is_a? Hash
465
465
  opts.merge!(options)
@@ -482,7 +482,7 @@ module CssParser
482
482
 
483
483
  # Load a local CSS string.
484
484
  def load_string!(src, options = {}, deprecated = nil)
485
- opts = {:base_dir => nil, :media_types => :all}
485
+ opts = {base_dir: nil, media_types: :all}
486
486
 
487
487
  if options.is_a? Hash
488
488
  opts.merge!(options)
@@ -494,9 +494,8 @@ module CssParser
494
494
  add_block!(src, opts)
495
495
  end
496
496
 
497
-
498
-
499
497
  protected
498
+
500
499
  # Check that a path hasn't been loaded already
501
500
  #
502
501
  # Raises a CircularReferenceError exception if io_exceptions are on,
@@ -505,10 +504,11 @@ module CssParser
505
504
  path = path.to_s
506
505
  if @loaded_uris.include?(path)
507
506
  raise CircularReferenceError, "can't load #{path} more than once" if @options[:io_exceptions]
508
- return false
507
+
508
+ false
509
509
  else
510
510
  @loaded_uris << path
511
- return true
511
+ true
512
512
  end
513
513
  end
514
514
 
@@ -528,7 +528,7 @@ module CssParser
528
528
  # Returns a string.
529
529
  def cleanup_block(block, options = {}) # :nodoc:
530
530
  # Strip CSS comments
531
- utf8_block = block.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: ' ')
531
+ utf8_block = block.encode('UTF-8', 'UTF-8', invalid: :replace, undef: :replace, replace: ' ')
532
532
  utf8_block = ignore_pattern(utf8_block, STRIP_CSS_COMMENTS_RX, options)
533
533
 
534
534
  # Strip HTML comments - they shouldn't really be in here but
@@ -536,7 +536,7 @@ module CssParser
536
536
  utf8_block = ignore_pattern(utf8_block, STRIP_HTML_COMMENTS_RX, options)
537
537
 
538
538
  # Strip lines containing just whitespace
539
- utf8_block.gsub!(/^\s+$/, "") unless options[:capture_offsets]
539
+ utf8_block.gsub!(/^\s+$/, '') unless options[:capture_offsets]
540
540
 
541
541
  utf8_block
542
542
  end
@@ -572,11 +572,8 @@ module CssParser
572
572
  if uri.scheme == 'file'
573
573
  # local file
574
574
  path = uri.path
575
- path.gsub!(/^\//, '') if Gem.win_platform?
576
- fh = open(path, 'rb')
577
- src = fh.read
578
- charset = fh.respond_to?(:charset) ? fh.charset : 'utf-8'
579
- fh.close
575
+ path.gsub!(%r{^/}, '') if Gem.win_platform?
576
+ src = File.read(path, mode: 'rb')
580
577
  else
581
578
  # remote file
582
579
  if uri.scheme == 'https'
@@ -594,21 +591,22 @@ module CssParser
594
591
 
595
592
  if res.code.to_i >= 400
596
593
  @redirect_count = nil
597
- raise RemoteFileError.new(uri.to_s) if @options[:io_exceptions]
594
+ raise RemoteFileError, uri.to_s if @options[:io_exceptions]
595
+
598
596
  return '', nil
599
597
  elsif res.code.to_i >= 300 and res.code.to_i < 400
600
- if res['Location'] != nil
598
+ unless res['Location'].nil?
601
599
  return read_remote_file Addressable::URI.parse(Addressable::URI.escape(res['Location']))
602
600
  end
603
601
  end
604
602
 
605
603
  case res['content-encoding']
606
- when 'gzip'
607
- io = Zlib::GzipReader.new(StringIO.new(res.body))
608
- src = io.read
609
- when 'deflate'
610
- io = Zlib::Inflate.new
611
- src = io.inflate(res.body)
604
+ when 'gzip'
605
+ io = Zlib::GzipReader.new(StringIO.new(res.body))
606
+ src = io.read
607
+ when 'deflate'
608
+ io = Zlib::Inflate.new
609
+ src = io.inflate(res.body)
612
610
  end
613
611
  end
614
612
 
@@ -622,15 +620,17 @@ module CssParser
622
620
  end
623
621
  rescue
624
622
  @redirect_count = nil
625
- raise RemoteFileError.new(uri.to_s)if @options[:io_exceptions]
623
+ raise RemoteFileError, uri.to_s if @options[:io_exceptions]
624
+
626
625
  return nil, nil
627
626
  end
628
627
 
629
628
  @redirect_count = nil
630
- return src, charset
629
+ [src, charset]
631
630
  end
632
631
 
633
632
  private
633
+
634
634
  # Save a folded declaration block to the internal cache.
635
635
  def save_folded_declaration(block_hash, folded_declaration) # :nodoc:
636
636
  @folded_declaration_cache[block_hash] = folded_declaration
@@ -638,7 +638,7 @@ module CssParser
638
638
 
639
639
  # Retrieve a folded declaration block from the internal cache.
640
640
  def get_folded_declaration(block_hash) # :nodoc:
641
- return @folded_declaration_cache[block_hash] ||= nil
641
+ @folded_declaration_cache[block_hash] ||= nil
642
642
  end
643
643
 
644
644
  def reset! # :nodoc:
@@ -652,14 +652,15 @@ module CssParser
652
652
  # passed hash
653
653
  def css_node_to_h(hash, key, val)
654
654
  hash[key.strip] = '' and return hash if val.nil?
655
+
655
656
  lines = val.split(';')
656
657
  nodes = {}
657
658
  lines.each do |line|
658
659
  parts = line.split(':', 2)
659
- if (parts[1] =~ /:/)
660
+ if parts[1] =~ /:/
660
661
  nodes[parts[0]] = css_node_to_h(hash, parts[0], parts[1])
661
662
  else
662
- nodes[parts[0].to_s.strip] =parts[1].to_s.strip
663
+ nodes[parts[0].to_s.strip] = parts[1].to_s.strip
663
664
  end
664
665
  end
665
666
  hash[key.strip] = nodes