css_parser 1.7.0 → 1.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 66714382244cadbf112104868d94ff8b131ac4cf007deacac52d726bee1a9f9a
4
- data.tar.gz: 8ac559b1fb982da00dfbef4188b3773d2a78637f4db26393aba7cf62287b7883
3
+ metadata.gz: ebc43b9f09241b83ffe9a2878e0a2e5295c79f6d2e02bb20dcaf54d376f91e58
4
+ data.tar.gz: 5b29858923af894f1b84251c59de6a4638ae0929b63ac0b432a7607d9dd32111
5
5
  SHA512:
6
- metadata.gz: ba75d2e879d80cb7c854889990fd190f5c1fb664ed96b3a47ad78614a257d2ed7acfc3ba453ea34e87612636512305089c1a73d081c8a1c729a854217b17613e
7
- data.tar.gz: ff49b36b1007ae8ce0ecbfd6a2e92536d16c46062898c0a3bae402bacdb731785d6e4b80b0d08e557fcf372f2b0da2d75629c471c8ed2ce59addd13629eda9b9
6
+ metadata.gz: fd7a00f2be5a2ddbf730eb115dbc0e15f9ac7a71ac92e1a056161a2b418e01d7abeb757c6f8d5fabafd9e4f7dba034b209ca571012ee44ee6b377b2e4bd8100f
7
+ data.tar.gz: ac8aeba706f1ae35126c6d5c4a150a52862af8654f1342e6c5b36ebce8c0b71c26e0d0931d09081b3b01a70685c3d2b173031a3e1ff32a8477799c3e39e013e4
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
3
+ require 'strscan'
4
+
2
5
  module CssParser
3
6
  # Exception class used for any errors encountered while downloading remote files.
4
7
  class RemoteFileError < IOError; end
@@ -15,13 +18,13 @@ module CssParser
15
18
  # [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
16
19
  # [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
17
20
  class Parser
18
- USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)"
19
-
20
- STRIP_CSS_COMMENTS_RX = /\/\*.*?\*\//m
21
- STRIP_HTML_COMMENTS_RX = /\<\!\-\-|\-\-\>/m
21
+ USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)".freeze
22
+ RULESET_TOKENIZER_RX = /\s+|\\{2,}|\\?[{}\s"]|[()]|.[^\s"{}()\\]*/.freeze
23
+ STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
24
+ STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
22
25
 
23
26
  # Initial parsing
