css_parser 1.7.1 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
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