css_parser 1.7.1 → 1.13.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
2
  SHA256:
3
- metadata.gz: f2dafcbcaaa6e1e5ecb45b092b2d997c799d02c1c06d90130989b45cc085452d
4
- data.tar.gz: 1b5cfa1908355aa798b96ed3234e00ea26d3f4a8f36076a2f6ccdec840dfaa71
3
+ metadata.gz: 9bad6c42b4f2fbda14292b503c3f381ea4b3a9a4b77c74dd44da724866237901
4
+ data.tar.gz: 0b47ed4ef7d197a5552af3a99db42b89a8937cca205c7b6cf865cfb930d2d265
5
5
  SHA512:
6
- metadata.gz: a589901eab26c8b78d8b0c5eae5224f249ff6e30caa9021c41d583b5a530b4b926f7d3f57cefad6c7b340c9ea925cc44e6b7b86321f6cecfd27684921822b594
7
- data.tar.gz: 60882d77d15847bab9aa70ebcfc788cf959580e61577e372badf1677aacee6958eae54e91e6db8ea6063a1cc9b627525a704adb13040b7be601608b69627b0d7
6
+ metadata.gz: e91f481e093e423b873fcfd673bda8337645b066a7c278bea28175f8e11d94d9ac1dc401fc7defa1f8afca11d9125f7007aa8c12e1a4410351511f6e876bf593
7
+ data.tar.gz: 0fd7db359a459575bc2b7f08b5d6f86d5d78c58247af82db87884e5a1e10e768f9f3f0247abf2d491ebf30c9df8ec623fd65c8c51bf089fa29eba9cf75a9d9ac
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module CssParser
3
4
  # Exception class used for any errors encountered while downloading remote files.
4
5
  class RemoteFileError < IOError; end
@@ -15,13 +16,13 @@ module CssParser
15
16
  # [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
16
17
  # [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
17
18
  class Parser
18
- 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)"
19
20
 
20
- STRIP_CSS_COMMENTS_RX = /\/\*.*?\*\//m
21
- STRIP_HTML_COMMENTS_RX = /\<\!\-\-|\-\-\>/m
21
+ STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
22
+ STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
22
23
 
23
24
  # Initial parsing
24
- 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
25
26
 
26
27
  MAX_REDIRECTS = 3
27
28
 
@@ -35,10 +36,11 @@ module CssParser
35
36
  class << self; attr_reader :folded_declaration_cache; end
36
37
 
37
38
  def initialize(options = {})
38
- @options = {:absolute_paths => false,
39
- :import => true,
40
- :io_exceptions => true,
41
- :capture_offsets => false}.merge(options)
39
+ @options = {absolute_paths: false,
40
+ import: true,
41
+ io_exceptions: true,
42
+ rule_set_exceptions: true,
43
+ capture_offsets: false}.merge(options)
42
44
 
43
45
  # array of RuleSets
44
46
  @rules = []
@@ -70,21 +72,20 @@ module CssParser
70
72
  # Returns an array of declarations.
71
73
  def find_by_selector(selector, media_types = :all)
72
74
  out = []
73
- each_selector(media_types) do |sel, dec, spec|
75
+ each_selector(media_types) do |sel, dec, _spec|
74
76
  out << dec if sel.strip == selector.strip
75
77
  end
76
78
  out
77
79
  end
78
- alias_method :[], :find_by_selector
80
+ alias [] find_by_selector
79
81
 
80
82
  # Finds the rule sets that match the given selectors
81
83
  def find_rule_sets(selectors, media_types = :all)
82
84
  rule_sets = []
83
85
 
84
86
  selectors.each do |selector|
85
- selector.gsub!(/\s+/, ' ')
86
- selector.strip!
87
- each_rule_set(media_types) do |rule_set, media_type|
87
+ selector = selector.gsub(/\s+/, ' ').strip
88
+ each_rule_set(media_types) do |rule_set, _media_type|
88
89
  if !rule_sets.member?(rule_set) && rule_set.selectors.member?(selector)
89
90
  rule_sets << rule_set
90
91
  end
@@ -115,9 +116,9 @@ module CssParser
115
116
  # parser = CssParser::Parser.new