24
- RE_AT_IMPORT_RULE = /\@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s\)]*)["']?\)?([\w\s\,^\]\(\)]*)\)?[;\n]?/
27
+ RE_AT_IMPORT_RULE = /@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s)]*)["']?\)?([\w\s,^\]()]*)\)?[;\n]?/.freeze
25
28
 
26
29
  MAX_REDIRECTS = 3
27
30
 
@@ -35,10 +38,14 @@ module CssParser
35
38
  class << self; attr_reader :folded_declaration_cache; end
36
39
 
37
40
  def initialize(options = {})
38
- @options = {:absolute_paths => false,
39
- :import => true,
40
- :io_exceptions => true,
41
- :capture_offsets => false}.merge(options)
41
+ @options = {
42
+ absolute_paths: false,
43
+ import: true,
44
+ io_exceptions: true,
45
+ rule_set_exceptions: true,
46
+ capture_offsets: false,
47
+ user_agent: USER_AGENT
48
+ }.merge(options)
42
49
 
43
50
  # array of RuleSets
44
51
  @rules = []
@@ -70,21 +77,20 @@ module CssParser
70
77
  # Returns an array of declarations.
71
78
  def find_by_selector(selector, media_types = :all)
72
79
  out = []
73
- each_selector(media_types) do |sel, dec, spec|
80
+ each_selector(media_types) do |sel, dec, _spec|
74
81
  out << dec if sel.strip == selector.strip
75
82
  end
76
83
  out
77
84
  end
78
- alias_method :[], :find_by_selector
85
+ alias [] find_by_selector
79
86
 
80
87
  # Finds the rule sets that match the given selectors
81
88
  def find_rule_sets(selectors, media_types = :all)
82
89
  rule_sets = []
83
90
 
84
91
  selectors.each do |selector|
85
- selector.gsub!(/\s+/, ' ')
86
- selector.strip!
87
- each_rule_set(media_types) do |rule_set, media_type|
92
+ selector = selector.gsub(/\s+/, ' ').strip
93
+ each_rule_set(media_types) do |rule_set, _media_type|
88
94
  if !rule_sets.member?(rule_set) && rule_set.selectors.member?(selector)
89
95
  rule_sets << rule_set
90
96
  end
@@ -115,9 +121,9 @@ module CssParser
115
121
  # parser = CssParser::Parser.new
116
122
  # parser.add_block!(css)
117
123
  def add_block!(block, options = {})
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)}
124
+ options = {base_uri: nil, base_dir: nil, charset: nil, media_types: :all, only_media_types: :all}.merge(options)
125
+ options[:media_types] = [options[:media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
126
+ options[:only_media_types] = [options[:only_media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
121
127
 
122
128
  block = cleanup_block(block, options)
123
129
 
@@ -129,19 +135,19 @@ module CssParser
129
135
  if @options[:import]
130
136
  block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
131
137
  media_types = []
132
- if media_string = import_rule[-1]
133
- media_string.split(/[,]/).each do |t|
138
+ if (media_string = import_rule[-1])
139
+ media_string.split(',').each do |t|
134
140
  media_types << CssParser.sanitize_media_query(t) unless t.empty?
135
141
  end
136
142
  else
137
143
  media_types = [:all]
138
144
  end
139
145
 
140
- next unless options[:only_media_types].include?(:all) or media_types.length < 1 or (media_types & options[:only_media_types]).length > 0
146
+ next unless options[:only_media_types].include?(:all) or media_types.empty? or !(media_types & options[:only_media_types]).empty?
141
147
 
142
148
  import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
143
149
 
144
- import_options = { :media_types => media_types }
150
+ import_options = {media_types: media_types}
145
151
  import_options[:capture_offsets] = true if options[:capture_offsets]
146
152
 
147
153
  if options[:base_uri]
@@ -161,12 +167,44 @@ module CssParser
161
167
  parse_block_into_rule_sets!(block, options)
162
168
  end
163
169
 
164
- # Add a CSS rule by setting the +selectors+, +declarations+ and +media_types+.
170
+ # Add a CSS rule by setting the +selectors+, +declarations+
171
+ # and +media_types+. Optional pass +filename+ , +offset+ for source
172
+ # reference too.
165
173
  #
166
- # +media_types+ can be a symbol or an array of symbols.
167
- def add_rule!(selectors, declarations, media_types = :all)
168
- rule_set = RuleSet.new(selectors, declarations)
169
- add_rule_set!(rule_set, media_types)
174
+ # +media_types+ can be a symbol or an array of symbols. default to :all
175
+ # optional fields for source location for source location
176
+ # +filename+ can be a string or uri pointing to the file or url location.
177
+ # +offset+ should be Range object representing the start and end byte locations where the rule was found in the file.
178
+ def add_rule!(*args, selectors: nil, block: nil, filename: nil, offset: nil, media_types: :all) # rubocop:disable Metrics/ParameterLists
179
+ if args.any?
180
+ media_types = nil
181
+ if selectors || block || filename || offset || media_types
182
+ raise ArgumentError, "don't mix positional and keyword arguments arguments"
183
+ end
184
+
185
+ warn '[DEPRECATION] `add_rule!` with positional arguments is deprecated. ' \
186
+ 'Please use keyword arguments instead.', uplevel: 1
187
+
188
+ case args.length
189
+ when 2
190
+ selectors, block = args
191
+ when 3
192
+ selectors, block, media_types = args
193
+ else
194
+ raise ArgumentError
195
+ end
196
+ end
197
+
198
+ begin
199
+ rule_set = RuleSet.new(
200
+ selectors: selectors, block: block,
201
+ offset: offset, filename: filename
202
+ )
203
+
204
+ add_rule_set!(rule_set, media_types)
205
+ rescue ArgumentError => e
206
+ raise e if @options[:rule_set_exceptions]
207
+ end
170
208
  end
171
209
 
172
210
  # Add a CSS rule by setting the +selectors+, +declarations+, +filename+, +offset+ and +media_types+.
@@ -175,29 +213,32 @@ module CssParser
175
213
  # +offset+ should be Range object representing the start and end byte locations where the rule was found in the file.
176
214
  # +media_types+ can be a symbol or an array of symbols.
177
215
  def add_rule_with_offsets!(selectors, declarations, filename, offset, media_types = :all)
178
- rule_set = OffsetAwareRuleSet.new(filename, offset, selectors, declarations)
179
- add_rule_set!(rule_set, media_types)
216
+ warn '[DEPRECATION] `add_rule_with_offsets!` is deprecated. Please use `add_rule!` instead.', uplevel: 1
217
+ add_rule!(
218
+ selectors: selectors, block: declarations, media_types: media_types,
219
+ filename: filename, offset: offset
220
+ )
180
221
  end
181
222
 
182
223
  # Add a CssParser RuleSet object.
183
224
  #
184
225
  # +media_types+ can be a symbol or an array of symbols.
185
226
  def add_rule_set!(ruleset, media_types = :all)
186
- raise ArgumentError unless ruleset.kind_of?(CssParser::RuleSet)
227
+ raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
187
228
 
188
- media_types = [media_types] unless Array === media_types
189
- media_types = media_types.flat_map { |mt| CssParser.sanitize_media_query(mt)}
229
+ media_types = [media_types] unless media_types.is_a?(Array)
230
+ media_types = media_types.flat_map { |mt| CssParser.sanitize_media_query(mt) }
190
231
 
191
- @rules << {:media_types => media_types, :rules => ruleset}
232
+ @rules << {media_types: media_types, rules: ruleset}
192
233
  end
193
234
 
194
235
  # Remove a CssParser RuleSet object.
195
236
  #
196
237
  # +media_types+ can be a symbol or an array of symbols.
197
238
  def remove_rule_set!(ruleset, media_types = :all)
198
- raise ArgumentError unless ruleset.kind_of?(CssParser::RuleSet)
239
+ raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
199
240
 
200
- media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
241
+ media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
201
242
 
202
243
  @rules.reject! do |rule|
203
244
  rule[:media_types] == media_types && rule[:rules].to_s == ruleset.to_s
@@ -209,7 +250,7 @@ module CssParser
209
250
  # +media_types+ can be a symbol or an array of symbols.
210
251
  def each_rule_set(media_types = :all) # :yields: rule_set, media_types
211
252
  media_types = [:all] if media_types.nil?
212
- media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
253
+ media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
213
254
 
214
255
  @rules.each do |block|
215
256
  if media_types.include?(:all) or block[:media_types].any? { |mt| media_types.include?(mt) }
@@ -222,7 +263,7 @@ module CssParser
222
263
  def to_h(which_media = :all)
223
264
  out = {}
224
265
  styles_by_media_types = {}
225
- each_selector(which_media) do |selectors, declarations, specificity, media_types|
266
+ each_selector(which_media) do |selectors, declarations, _specificity, media_types|
226
267
  media_types.each do |media_type|
227
268
  styles_by_media_types[media_type] ||= []
228
269
  styles_by_media_types[media_type] << [selectors, declarations]
@@ -244,7 +285,7 @@ module CssParser
244
285
  # +media_types+ can be a symbol or an array of symbols.
245
286
  # See RuleSet#each_selector for +options+.
246
287
  def each_selector(all_media_types = :all, options = {}) # :yields: selectors, declarations, specificity, media_types
247
- return to_enum(:each_selector) unless block_given?
288
+ return to_enum(__method__, all_media_types, options) unless block_given?
248
289
 
249
290
  each_rule_set(all_media_types) do |rule_set, media_types|
250
291
  rule_set.each_selector(options) do |selectors, declarations, specificity|
@@ -255,9 +296,10 @@ module CssParser
255
296
 
256
297
  # Output all CSS rules as a single stylesheet.
257
298
  def to_s(which_media = :all)
258
- out = String.new
299
+ out = []
259
300
  styles_by_media_types = {}
260
- each_selector(which_media) do |selectors, declarations, specificity, media_types|
301
+
302
+ each_selector(which_media) do |selectors, declarations, _specificity, media_types|
261
303
  media_types.each do |media_type|
262
304
  styles_by_media_types[media_type] ||= []
263
305
  styles_by_media_types[media_type] << [selectors, declarations]
@@ -266,20 +308,21 @@ module CssParser
266
308
 
267
309
  styles_by_media_types.each_pair do |media_type, media_styles|
268
310
  media_block = (media_type != :all)
269
- out << "@media #{media_type} {\n" if media_block
311
+ out << "@media #{media_type} {" if media_block
270
312
 
271
313
  media_styles.each do |media_style|
272
314
  if media_block
273
- out << " #{media_style[0]} {\n #{media_style[1]}\n }\n"
315
+ out.push(" #{media_style[0]} {\n #{media_style[1]}\n }")
274
316
  else
275
- out << "#{media_style[0]} {\n#{media_style[1]}\n}\n"
317
+ out.push("#{media_style[0]} {\n#{media_style[1]}\n}")
276
318
  end
277
319
  end
278
320
 
279
- out << "}\n" if media_block
321
+ out << '}' if media_block
280
322
  end
281
323
 
282
- out
324
+ out << ''
325
+ out.join("\n")
283
326
  end
284
327
 
285
328
  # A hash of { :media_query => rule_sets }
@@ -287,7 +330,7 @@ module CssParser
287
330
  rules_by_media = {}
288
331
  @rules.each do |block|
289
332
  block[:media_types].each do |mt|
290
- unless rules_by_media.has_key?(mt)
333
+ unless rules_by_media.key?(mt)
291
334
  rules_by_media[mt] = []
292
335
  end
293
336
  rules_by_media[mt] << block[:rules]
@@ -299,15 +342,13 @@ module CssParser
299
342
 
300
343
  # Merge declarations with the same selector.
301
344
  def compact! # :nodoc:
302
- compacted = []
303
-
304
- compacted
345
+ []
305
346
  end
306
347
 
307
348
  def parse_block_into_rule_sets!(block, options = {}) # :nodoc:
308
349
  current_media_queries = [:all]
309
350
  if options[:media_types]
310
- current_media_queries = options[:media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
351
+ current_media_queries = options[:media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
311
352
  end
312
353
 
313
354
  in_declarations = 0
@@ -318,17 +359,21 @@ module CssParser
318
359
  in_at_media_rule = false
319
360
  in_media_block = false
320
361
 
321
- current_selectors = String.new
322
- current_media_query = String.new
323
- current_declarations = String.new
362
+ current_selectors = +''
363
+ current_media_query = +''
364
+ current_declarations = +''
324
365
 
325
366
  # once we are in a rule, we will use this to store where we started if we are capturing offsets
326
367
  rule_start = nil
327
- offset = nil
368
+ start_offset = nil
369
+ end_offset = nil
328
370
 
329
- block.scan(/\s+|[\\]{2,}|[\\]?[{}\s"]|.[^\s"{}\\]*/) do |token|
371
+ scanner = StringScanner.new(block)
372
+ until scanner.eos?
330
373
  # save the regex offset so that we know where in the file we are
331
- offset = Regexp.last_match.offset(0) if options[:capture_offsets]
374
+ start_offset = scanner.pos
375
+ token = scanner.scan(RULESET_TOKENIZER_RX)
376
+ end_offset = scanner.pos
332
377
 
333
378
  if token.start_with?('"') # found un-escaped double quote
334
379
  in_string = !in_string
@@ -349,82 +394,95 @@ module CssParser
349
394
  current_declarations << token
350
395
 
351
396
  if !in_string && token.include?('}')
352
- current_declarations.gsub!(/\}[\s]*$/, '')
397
+ current_declarations.gsub!(/\}\s*$/, '')
353
398
 
354
399
  in_declarations -= 1
355
400
  current_declarations.strip!
356
401
 
357
402
  unless current_declarations.empty?
403
+ add_rule_options = {
404
+ selectors: current_selectors, block: current_declarations,
405
+ media_types: current_media_queries
406
+ }
358
407
  if options[:capture_offsets]
359
- add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
360
- else
361
- add_rule!(current_selectors, current_declarations, current_media_queries)
408
+ add_rule_options[:filename] = options[:filename]
409
+ add_rule_options[:offset] = rule_start..end_offset
362
410
  end
411
+ add_rule!(**add_rule_options)
363
412
  end
364
413
 
365
- current_selectors = String.new
366
- current_declarations = String.new
414
+ current_selectors = +''
415
+ current_declarations = +''
367
416
 
368
417
  # restart our search for selectors and declarations
369
418
  rule_start = nil if options[:capture_offsets]
370
419
  end
371
- elsif token =~ /@media/i
420
+ elsif /@media/i.match?(token)
372
421
  # found '@media', reset current media_types
373
422
  in_at_media_rule = true
374
423
  current_media_queries = []
375
424
  elsif in_at_media_rule
376
425
  if token.include?('{')
377
- block_depth = block_depth + 1
426
+ block_depth += 1
378
427
  in_at_media_rule = false
379
428
  in_media_block = true
380
429
  current_media_queries << CssParser.sanitize_media_query(current_media_query)
381
- current_media_query = String.new
430
+ current_media_query = +''
382
431
  elsif token.include?(',')
383
432
  # new media query begins
384
433
  token.tr!(',', ' ')
385
434
  token.strip!
386
435
  current_media_query << token << ' '
387
436
  current_media_queries << CssParser.sanitize_media_query(current_media_query)
388
- current_media_query = String.new
437
+ current_media_query = +''
389
438
  else
390
439
  token.strip!
391
- current_media_query << token << ' '
440
+ # special-case the ( and ) tokens to remove inner-whitespace
441
+ # (eg we'd prefer '(width: 500px)' to '( width: 500px )' )
442
+ case token
443
+ when '('
444
+ current_media_query << token
445
+ when ')'
446
+ current_media_query.sub!(/ ?$/, token)
447
+ else
448
+ current_media_query << token << ' '
449
+ end
392
450
  end
393
- elsif in_charset or token =~ /@charset/i
451
+ elsif in_charset or /@charset/i.match?(token)
394
452
  # iterate until we are out of the charset declaration
395
453
  in_charset = !token.include?(';')
396
- else
397
- if !in_string && token.include?('}')
398
- block_depth = block_depth - 1
399
-
400
- # reset the current media query scope
401
- if in_media_block
402
- current_media_queries = [:all]
403
- in_media_block = false
404
- end
405
- else
406
- if !in_string && token.include?('{')
407
- current_selectors.strip!
408
- in_declarations += 1
409
- else
410
- # if we are in a selector, add the token to the current selectors
411
- current_selectors << token
454
+ elsif !in_string && token.include?('}')
455
+ block_depth -= 1
412
456
 
413
- # mark this as the beginning of the selector unless we have already marked it
414
- rule_start = offset.first if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
415
- end
457
+ # reset the current media query scope
458
+ if in_media_block
459
+ current_media_queries = [:all]
460
+ in_media_block = false
416
461
  end
462
+ elsif !in_string && token.include?('{')
463
+ current_selectors.strip!
464
+ in_declarations += 1
465
+ else
466
+ # if we are in a selector, add the token to the current selectors
467
+ current_selectors << token
468
+
469
+ # mark this as the beginning of the selector unless we have already marked it
470
+ rule_start = start_offset if options[:capture_offsets] && rule_start.nil? && /^[^\s]+$/.match?(token)
417
471
  end
418
472
  end
419
473
 
420
474
  # check for unclosed braces
421
- if in_declarations > 0
422
- if options[:capture_offsets]
423
- add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
424
- else
425
- add_rule!(current_selectors, current_declarations, current_media_queries)
426
- end
475
+ return unless in_declarations > 0
476
+
477
+ add_rule_options = {
478
+ selectors: current_selectors, block: current_declarations,
479
+ media_types: current_media_queries
480
+ }
481
+ if options[:capture_offsets]
482
+ add_rule_options[:filename] = options[:filename]
483
+ add_rule_options[:offset] = rule_start..end_offset
427
484
  end
485
+ add_rule!(**add_rule_options)
428
486
  end
429
487
 
430
488
  # Load a remote CSS file.
@@ -437,11 +495,13 @@ module CssParser
437
495
  def load_uri!(uri, options = {}, deprecated = nil)
438
496
  uri = Addressable::URI.parse(uri) unless uri.respond_to? :scheme
439
497
 
440
- opts = {:base_uri => nil, :media_types => :all}
498
+ opts = {base_uri: nil, media_types: :all}
441
499
 
442
500
  if options.is_a? Hash
443
501
  opts.merge!(options)
444
502
  else
503
+ warn '[DEPRECATION] `load_uri!` with positional arguments is deprecated. ' \
504
+ 'Please use keyword arguments instead.', uplevel: 1
445
505
  opts[:base_uri] = options if options.is_a? String
446
506
  opts[:media_types] = deprecated if deprecated
447
507
  end
@@ -457,18 +517,19 @@ module CssParser
457
517
  opts[:filename] = uri.to_s if opts[:capture_offsets]
458
518
 
459
519
  src, = read_remote_file(uri) # skip charset
460
- if src
461
- add_block!(src, opts)
462
- end
520
+
521
+ add_block!(src, opts) if src
463
522
  end
464
523
 
465
524
  # Load a local CSS file.
466
525
  def load_file!(file_name, options = {}, deprecated = nil)
467
- opts = {:base_dir => nil, :media_types => :all}
526
+ opts = {base_dir: nil, media_types: :all}
468
527
 
469
528
  if options.is_a? Hash
470
529
  opts.merge!(options)
471
530
  else
531
+ warn '[DEPRECATION] `load_file!` with positional arguments is deprecated. ' \
532
+ 'Please use keyword arguments instead.', uplevel: 1
472
533
  opts[:base_dir] = options if options.is_a? String
473
534
  opts[:media_types] = deprecated if deprecated
474
535
  end
@@ -477,7 +538,7 @@ module CssParser
477
538
  return unless File.readable?(file_name)
478
539
  return unless circular_reference_check(file_name)
479
540
 
480
- src = IO.read(file_name)
541
+ src = File.read(file_name)
481
542
 
482
543
  opts[:filename] = file_name if opts[:capture_offsets]
483
544
  opts[:base_dir] = File.dirname(file_name)
@@ -487,11 +548,13 @@ module CssParser
487
548
 
488
549
  # Load a local CSS string.
489
550
  def load_string!(src, options = {}, deprecated = nil)
490
- opts = {:base_dir => nil, :media_types => :all}
551
+ opts = {base_dir: nil, media_types: :all}
491
552
 
492
553
  if options.is_a? Hash
493
554
  opts.merge!(options)
494
555
  else
556
+ warn '[DEPRECATION] `load_file!` with positional arguments is deprecated. ' \
557
+ 'Please use keyword arguments instead.', uplevel: 1
495
558
  opts[:base_dir] = options if options.is_a? String
496
559
  opts[:media_types] = deprecated if deprecated
497
560
  end
@@ -499,9 +562,8 @@ module CssParser
499
562
  add_block!(src, opts)
500
563
  end
501
564
 
502
-
503
-
504
565
  protected
566
+
505
567
  # Check that a path hasn't been loaded already
506
568
  #
507
569
  # Raises a CircularReferenceError exception if io_exceptions are on,
@@ -510,10 +572,11 @@ module CssParser
510
572
  path = path.to_s
511
573
  if @loaded_uris.include?(path)
512
574
  raise CircularReferenceError, "can't load #{path} more than once" if @options[:io_exceptions]
513
- return false
575
+
576
+ false
514
577
  else
515
578
  @loaded_uris << path
516
- return true
579
+ true
517
580
  end
518
581
  end
519
582
 
@@ -533,7 +596,7 @@ module CssParser
533
596
  # Returns a string.
534
597
  def cleanup_block(block, options = {}) # :nodoc:
535
598
  # Strip CSS comments
536
- utf8_block = block.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: ' ')
599
+ utf8_block = block.encode('UTF-8', 'UTF-8', invalid: :replace, undef: :replace, replace: ' ')
537
600
  utf8_block = ignore_pattern(utf8_block, STRIP_CSS_COMMENTS_RX, options)
538
601
 
539
602
  # Strip HTML comments - they shouldn't really be in here but
@@ -541,7 +604,7 @@ module CssParser
541
604
  utf8_block = ignore_pattern(utf8_block, STRIP_HTML_COMMENTS_RX, options)
542
605
 
543
606
  # Strip lines containing just whitespace
544
- utf8_block.gsub!(/^\s+$/, "") unless options[:capture_offsets]
607
+ utf8_block.gsub!(/^\s+$/, '') unless options[:capture_offsets]
545
608
 
546
609
  utf8_block
547
610
  end
@@ -577,11 +640,8 @@ module CssParser
577
640
  if uri.scheme == 'file'
578
641
  # local file
579
642
  path = uri.path
580
- path.gsub!(/^\//, '') if Gem.win_platform?
581
- fh = open(path, 'rb')
582
- src = fh.read
583
- charset = fh.respond_to?(:charset) ? fh.charset : 'utf-8'
584
- fh.close
643
+ path.gsub!(%r{^/}, '') if Gem.win_platform?
644
+ src = File.read(path, mode: 'rb')
585
645
  else
586
646
  # remote file
587
647
  if uri.scheme == 'https'
@@ -593,49 +653,47 @@ module CssParser
593
653
  http = Net::HTTP.new(uri.host, uri.port)
594
654
  end
595
655
 
596
- res = http.get(uri.request_uri, {'User-Agent' => USER_AGENT, 'Accept-Encoding' => 'gzip'})
656
+ res = http.get(uri.request_uri, {'User-Agent' => @options[:user_agent], 'Accept-Encoding' => 'gzip'})
597
657
  src = res.body
598
658
  charset = res.respond_to?(:charset) ? res.encoding : 'utf-8'
599
659
 
600
660
  if res.code.to_i >= 400
601
661
  @redirect_count = nil
602
- raise RemoteFileError.new(uri.to_s) if @options[:io_exceptions]
662
+ raise RemoteFileError, uri.to_s if @options[:io_exceptions]
663
+
603
664
  return '', nil
604
665
  elsif res.code.to_i >= 300 and res.code.to_i < 400
605
- if res['Location'] != nil
666
+ unless res['Location'].nil?
606
667
  return read_remote_file Addressable::URI.parse(Addressable::URI.escape(res['Location']))
607
668
  end
608
669
  end
609
670
 
610
671
  case res['content-encoding']
611
- when 'gzip'
612
- io = Zlib::GzipReader.new(StringIO.new(res.body))
613
- src = io.read
614
- when 'deflate'
615
- io = Zlib::Inflate.new
616
- src = io.inflate(res.body)
672
+ when 'gzip'
673
+ io = Zlib::GzipReader.new(StringIO.new(res.body))
674
+ src = io.read
675
+ when 'deflate'
676
+ io = Zlib::Inflate.new
677
+ src = io.inflate(res.body)
617
678
  end
618
679
  end
619
680
 
620
681
  if charset
621
- if String.method_defined?(:encode)
622
- src.encode!('UTF-8', charset)
623
- else
624
- ic = Iconv.new('UTF-8//IGNORE', charset)
625
- src = ic.iconv(src)
626
- end
682
+ src.encode!('UTF-8', charset)
627
683
  end
628
684
  rescue
629
685
  @redirect_count = nil
630
- raise RemoteFileError.new(uri.to_s)if @options[:io_exceptions]
686
+ raise RemoteFileError, uri.to_s if @options[:io_exceptions]
687
+
631
688
  return nil, nil
632
689
  end
633
690
 
634
691
  @redirect_count = nil
635
- return src, charset
692
+ [src, charset]
636
693
  end
637
694
 
638
695
  private
696
+
639
697
  # Save a folded declaration block to the internal cache.
640
698
  def save_folded_declaration(block_hash, folded_declaration) # :nodoc:
641
699
  @folded_declaration_cache[block_hash] = folded_declaration
@@ -643,7 +701,7 @@ module CssParser
643
701
 
644
702
  # Retrieve a folded declaration block from the internal cache.
645
703
  def get_folded_declaration(block_hash) # :nodoc:
646
- return @folded_declaration_cache[block_hash] ||= nil
704
+ @folded_declaration_cache[block_hash] ||= nil
647
705
  end
648
706
 
649
707
  def reset! # :nodoc:
@@ -657,14 +715,15 @@ module CssParser
657
715
  # passed hash
658
716
  def css_node_to_h(hash, key, val)
659
717
  hash[key.strip] = '' and return hash if val.nil?
718
+
660
719
  lines = val.split(';')
661
720
  nodes = {}
662
721
  lines.each do |line|
663
722
  parts = line.split(':', 2)
664
- if (parts[1] =~ /:/)
723
+ if parts[1].include?(':')
665
724
  nodes[parts[0]] = css_node_to_h(hash, parts[0], parts[1])
666
725
  else
667
- nodes[parts[0].to_s.strip] =parts[1].to_s.strip
726
+ nodes[parts[0].to_s.strip] = parts[1].to_s.strip
668
727
  end
669
728
  end
670
729
  hash[key.strip] = nodes