116
117
  # parser.add_block!(css)
117
118
  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)}
119
+ options = {base_uri: nil, base_dir: nil, charset: nil, media_types: :all, only_media_types: :all}.merge(options)
120
+ options[:media_types] = [options[:media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
121
+ options[:only_media_types] = [options[:only_media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
121
122
 
122
123
  block = cleanup_block(block, options)
123
124
 
@@ -129,19 +130,19 @@ module CssParser
129
130
  if @options[:import]
130
131
  block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
131
132
  media_types = []
132
- if media_string = import_rule[-1]
133
- media_string.split(/[,]/).each do |t|
133
+ if (media_string = import_rule[-1])
134
+ media_string.split(/,/).each do |t|
134
135
  media_types << CssParser.sanitize_media_query(t) unless t.empty?
135
136
  end
136
137
  else
137
138
  media_types = [:all]
138
139
  end
139
140
 
140
- next unless options[:only_media_types].include?(:all) or media_types.length < 1 or (media_types & options[:only_media_types]).length > 0
141
+ next unless options[:only_media_types].include?(:all) or media_types.empty? or !(media_types & options[:only_media_types]).empty?
141
142
 
142
143
  import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
143
144
 
144
- import_options = { :media_types => media_types }
145
+ import_options = {media_types: media_types}
145
146
  import_options[:capture_offsets] = true if options[:capture_offsets]
146
147
 
147
148
  if options[:base_uri]
@@ -167,6 +168,8 @@ module CssParser
167
168
  def add_rule!(selectors, declarations, media_types = :all)
168
169
  rule_set = RuleSet.new(selectors, declarations)
169
170
  add_rule_set!(rule_set, media_types)
171
+ rescue ArgumentError => e
172
+ raise e if @options[:rule_set_exceptions]
170
173
  end
171
174
 
172
175
  # Add a CSS rule by setting the +selectors+, +declarations+, +filename+, +offset+ and +media_types+.
@@ -183,21 +186,21 @@ module CssParser
183
186
  #
184
187
  # +media_types+ can be a symbol or an array of symbols.
185
188
  def add_rule_set!(ruleset, media_types = :all)
186
- raise ArgumentError unless ruleset.kind_of?(CssParser::RuleSet)
189
+ raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
187
190
 
188
- media_types = [media_types] unless Array === media_types
189
- media_types = media_types.flat_map { |mt| CssParser.sanitize_media_query(mt)}
191
+ media_types = [media_types] unless media_types.is_a?(Array)
192
+ media_types = media_types.flat_map { |mt| CssParser.sanitize_media_query(mt) }
190
193
 
191
- @rules << {:media_types => media_types, :rules => ruleset}
194
+ @rules << {media_types: media_types, rules: ruleset}
192
195
  end
193
196
 
194
197
  # Remove a CssParser RuleSet object.
195
198
  #
196
199
  # +media_types+ can be a symbol or an array of symbols.
197
200
  def remove_rule_set!(ruleset, media_types = :all)
198
- raise ArgumentError unless ruleset.kind_of?(CssParser::RuleSet)
201
+ raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
199
202
 
200
- media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
203
+ media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
201
204
 
202
205
  @rules.reject! do |rule|
203
206
  rule[:media_types] == media_types && rule[:rules].to_s == ruleset.to_s
@@ -209,7 +212,7 @@ module CssParser
209
212
  # +media_types+ can be a symbol or an array of symbols.
210
213
  def each_rule_set(media_types = :all) # :yields: rule_set, media_types
211
214
  media_types = [:all] if media_types.nil?
212
- media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
215
+ media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
213
216
 
214
217
  @rules.each do |block|
215
218
  if media_types.include?(:all) or block[:media_types].any? { |mt| media_types.include?(mt) }
@@ -222,7 +225,7 @@ module CssParser
222
225
  def to_h(which_media = :all)
223
226
  out = {}
224
227
  styles_by_media_types = {}
225
- each_selector(which_media) do |selectors, declarations, specificity, media_types|
228
+ each_selector(which_media) do |selectors, declarations, _specificity, media_types|
226
229
  media_types.each do |media_type|
227
230
  styles_by_media_types[media_type] ||= []
228
231
  styles_by_media_types[media_type] << [selectors, declarations]
@@ -244,7 +247,7 @@ module CssParser
244
247
  # +media_types+ can be a symbol or an array of symbols.
245
248
  # See RuleSet#each_selector for +options+.
246
249
  def each_selector(all_media_types = :all, options = {}) # :yields: selectors, declarations, specificity, media_types
247
- return to_enum(:each_selector) unless block_given?
250
+ return to_enum(__method__, all_media_types, options) unless block_given?
248
251
 
249
252
  each_rule_set(all_media_types) do |rule_set, media_types|
250
253
  rule_set.each_selector(options) do |selectors, declarations, specificity|
@@ -255,9 +258,10 @@ module CssParser
255
258
 
256
259
  # Output all CSS rules as a single stylesheet.
257
260
  def to_s(which_media = :all)
258
- out = String.new
261
+ out = []
259
262
  styles_by_media_types = {}
260
- each_selector(which_media) do |selectors, declarations, specificity, media_types|
263
+
264
+ each_selector(which_media) do |selectors, declarations, _specificity, media_types|
261
265
  media_types.each do |media_type|
262
266
  styles_by_media_types[media_type] ||= []
263
267
  styles_by_media_types[media_type] << [selectors, declarations]
@@ -266,20 +270,21 @@ module CssParser
266
270
 
267
271
  styles_by_media_types.each_pair do |media_type, media_styles|
268
272
  media_block = (media_type != :all)
269
- out << "@media #{media_type} {\n" if media_block
273
+ out << "@media #{media_type} {" if media_block
270
274
 
271
275
  media_styles.each do |media_style|
272
276
  if media_block
273
- out << " #{media_style[0]} {\n #{media_style[1]}\n }\n"
277
+ out.push(" #{media_style[0]} {\n #{media_style[1]}\n }")
274
278
  else
275
- out << "#{media_style[0]} {\n#{media_style[1]}\n}\n"
279
+ out.push("#{media_style[0]} {\n#{media_style[1]}\n}")
276
280
  end
277
281
  end
278
282
 
279
- out << "}\n" if media_block
283
+ out << '}' if media_block
280
284
  end
281
285
 
282
- out
286
+ out << ''
287
+ out.join("\n")
283
288
  end
284
289
 
285
290
  # A hash of { :media_query => rule_sets }
@@ -287,7 +292,7 @@ module CssParser
287
292
  rules_by_media = {}
288
293
  @rules.each do |block|
289
294
  block[:media_types].each do |mt|
290
- unless rules_by_media.has_key?(mt)
295
+ unless rules_by_media.key?(mt)
291
296
  rules_by_media[mt] = []
292
297
  end
293
298
  rules_by_media[mt] << block[:rules]
@@ -299,15 +304,13 @@ module CssParser
299
304
 
300
305
  # Merge declarations with the same selector.
301
306
  def compact! # :nodoc:
302
- compacted = []
303
-
304
- compacted
307
+ []
305
308
  end
306
309
 
307
310
  def parse_block_into_rule_sets!(block, options = {}) # :nodoc:
308
311
  current_media_queries = [:all]
309
312
  if options[:media_types]
310
- current_media_queries = options[:media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
313
+ current_media_queries = options[:media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
311
314
  end
312
315
 
313
316
  in_declarations = 0
@@ -326,7 +329,7 @@ module CssParser
326
329
  rule_start = nil
327
330
  offset = nil
328
331
 
329
- block.scan(/\s+|[\\]{2,}|[\\]?[{}\s"]|.[^\s"{}\\]*/) do |token|
332
+ block.scan(/\s+|\\{2,}|\\?[{}\s"]|.[^\s"{}\\]*/) do |token|
330
333
  # save the regex offset so that we know where in the file we are
331
334
  offset = Regexp.last_match.offset(0) if options[:capture_offsets]
332
335
 
@@ -349,7 +352,7 @@ module CssParser
349
352
  current_declarations << token
350
353
 
351
354
  if !in_string && token.include?('}')
352
- current_declarations.gsub!(/\}[\s]*$/, '')
355
+ current_declarations.gsub!(/\}\s*$/, '')
353
356
 
354
357
  in_declarations -= 1
355
358
  current_declarations.strip!
@@ -374,7 +377,7 @@ module CssParser
374
377
  current_media_queries = []
375
378
  elsif in_at_media_rule
376
379
  if token.include?('{')
377
- block_depth = block_depth + 1
380
+ block_depth += 1
378
381
  in_at_media_rule = false
379
382
  in_media_block = true
380
383
  current_media_queries << CssParser.sanitize_media_query(current_media_query)
@@ -393,38 +396,34 @@ module CssParser
393
396
  elsif in_charset or token =~ /@charset/i
394
397
  # iterate until we are out of the charset declaration
395
398
  in_charset = !token.include?(';')
396
- else
397
- if !in_string && token.include?('}')
398
- block_depth = block_depth - 1
399
+ elsif !in_string && token.include?('}')
400
+ block_depth -= 1
399
401
 
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
412
-
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
402
+ # reset the current media query scope
403
+ if in_media_block
404
+ current_media_queries = [:all]
405
+ in_media_block = false
416
406
  end
407
+ elsif !in_string && token.include?('{')
408
+ current_selectors.strip!
409
+ in_declarations += 1
410
+ else
411
+ # if we are in a selector, add the token to the current selectors
412
+ current_selectors << token
413
+
414
+ # mark this as the beginning of the selector unless we have already marked it
415
+ rule_start = offset.first if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
417
416
  end
418
417
  end
419
418
 
420
419
  # 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
420
+ return unless in_declarations > 0
421
+
422
+ unless options[:capture_offsets]
423
+ return add_rule!(current_selectors, current_declarations, current_media_queries)
427
424
  end
425
+
426
+ add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
428
427
  end
429
428
 
430
429
  # Load a remote CSS file.
@@ -437,7 +436,7 @@ module CssParser
437
436
  def load_uri!(uri, options = {}, deprecated = nil)
438
437
  uri = Addressable::URI.parse(uri) unless uri.respond_to? :scheme
439
438
 
440
- opts = {:base_uri => nil, :media_types => :all}
439
+ opts = {base_uri: nil, media_types: :all}
441
440
 
442
441
  if options.is_a? Hash
443
442
  opts.merge!(options)
@@ -457,14 +456,13 @@ module CssParser
457
456
  opts[:filename] = uri.to_s if opts[:capture_offsets]
458
457
 
459
458
  src, = read_remote_file(uri) # skip charset
460
- if src
461
- add_block!(src, opts)
462
- end
459
+
460
+ add_block!(src, opts) if src
463
461
  end
464
462
 
465
463
  # Load a local CSS file.
466
464
  def load_file!(file_name, options = {}, deprecated = nil)
467
- opts = {:base_dir => nil, :media_types => :all}
465
+ opts = {base_dir: nil, media_types: :all}
468
466
 
469
467
  if options.is_a? Hash
470
468
  opts.merge!(options)
@@ -487,7 +485,7 @@ module CssParser
487
485
 
488
486
  # Load a local CSS string.
489
487
  def load_string!(src, options = {}, deprecated = nil)
490
- opts = {:base_dir => nil, :media_types => :all}
488
+ opts = {base_dir: nil, media_types: :all}
491
489
 
492
490
  if options.is_a? Hash
493
491
  opts.merge!(options)
@@ -499,9 +497,8 @@ module CssParser
499
497
  add_block!(src, opts)
500
498
  end
501
499
 
502
-
503
-
504
500
  protected
501
+
505
502
  # Check that a path hasn't been loaded already
506
503
  #
507
504
  # Raises a CircularReferenceError exception if io_exceptions are on,
@@ -510,10 +507,11 @@ module CssParser
510
507
  path = path.to_s
511
508
  if @loaded_uris.include?(path)
512
509
  raise CircularReferenceError, "can't load #{path} more than once" if @options[:io_exceptions]
513
- return false
510
+
511
+ false
514
512
  else
515
513
  @loaded_uris << path
516
- return true
514
+ true
517
515
  end
518
516
  end
519
517
 
@@ -541,7 +539,7 @@ module CssParser
541
539
  utf8_block = ignore_pattern(utf8_block, STRIP_HTML_COMMENTS_RX, options)
542
540
 
543
541
  # Strip lines containing just whitespace
544
- utf8_block.gsub!(/^\s+$/, "") unless options[:capture_offsets]
542
+ utf8_block.gsub!(/^\s+$/, '') unless options[:capture_offsets]
545
543
 
546
544
  utf8_block
547
545
  end
@@ -577,11 +575,8 @@ module CssParser
577
575
  if uri.scheme == 'file'
578
576
  # local file
579
577
  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
578
+ path.gsub!(%r{^/}, '') if Gem.win_platform?
579
+ src = File.read(path, mode: 'rb')
585
580
  else
586
581
  # remote file
587
582
  if uri.scheme == 'https'
@@ -599,21 +594,22 @@ module CssParser
599
594
 
600
595
  if res.code.to_i >= 400
601
596
  @redirect_count = nil
602
- raise RemoteFileError.new(uri.to_s) if @options[:io_exceptions]
597
+ raise RemoteFileError, uri.to_s if @options[:io_exceptions]
598
+
603
599
  return '', nil
604
600
  elsif res.code.to_i >= 300 and res.code.to_i < 400
605
- if res['Location'] != nil
601
+ unless res['Location'].nil?
606
602
  return read_remote_file Addressable::URI.parse(Addressable::URI.escape(res['Location']))
607
603
  end
608
604
  end
609
605
 
610
606
  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)
607
+ when 'gzip'
608
+ io = Zlib::GzipReader.new(StringIO.new(res.body))
609
+ src = io.read
610
+ when 'deflate'
611
+ io = Zlib::Inflate.new
612
+ src = io.inflate(res.body)
617
613
  end
618
614
  end
619
615
 
@@ -627,15 +623,17 @@ module CssParser
627
623
  end
628
624
  rescue
629
625
  @redirect_count = nil
630
- raise RemoteFileError.new(uri.to_s)if @options[:io_exceptions]
626
+ raise RemoteFileError, uri.to_s if @options[:io_exceptions]
627
+
631
628
  return nil, nil
632
629
  end
633
630
 
634
631
  @redirect_count = nil
635
- return src, charset
632
+ [src, charset]
636
633
  end
637
634
 
638
635
  private
636
+
639
637
  # Save a folded declaration block to the internal cache.
640
638
  def save_folded_declaration(block_hash, folded_declaration) # :nodoc:
641
639
  @folded_declaration_cache[block_hash] = folded_declaration
@@ -643,7 +641,7 @@ module CssParser
643
641
 
644
642
  # Retrieve a folded declaration block from the internal cache.
645
643
  def get_folded_declaration(block_hash) # :nodoc:
646
- return @folded_declaration_cache[block_hash] ||= nil
644
+ @folded_declaration_cache[block_hash] ||= nil
647
645
  end
648
646
 
649
647
  def reset! # :nodoc:
@@ -657,14 +655,15 @@ module CssParser
657
655
  # passed hash
658
656
  def css_node_to_h(hash, key, val)
659
657
  hash[key.strip] = '' and return hash if val.nil?
658
+
660
659
  lines = val.split(';')
661
660
  nodes = {}
662
661
  lines.each do |line|
663
662
  parts = line.split(':', 2)
664
- if (parts[1] =~ /:/)
663
+ if parts[1] =~ /:/
665
664
  nodes[parts[0]] = css_node_to_h(hash, parts[0], parts[1])
666
665
  else
667
- nodes[parts[0].to_s.strip] =parts[1].to_s.strip
666
+ nodes[parts[0].to_s.strip] = parts[1].to_s.strip
668
667
  end
669
668
  end
670
669
  hash[key.strip] = nodes
@@ -1,65 +1,86 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CssParser
2
- def self.regex_possible_values *values
4
+ def self.regex_possible_values(*values)
3
5
  Regexp.new("([\s]*^)?(#{values.join('|')})([\s]*$)?", 'i')
4
6
  end
5
7
 
6
8
  # :stopdoc:
7
9
  # Base types
8
10
  RE_NL = Regexp.new('(\n|\r\n|\r|\f)')
9
- RE_NON_ASCII = Regexp.new('([\x00-\xFF])', Regexp::IGNORECASE, 'n') #[^\0-\177]
10
- RE_UNICODE = Regexp.new('(\\\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])*)', Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE, 'n')
11
+ RE_NON_ASCII = Regexp.new('([\x00-\xFF])', Regexp::IGNORECASE | Regexp::NOENCODING) # [^\0-\177]
12
+ RE_UNICODE = Regexp.new('(\\\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])*)', Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE | Regexp::NOENCODING)
11
13
  RE_ESCAPE = Regexp.union(RE_UNICODE, '|(\\\\[^\n\r\f0-9a-f])')
12
- RE_IDENT = Regexp.new("[\-]?([_a-z]|#{RE_NON_ASCII}|#{RE_ESCAPE})([_a-z0-9\-]|#{RE_NON_ASCII}|#{RE_ESCAPE})*", Regexp::IGNORECASE, 'n')
14
+ RE_IDENT = Regexp.new("[\-]?([_a-z]|#{RE_NON_ASCII}|#{RE_ESCAPE})([_a-z0-9\-]|#{RE_NON_ASCII}|#{RE_ESCAPE})*", Regexp::IGNORECASE | Regexp::NOENCODING)
13
15
 
14
16
  # General strings
15
- RE_STRING1 = Regexp.new('(\"(.[^\n\r\f\\"]*|\\\\' + RE_NL.to_s + '|' + RE_ESCAPE.to_s + ')*\")')
16
- RE_STRING2 = Regexp.new('(\'(.[^\n\r\f\\\']*|\\\\' + RE_NL.to_s + '|' + RE_ESCAPE.to_s + ')*\')')
17
+ RE_STRING1 = /("(.[^\n\r\f"]*|\\#{RE_NL}|#{RE_ESCAPE})*")/.freeze
18
+ RE_STRING2 = /('(.[^\n\r\f']*|\\#{RE_NL}|#{RE_ESCAPE})*')/.freeze
17
19
  RE_STRING = Regexp.union(RE_STRING1, RE_STRING2)
18
20
 
19
21
  RE_INHERIT = regex_possible_values 'inherit'
20
22
 
21
- RE_URI = Regexp.new('(url\([\s]*([\s]*' + RE_STRING.to_s + '[\s]*)[\s]*\))|(url\([\s]*([!#$%&*\-~]|' + RE_NON_ASCII.to_s + '|' + RE_ESCAPE.to_s + ')*[\s]*)\)', Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE, 'n')
22
- URI_RX = /url\(("([^"]*)"|'([^']*)'|([^)]*))\)/im
23
- RE_GRADIENT = /[-a-z]*gradient\([-a-z0-9 .,#%()]*\)/im
23
+ RE_URI = /(url\(\s*(\s*#{RE_STRING}\s*)\s*\))|(url\(\s*([!#$%&*\-~]|#{RE_NON_ASCII}|#{RE_ESCAPE})*\s*)\)/ixm.freeze
24
+ URI_RX = /url\(("([^"]*)"|'([^']*)'|([^)]*))\)/im.freeze
25
+ URI_RX_OR_NONE = Regexp.union(URI_RX, /none/i)
26
+ RE_GRADIENT = /[-a-z]*gradient\([-a-z0-9 .,#%()]*\)/im.freeze
24
27
 
25
28
  # Initial parsing
26
- RE_AT_IMPORT_RULE = /\@import\s+(url\()?["']?(.[^'"\s]*)["']?\)?([\w\s\,^\])]*)\)?;?/
29
+ RE_AT_IMPORT_RULE = /@import\s+(url\()?["']?(.[^'"\s]*)["']?\)?([\w\s,^\])]*)\)?;?/.freeze
27
30
 
28
31
  #--
29
- #RE_AT_MEDIA_RULE = Regexp.new('(\"(.[^\n\r\f\\"]*|\\\\' + RE_NL.to_s + '|' + RE_ESCAPE.to_s + ')*\")')
32
+ # RE_AT_MEDIA_RULE = Regexp.new('(\"(.[^\n\r\f\\"]*|\\\\' + RE_NL.to_s + '|' + RE_ESCAPE.to_s + ')*\")')
30
33
 
31
- #RE_AT_IMPORT_RULE = Regexp.new('@import[\s]*(' + RE_STRING.to_s + ')([\w\s\,]*)[;]?', Regexp::IGNORECASE) -- should handle url() even though it is not allowed
34
+ # RE_AT_IMPORT_RULE = Regexp.new('@import[\s]*(' + RE_STRING.to_s + ')([\w\s\,]*)[;]?', Regexp::IGNORECASE) -- should handle url() even though it is not allowed
32
35
  #++
33
- IMPORTANT_IN_PROPERTY_RX = /[\s]*!important\b[\s]*/i
36
+ IMPORTANT_IN_PROPERTY_RX = /\s*!important\b\s*/i.freeze
34
37
 
35
38
  RE_INSIDE_OUTSIDE = regex_possible_values 'inside', 'outside'
36
39
  RE_SCROLL_FIXED = regex_possible_values 'scroll', 'fixed'
37
40
  RE_REPEAT = regex_possible_values 'repeat(\-x|\-y)*|no\-repeat'
38
- RE_LIST_STYLE_TYPE = regex_possible_values 'disc', 'circle', 'square', 'decimal-leading-zero', 'decimal', 'lower-roman',
39
- 'upper-roman', 'lower-greek', 'lower-alpha', 'lower-latin', 'upper-alpha',
40
- 'upper-latin', 'hebrew', 'armenian', 'georgian', 'cjk-ideographic', 'hiragana',
41
- 'hira-gana-iroha', 'katakana-iroha', 'katakana', 'none'
41
+ RE_LIST_STYLE_TYPE = regex_possible_values(
42
+ 'disc', 'circle', 'square', 'decimal-leading-zero', 'decimal', 'lower-roman',
43
+ 'upper-roman', 'lower-greek', 'lower-alpha', 'lower-latin', 'upper-alpha',
44
+ 'upper-latin', 'hebrew', 'armenian', 'georgian', 'cjk-ideographic', 'hiragana',
45
+ 'hira-gana-iroha', 'katakana-iroha', 'katakana', 'none'
46
+ )
47
+ RE_IMAGE = Regexp.union(CssParser::URI_RX, CssParser::RE_GRADIENT, /none/i)
42
48
 
43
- STRIP_CSS_COMMENTS_RX = /\/\*.*?\*\//m
44
- STRIP_HTML_COMMENTS_RX = /\<\!\-\-|\-\-\>/m
49
+ STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
50
+ STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
45
51
 
46
52
  # Special units
47
- BOX_MODEL_UNITS_RX = /(auto|inherit|0|([\-]*([0-9]+|[0-9]*\.[0-9]+)(rem|vw|vh|vm|vmin|vmax|e[mx]+|px|[cm]+m|p[tc+]|in|\%)))([\s;]|\Z)/imx
53
+ BOX_MODEL_UNITS_RX = /(auto|inherit|0|(-*([0-9]+|[0-9]*\.[0-9]+)(rem|vw|vh|vm|vmin|vmax|e[mx]+|px|[cm]+m|p[tc+]|in|%)))([\s;]|\Z)/imx.freeze
48
54
  RE_LENGTH_OR_PERCENTAGE = Regexp.new('([\-]*(([0-9]*\.[0-9]+)|[0-9]+)(e[mx]+|px|[cm]+m|p[tc+]|in|\%))', Regexp::IGNORECASE)
49
55
  RE_BACKGROUND_POSITION = Regexp.new("((((#{RE_LENGTH_OR_PERCENTAGE})|left|center|right|top|bottom)[\s]*){1,2})", Regexp::IGNORECASE | Regexp::EXTENDED)
50
56
  RE_BACKGROUND_SIZE = Regexp.new("\\s*/\\s*((((#{RE_LENGTH_OR_PERCENTAGE})|auto|cover|contain|initial|inherit)[\\s]*){1,2})", Regexp::IGNORECASE | Regexp::EXTENDED)
51
- FONT_UNITS_RX = /(([x]+\-)*small|medium|large[r]*|auto|inherit|([0-9]+|[0-9]*\.[0-9]+)(e[mx]+|px|[cm]+m|p[tc+]|in|\%)*)/i
52
- RE_BORDER_STYLE = /([\s]*^)?(none|hidden|dotted|dashed|solid|double|dot-dash|dot-dot-dash|wave|groove|ridge|inset|outset)([\s]*$)?/imx
57
+ FONT_UNITS_RX = /((x+-)*small|medium|larger*|auto|inherit|([0-9]+|[0-9]*\.[0-9]+)(e[mx]+|px|[cm]+m|p[tc+]|in|%)*)/i.freeze
58
+ RE_BORDER_STYLE = /(\s*^)?(none|hidden|dotted|dashed|solid|double|dot-dash|dot-dot-dash|wave|groove|ridge|inset|outset)(\s*$)?/imx.freeze
53
59
  RE_BORDER_UNITS = Regexp.union(BOX_MODEL_UNITS_RX, /(thin|medium|thick)/i)
54
60
 
61
+ # Functions like calc, var, clamp, etc.
62
+ RE_FUNCTIONS = /
63
+ (
64
+ [a-z0-9-]+ # function name
65
+ )
66
+ (?>
67
+ \( # opening parenthesis
68
+ (?:
69
+ ([^()]+)
70
+ | # recursion via subexpression
71
+ \g<0>
72
+ )*
73
+ \) # closing parenthesis
74
+ )
75
+ /imx.freeze
55
76
 
56
77
  # Patterns for specificity calculations
57
- NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX_NC= /
58
- (?:\.[\w]+) # classes
78
+ NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX_NC = /
79
+ (?:\.\w+) # classes
59
80
  |
60
81
  \[(?:\w+) # attributes
61
82
  |
62
- (?:\:(?: # pseudo classes
83
+ (?::(?: # pseudo classes
63
84
  link|visited|active
64
85
  |hover|focus
65
86
  |lang
@@ -71,16 +92,16 @@ module CssParser
71
92
  |only-child|only-of-type
72
93
  |empty|contains
73
94
  ))
74
- /ix
95
+ /ix.freeze
75
96
  ELEMENTS_AND_PSEUDO_ELEMENTS_RX_NC = /
76
- (?:(?:^|[\s\+\>\~]+)[\w]+ # elements
97
+ (?:(?:^|[\s+>~]+)\w+ # elements
77
98
  |
78
- \:{1,2}(?: # pseudo-elements
99
+ :{1,2}(?: # pseudo-elements
79
100
  after|before
80
101
  |first-letter|first-line
81
102
  |selection
82
103
  )
83
- )/ix
104
+ )/ix.freeze
84
105
 
85
106
  # Colours
86
107
  NAMED_COLOURS = %w[
@@ -235,11 +256,11 @@ module CssParser
235
256
  transparent
236
257
  inherit
237
258
  currentColor
238
- ]
239
- RE_COLOUR_NUMERIC = /\b(hsl|rgb)\s*\(-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?\)/i
240
- RE_COLOUR_NUMERIC_ALPHA = /\b(hsla|rgba)\s*\(-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?\)/i
241
- RE_COLOUR_HEX = /\s*#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})\b/
242
- RE_COLOUR_NAMED = /\s*\b(#{NAMED_COLOURS.join('|')})\b/i
259
+ ].freeze
260
+ RE_COLOUR_NUMERIC = /\b(hsl|rgb)\s*\(-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?\)/i.freeze
261
+ RE_COLOUR_NUMERIC_ALPHA = /\b(hsla|rgba)\s*\(-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?,-?\s*-?\d+(\.\d+)?%?\s*%?\)/i.freeze
262
+ RE_COLOUR_HEX = /\s*#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})\b/.freeze
263
+ RE_COLOUR_NAMED = /\s*\b(#{NAMED_COLOURS.join('|')})\b/i.freeze
243
264
  RE_COLOUR = Regexp.union(RE_COLOUR_NUMERIC, RE_COLOUR_NUMERIC_ALPHA, RE_COLOUR_HEX, RE_COLOUR_NAMED)
244
265
  # :startdoc:
245
266
  end