css_parser 1.14.0 → 2.2.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 +4 -4
- data/lib/css_parser/parser.rb +110 -56
- data/lib/css_parser/regexps.rb +9 -5
- data/lib/css_parser/rule_set.rb +90 -46
- data/lib/css_parser/version.rb +1 -1
- data/lib/css_parser.rb +7 -10
- metadata +5 -119
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed16a56359267ef6ea44fe0e45a76611e1f8365f65e9437c4c556fd4dfaa983f
|
|
4
|
+
data.tar.gz: 51136b897f4f511bd1d285138e366e8d66e090d7a8b5811afd34178e804bfd35
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 51fd7d3070a36f6a8d048f4eefcc6e5e3a85bef1f14dd14d751242bb35de0b2efb1a6278641980183e2a27e5c33ad84b9b39ed04e768f7439a76a015572f393c
|
|
7
|
+
data.tar.gz: f3116a5e15595a2c869507d43eb8c744eb19d26c850991d9eeeb4ff190956191c2136c5add0408402177fa865cf18903fc917dd43c6932ce32ed5cb8843aa992
|
data/lib/css_parser/parser.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'strscan'
|
|
4
|
+
|
|
3
5
|
module CssParser
|
|
4
6
|
# Exception class used for any errors encountered while downloading remote files.
|
|
5
7
|
class RemoteFileError < IOError; end
|
|
@@ -16,8 +18,8 @@ module CssParser
|
|
|
16
18
|
# [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
|
|
17
19
|
# [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
|
|
18
20
|
class Parser
|
|
19
|
-
USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)"
|
|
20
|
-
|
|
21
|
+
USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)".freeze
|
|
22
|
+
RULESET_TOKENIZER_RX = /\s+|\\{2,}|\\?[{}\s"]|[()]|.[^\s"{}()\\]*/.freeze
|
|
21
23
|
STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
|
|
22
24
|
STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
|
|
23
25
|
|
|
@@ -29,18 +31,15 @@ module CssParser
|
|
|
29
31
|
# Array of CSS files that have been loaded.
|
|
30
32
|
attr_reader :loaded_uris
|
|
31
33
|
|
|
32
|
-
#--
|
|
33
|
-
# Class variable? see http://www.oreillynet.com/ruby/blog/2007/01/nubygems_dont_use_class_variab_1.html
|
|
34
|
-
#++
|
|
35
|
-
@folded_declaration_cache = {}
|
|
36
|
-
class << self; attr_reader :folded_declaration_cache; end
|
|
37
|
-
|
|
38
34
|
def initialize(options = {})
|
|
39
|
-
@options = {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
@options = {
|
|
36
|
+
absolute_paths: false,
|
|
37
|
+
import: true,
|
|
38
|
+
io_exceptions: true,
|
|
39
|
+
rule_set_exceptions: true,
|
|
40
|
+
capture_offsets: false,
|
|
41
|
+
user_agent: USER_AGENT
|
|
42
|
+
}.merge(options)
|
|
44
43
|
|
|
45
44
|
# array of RuleSets
|
|
46
45
|
@rules = []
|
|
@@ -131,14 +130,14 @@ module CssParser
|
|
|
131
130
|
block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
|
|
132
131
|
media_types = []
|
|
133
132
|
if (media_string = import_rule[-1])
|
|
134
|
-
media_string.split(
|
|
133
|
+
media_string.split(',').each do |t|
|
|
135
134
|
media_types << CssParser.sanitize_media_query(t) unless t.empty?
|
|
136
135
|
end
|
|
137
136
|
else
|
|
138
137
|
media_types = [:all]
|
|
139
138
|
end
|
|
140
139
|
|
|
141
|
-
next unless options[:only_media_types].include?(:all) or media_types.empty? or
|
|
140
|
+
next unless options[:only_media_types].include?(:all) or media_types.empty? or media_types.intersect?(options[:only_media_types])
|
|
142
141
|
|
|
143
142
|
import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
|
|
144
143
|
|
|
@@ -162,14 +161,44 @@ module CssParser
|
|
|
162
161
|
parse_block_into_rule_sets!(block, options)
|
|
163
162
|
end
|
|
164
163
|
|
|
165
|
-
# Add a CSS rule by setting the +selectors+, +declarations+
|
|
164
|
+
# Add a CSS rule by setting the +selectors+, +declarations+
|
|
165
|
+
# and +media_types+. Optional pass +filename+ , +offset+ for source
|
|
166
|
+
# reference too.
|
|
166
167
|
#
|
|
167
|
-
# +media_types+ can be a symbol or an array of symbols.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
# +media_types+ can be a symbol or an array of symbols. default to :all
|
|
169
|
+
# optional fields for source location for source location
|
|
170
|
+
# +filename+ can be a string or uri pointing to the file or url location.
|
|
171
|
+
# +offset+ should be Range object representing the start and end byte locations where the rule was found in the file.
|
|
172
|
+
def add_rule!(*args, selectors: nil, block: nil, filename: nil, offset: nil, media_types: :all) # rubocop:disable Metrics/ParameterLists
|
|
173
|
+
if args.any?
|
|
174
|
+
media_types = nil
|
|
175
|
+
if selectors || block || filename || offset || media_types
|
|
176
|
+
raise ArgumentError, "don't mix positional and keyword arguments arguments"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
warn '[DEPRECATION] `add_rule!` with positional arguments is deprecated. ' \
|
|
180
|
+
'Please use keyword arguments instead.', uplevel: 1
|
|
181
|
+
|
|
182
|
+
case args.length
|
|
183
|
+
when 2
|
|
184
|
+
selectors, block = args
|
|
185
|
+
when 3
|
|
186
|
+
selectors, block, media_types = args
|
|
187
|
+
else
|
|
188
|
+
raise ArgumentError
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
begin
|
|
193
|
+
rule_set = RuleSet.new(
|
|
194
|
+
selectors: selectors, block: block,
|
|
195
|
+
offset: offset, filename: filename
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
add_rule_set!(rule_set, media_types)
|
|
199
|
+
rescue ArgumentError => e
|
|
200
|
+
raise e if @options[:rule_set_exceptions]
|
|
201
|
+
end
|
|
173
202
|
end
|
|
174
203
|
|
|
175
204
|
# Add a CSS rule by setting the +selectors+, +declarations+, +filename+, +offset+ and +media_types+.
|
|
@@ -178,8 +207,11 @@ module CssParser
|
|
|
178
207
|
# +offset+ should be Range object representing the start and end byte locations where the rule was found in the file.
|
|
179
208
|
# +media_types+ can be a symbol or an array of symbols.
|
|
180
209
|
def add_rule_with_offsets!(selectors, declarations, filename, offset, media_types = :all)
|
|
181
|
-
|
|
182
|
-
|
|
210
|
+
warn '[DEPRECATION] `add_rule_with_offsets!` is deprecated. Please use `add_rule!` instead.', uplevel: 1
|
|
211
|
+
add_rule!(
|
|
212
|
+
selectors: selectors, block: declarations, media_types: media_types,
|
|
213
|
+
filename: filename, offset: offset
|
|
214
|
+
)
|
|
183
215
|
end
|
|
184
216
|
|
|
185
217
|
# Add a CssParser RuleSet object.
|
|
@@ -321,17 +353,21 @@ module CssParser
|
|
|
321
353
|
in_at_media_rule = false
|
|
322
354
|
in_media_block = false
|
|
323
355
|
|
|
324
|
-
current_selectors =
|
|
325
|
-
current_media_query =
|
|
326
|
-
current_declarations =
|
|
356
|
+
current_selectors = +''
|
|
357
|
+
current_media_query = +''
|
|
358
|
+
current_declarations = +''
|
|
327
359
|
|
|
328
360
|
# once we are in a rule, we will use this to store where we started if we are capturing offsets
|
|
329
361
|
rule_start = nil
|
|
330
|
-
|
|
362
|
+
start_offset = nil
|
|
363
|
+
end_offset = nil
|
|
331
364
|
|
|
332
|
-
|
|
365
|
+
scanner = StringScanner.new(block)
|
|
366
|
+
until scanner.eos?
|
|
333
367
|
# save the regex offset so that we know where in the file we are
|
|
334
|
-
|
|
368
|
+
start_offset = scanner.pos
|
|
369
|
+
token = scanner.scan(RULESET_TOKENIZER_RX)
|
|
370
|
+
end_offset = scanner.pos
|
|
335
371
|
|
|
336
372
|
if token.start_with?('"') # found un-escaped double quote
|
|
337
373
|
in_string = !in_string
|
|
@@ -358,20 +394,24 @@ module CssParser
|
|
|
358
394
|
current_declarations.strip!
|
|
359
395
|
|
|
360
396
|
unless current_declarations.empty?
|
|
397
|
+
add_rule_options = {
|
|
398
|
+
selectors: current_selectors, block: current_declarations,
|
|
399
|
+
media_types: current_media_queries
|
|
400
|
+
}
|
|
361
401
|
if options[:capture_offsets]
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
add_rule!(current_selectors, current_declarations, current_media_queries)
|
|
402
|
+
add_rule_options[:filename] = options[:filename]
|
|
403
|
+
add_rule_options[:offset] = rule_start..end_offset
|
|
365
404
|
end
|
|
405
|
+
add_rule!(**add_rule_options)
|
|
366
406
|
end
|
|
367
407
|
|
|
368
|
-
current_selectors =
|
|
369
|
-
current_declarations =
|
|
408
|
+
current_selectors = +''
|
|
409
|
+
current_declarations = +''
|
|
370
410
|
|
|
371
411
|
# restart our search for selectors and declarations
|
|
372
412
|
rule_start = nil if options[:capture_offsets]
|
|
373
413
|
end
|
|
374
|
-
elsif
|
|
414
|
+
elsif /@media/i.match?(token)
|
|
375
415
|
# found '@media', reset current media_types
|
|
376
416
|
in_at_media_rule = true
|
|
377
417
|
current_media_queries = []
|
|
@@ -381,19 +421,28 @@ module CssParser
|
|
|
381
421
|
in_at_media_rule = false
|
|
382
422
|
in_media_block = true
|
|
383
423
|
current_media_queries << CssParser.sanitize_media_query(current_media_query)
|
|
384
|
-
current_media_query =
|
|
424
|
+
current_media_query = +''
|
|
385
425
|
elsif token.include?(',')
|
|
386
426
|
# new media query begins
|
|
387
427
|
token.tr!(',', ' ')
|
|
388
428
|
token.strip!
|
|
389
429
|
current_media_query << token << ' '
|
|
390
430
|
current_media_queries << CssParser.sanitize_media_query(current_media_query)
|
|
391
|
-
current_media_query =
|
|
431
|
+
current_media_query = +''
|
|
392
432
|
else
|
|
393
433
|
token.strip!
|
|
394
|
-
|
|
434
|
+
# special-case the ( and ) tokens to remove inner-whitespace
|
|
435
|
+
# (eg we'd prefer '(width: 500px)' to '( width: 500px )' )
|
|
436
|
+
case token
|
|
437
|
+
when '('
|
|
438
|
+
current_media_query << token
|
|
439
|
+
when ')'
|
|
440
|
+
current_media_query.sub!(/ ?$/, token)
|
|
441
|
+
else
|
|
442
|
+
current_media_query << token << ' '
|
|
443
|
+
end
|
|
395
444
|
end
|
|
396
|
-
elsif in_charset or
|
|
445
|
+
elsif in_charset or /@charset/i.match?(token)
|
|
397
446
|
# iterate until we are out of the charset declaration
|
|
398
447
|
in_charset = !token.include?(';')
|
|
399
448
|
elsif !in_string && token.include?('}')
|
|
@@ -412,18 +461,22 @@ module CssParser
|
|
|
412
461
|
current_selectors << token
|
|
413
462
|
|
|
414
463
|
# mark this as the beginning of the selector unless we have already marked it
|
|
415
|
-
rule_start =
|
|
464
|
+
rule_start = start_offset if options[:capture_offsets] && rule_start.nil? && /^[^\s]+$/.match?(token)
|
|
416
465
|
end
|
|
417
466
|
end
|
|
418
467
|
|
|
419
468
|
# check for unclosed braces
|
|
420
469
|
return unless in_declarations > 0
|
|
421
470
|
|
|
422
|
-
|
|
423
|
-
|
|
471
|
+
add_rule_options = {
|
|
472
|
+
selectors: current_selectors, block: current_declarations,
|
|
473
|
+
media_types: current_media_queries
|
|
474
|
+
}
|
|
475
|
+
if options[:capture_offsets]
|
|
476
|
+
add_rule_options[:filename] = options[:filename]
|
|
477
|
+
add_rule_options[:offset] = rule_start..end_offset
|
|
424
478
|
end
|
|
425
|
-
|
|
426
|
-
add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
|
|
479
|
+
add_rule!(**add_rule_options)
|
|
427
480
|
end
|
|
428
481
|
|
|
429
482
|
# Load a remote CSS file.
|
|
@@ -441,6 +494,8 @@ module CssParser
|
|
|
441
494
|
if options.is_a? Hash
|
|
442
495
|
opts.merge!(options)
|
|
443
496
|
else
|
|
497
|
+
warn '[DEPRECATION] `load_uri!` with positional arguments is deprecated. ' \
|
|
498
|
+
'Please use keyword arguments instead.', uplevel: 1
|
|
444
499
|
opts[:base_uri] = options if options.is_a? String
|
|
445
500
|
opts[:media_types] = deprecated if deprecated
|
|
446
501
|
end
|
|
@@ -467,6 +522,8 @@ module CssParser
|
|
|
467
522
|
if options.is_a? Hash
|
|
468
523
|
opts.merge!(options)
|
|
469
524
|
else
|
|
525
|
+
warn '[DEPRECATION] `load_file!` with positional arguments is deprecated. ' \
|
|
526
|
+
'Please use keyword arguments instead.', uplevel: 1
|
|
470
527
|
opts[:base_dir] = options if options.is_a? String
|
|
471
528
|
opts[:media_types] = deprecated if deprecated
|
|
472
529
|
end
|
|
@@ -475,7 +532,7 @@ module CssParser
|
|
|
475
532
|
return unless File.readable?(file_name)
|
|
476
533
|
return unless circular_reference_check(file_name)
|
|
477
534
|
|
|
478
|
-
src =
|
|
535
|
+
src = File.read(file_name)
|
|
479
536
|
|
|
480
537
|
opts[:filename] = file_name if opts[:capture_offsets]
|
|
481
538
|
opts[:base_dir] = File.dirname(file_name)
|
|
@@ -490,6 +547,8 @@ module CssParser
|
|
|
490
547
|
if options.is_a? Hash
|
|
491
548
|
opts.merge!(options)
|
|
492
549
|
else
|
|
550
|
+
warn '[DEPRECATION] `load_file!` with positional arguments is deprecated. ' \
|
|
551
|
+
'Please use keyword arguments instead.', uplevel: 1
|
|
493
552
|
opts[:base_dir] = options if options.is_a? String
|
|
494
553
|
opts[:media_types] = deprecated if deprecated
|
|
495
554
|
end
|
|
@@ -503,7 +562,8 @@ module CssParser
|
|
|
503
562
|
#
|
|
504
563
|
# Raises a CircularReferenceError exception if io_exceptions are on,
|
|
505
564
|
# otherwise returns true/false.
|
|
506
|
-
|
|
565
|
+
# TODO: fix rubocop
|
|
566
|
+
def circular_reference_check(path) # rubocop:disable Naming/PredicateMethod
|
|
507
567
|
path = path.to_s
|
|
508
568
|
if @loaded_uris.include?(path)
|
|
509
569
|
raise CircularReferenceError, "can't load #{path} more than once" if @options[:io_exceptions]
|
|
@@ -583,12 +643,11 @@ module CssParser
|
|
|
583
643
|
uri.port = 443 unless uri.port
|
|
584
644
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
585
645
|
http.use_ssl = true
|
|
586
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
587
646
|
else
|
|
588
647
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
589
648
|
end
|
|
590
649
|
|
|
591
|
-
res = http.get(uri.request_uri, {'User-Agent' =>
|
|
650
|
+
res = http.get(uri.request_uri, {'User-Agent' => @options[:user_agent], 'Accept-Encoding' => 'gzip'})
|
|
592
651
|
src = res.body
|
|
593
652
|
charset = res.respond_to?(:charset) ? res.encoding : 'utf-8'
|
|
594
653
|
|
|
@@ -614,12 +673,7 @@ module CssParser
|
|
|
614
673
|
end
|
|
615
674
|
|
|
616
675
|
if charset
|
|
617
|
-
|
|
618
|
-
src.encode!('UTF-8', charset)
|
|
619
|
-
else
|
|
620
|
-
ic = Iconv.new('UTF-8//IGNORE', charset)
|
|
621
|
-
src = ic.iconv(src)
|
|
622
|
-
end
|
|
676
|
+
src.encode!('UTF-8', charset)
|
|
623
677
|
end
|
|
624
678
|
rescue
|
|
625
679
|
@redirect_count = nil
|
|
@@ -660,7 +714,7 @@ module CssParser
|
|
|
660
714
|
nodes = {}
|
|
661
715
|
lines.each do |line|
|
|
662
716
|
parts = line.split(':', 2)
|
|
663
|
-
if parts[1]
|
|
717
|
+
if parts[1].include?(':')
|
|
664
718
|
nodes[parts[0]] = css_node_to_h(hash, parts[0], parts[1])
|
|
665
719
|
else
|
|
666
720
|
nodes[parts[0].to_s.strip] = parts[1].to_s.strip
|
data/lib/css_parser/regexps.rb
CHANGED
|
@@ -11,7 +11,7 @@ module CssParser
|
|
|
11
11
|
RE_NON_ASCII = Regexp.new('([\x00-\xFF])', Regexp::IGNORECASE | Regexp::NOENCODING) # [^\0-\177]
|
|
12
12
|
RE_UNICODE = Regexp.new('(\\\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])*)', Regexp::IGNORECASE | Regexp::EXTENDED | Regexp::MULTILINE | Regexp::NOENCODING)
|
|
13
13
|
RE_ESCAPE = Regexp.union(RE_UNICODE, '|(\\\\[^\n\r\f0-9a-f])')
|
|
14
|
-
RE_IDENT = Regexp.new("[
|
|
14
|
+
RE_IDENT = Regexp.new("[-]?([_a-z]|#{RE_NON_ASCII}|#{RE_ESCAPE})([_a-z0-9-]|#{RE_NON_ASCII}|#{RE_ESCAPE})*", Regexp::IGNORECASE | Regexp::NOENCODING)
|
|
15
15
|
|
|
16
16
|
# General strings
|
|
17
17
|
RE_STRING1 = /("(.[^\n\r\f"]*|\\#{RE_NL}|#{RE_ESCAPE})*")/.freeze
|
|
@@ -52,8 +52,10 @@ module CssParser
|
|
|
52
52
|
# Special units
|
|
53
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
|
|
54
54
|
RE_LENGTH_OR_PERCENTAGE = Regexp.new('([\-]*(([0-9]*\.[0-9]+)|[0-9]+)(e[mx]+|px|[cm]+m|p[tc+]|in|\%))', Regexp::IGNORECASE)
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
RE_SINGLE_BACKGROUND_POSITION = /#{RE_LENGTH_OR_PERCENTAGE}|left|center|right|top|bottom/i.freeze
|
|
56
|
+
RE_SINGLE_BACKGROUND_SIZE = /#{RE_LENGTH_OR_PERCENTAGE}|auto|cover|contain|initial|inherit/i.freeze
|
|
57
|
+
RE_BACKGROUND_POSITION = /#{RE_SINGLE_BACKGROUND_POSITION}\s+#{RE_SINGLE_BACKGROUND_POSITION}|#{RE_SINGLE_BACKGROUND_POSITION}/.freeze
|
|
58
|
+
RE_BACKGROUND_SIZE = %r{\s*/\s*(#{RE_SINGLE_BACKGROUND_SIZE}\s+#{RE_SINGLE_BACKGROUND_SIZE}|#{RE_SINGLE_BACKGROUND_SIZE})}.freeze
|
|
57
59
|
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
60
|
RE_BORDER_STYLE = /(\s*^)?(none|hidden|dotted|dashed|solid|double|dot-dash|dot-dot-dash|wave|groove|ridge|inset|outset)(\s*$)?/imx.freeze
|
|
59
61
|
RE_BORDER_UNITS = Regexp.union(BOX_MODEL_UNITS_RX, /(thin|medium|thick)/i)
|
|
@@ -257,8 +259,10 @@ module CssParser
|
|
|
257
259
|
inherit
|
|
258
260
|
currentColor
|
|
259
261
|
].freeze
|
|
260
|
-
|
|
261
|
-
|
|
262
|
+
# CSS <number> allows the integer part to be omitted (e.g. `.1`), per CSS Values & Units.
|
|
263
|
+
# `(?:\d*\.)?\d+` accepts `1`, `1.5`, and `.5` while still rejecting bare `1.`.
|
|
264
|
+
RE_COLOUR_NUMERIC = /\b(hsl|rgb)\s*\(-?\s*-?(?:\d*\.)?\d+%?\s*%?,-?\s*-?(?:\d*\.)?\d+%?\s*%?,-?\s*-?(?:\d*\.)?\d+%?\s*%?\)/i.freeze
|
|
265
|
+
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
266
|
RE_COLOUR_HEX = /\s*#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})\b/.freeze
|
|
263
267
|
RE_COLOUR_NAMED = /\s*\b(#{NAMED_COLOURS.join('|')})\b/i.freeze
|
|
264
268
|
RE_COLOUR = Regexp.union(RE_COLOUR_NUMERIC, RE_COLOUR_NUMERIC_ALPHA, RE_COLOUR_HEX, RE_COLOUR_NAMED)
|
data/lib/css_parser/rule_set.rb
CHANGED
|
@@ -11,8 +11,10 @@ module CssParser
|
|
|
11
11
|
BACKGROUND_PROPERTIES = ['background-color', 'background-image', 'background-repeat', 'background-position', 'background-size', 'background-attachment'].freeze
|
|
12
12
|
LIST_STYLE_PROPERTIES = ['list-style-type', 'list-style-position', 'list-style-image'].freeze
|
|
13
13
|
FONT_STYLE_PROPERTIES = ['font-style', 'font-variant', 'font-weight', 'font-size', 'line-height', 'font-family'].freeze
|
|
14
|
+
FONT_WEIGHT_PROPERTIES = ['font-style', 'font-weight', 'font-variant'].freeze
|
|
14
15
|
BORDER_STYLE_PROPERTIES = ['border-width', 'border-style', 'border-color'].freeze
|
|
15
16
|
BORDER_PROPERTIES = ['border', 'border-left', 'border-right', 'border-top', 'border-bottom'].freeze
|
|
17
|
+
DIMENSION_DIRECTIONS = [:top, :right, :bottom, :left].freeze
|
|
16
18
|
|
|
17
19
|
NUMBER_OF_DIMENSIONS = 4
|
|
18
20
|
|
|
@@ -26,6 +28,12 @@ module CssParser
|
|
|
26
28
|
|
|
27
29
|
WHITESPACE_REPLACEMENT = '___SPACE___'
|
|
28
30
|
|
|
31
|
+
# Tokens for parse_declarations!
|
|
32
|
+
COLON = ':'.freeze
|
|
33
|
+
SEMICOLON = ';'.freeze
|
|
34
|
+
LPAREN = '('.freeze
|
|
35
|
+
RPAREN = ')'.freeze
|
|
36
|
+
IMPORTANT = '!important'.freeze
|
|
29
37
|
class Declarations
|
|
30
38
|
class Value
|
|
31
39
|
attr_reader :value
|
|
@@ -58,7 +66,7 @@ module CssParser
|
|
|
58
66
|
|
|
59
67
|
extend Forwardable
|
|
60
68
|
|
|
61
|
-
def_delegators :declarations, :each
|
|
69
|
+
def_delegators :declarations, :each, :each_value
|
|
62
70
|
|
|
63
71
|
def initialize(declarations = {})
|
|
64
72
|
self.declarations = {}
|
|
@@ -81,17 +89,20 @@ module CssParser
|
|
|
81
89
|
# puts declarations['margin']
|
|
82
90
|
# => #<CssParser::RuleSet::Declarations::Value:0x00000000030c1838 @important=true, @order=2, @value="0px auto">
|
|
83
91
|
#
|
|
84
|
-
# If the property already exists its value will be over-written
|
|
92
|
+
# If the property already exists its value will be over-written unless it was !important and the new value
|
|
93
|
+
# is not !important.
|
|
85
94
|
# If the value is empty - property will be deleted
|
|
86
95
|
def []=(property, value)
|
|
87
96
|
property = normalize_property(property)
|
|
97
|
+
currently_important = declarations[property]&.important
|
|
88
98
|
|
|
89
|
-
if value.is_a?(Value)
|
|
99
|
+
if value.is_a?(Value) && (!currently_important || value.important)
|
|
90
100
|
declarations[property] = value
|
|
91
101
|
elsif value.to_s.strip.empty?
|
|
92
102
|
delete property
|
|
93
103
|
else
|
|
94
|
-
|
|
104
|
+
value = Value.new(value)
|
|
105
|
+
declarations[property] = value if !currently_important || value.important
|
|
95
106
|
end
|
|
96
107
|
rescue ArgumentError => e
|
|
97
108
|
raise e.exception, "#{property} #{e.message}"
|
|
@@ -142,7 +153,7 @@ module CssParser
|
|
|
142
153
|
|
|
143
154
|
if preserve_importance
|
|
144
155
|
importance = get_value(property).important
|
|
145
|
-
replacement_declarations.
|
|
156
|
+
replacement_declarations.each_value { |value| value.important = importance }
|
|
146
157
|
end
|
|
147
158
|
|
|
148
159
|
replacement_keys = declarations.keys
|
|
@@ -190,7 +201,7 @@ module CssParser
|
|
|
190
201
|
end
|
|
191
202
|
|
|
192
203
|
def to_s(options = {})
|
|
193
|
-
str = declarations.reduce(
|
|
204
|
+
str = declarations.reduce(+'') do |memo, (prop, value)|
|
|
194
205
|
importance = options[:force_important] || value.important ? ' !important' : ''
|
|
195
206
|
memo << "#{prop}: #{value.value}#{importance}; "
|
|
196
207
|
end
|
|
@@ -223,6 +234,12 @@ module CssParser
|
|
|
223
234
|
|
|
224
235
|
extend Forwardable
|
|
225
236
|
|
|
237
|
+
# optional field for storing source reference
|
|
238
|
+
# File offset range
|
|
239
|
+
attr_reader :offset
|
|
240
|
+
# the local or remote location
|
|
241
|
+
attr_accessor :filename
|
|
242
|
+
|
|
226
243
|
# Array of selector strings.
|
|
227
244
|
attr_reader :selectors
|
|
228
245
|
|
|
@@ -237,9 +254,38 @@ module CssParser
|
|
|
237
254
|
alias []= add_declaration!
|
|
238
255
|
alias remove_declaration! delete
|
|
239
256
|
|
|
240
|
-
def initialize(selectors, block,
|
|
257
|
+
def initialize(*args, selectors: nil, block: nil, offset: nil, filename: nil, specificity: nil) # rubocop:disable Metrics/ParameterLists
|
|
258
|
+
if args.any?
|
|
259
|
+
if selectors || block || offset || filename || specificity
|
|
260
|
+
raise ArgumentError, "don't mix positional and keyword arguments"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
warn '[DEPRECATION] positional arguments are deprecated use keyword instead.', uplevel: 1
|
|
264
|
+
|
|
265
|
+
case args.length
|
|
266
|
+
when 2
|
|
267
|
+
selectors, block = args
|
|
268
|
+
when 3
|
|
269
|
+
selectors, block, specificity = args
|
|
270
|
+
when 4
|
|
271
|
+
filename, offset, selectors, block = args
|
|
272
|
+
when 5
|
|
273
|
+
filename, offset, selectors, block, specificity = args
|
|
274
|
+
else
|
|
275
|
+
raise ArgumentError
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
241
279
|
@selectors = []
|
|
242
280
|
@specificity = specificity
|
|
281
|
+
|
|
282
|
+
unless offset.nil? == filename.nil?
|
|
283
|
+
raise ArgumentError, 'require both offset and filename or no offset and no filename'
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
@offset = offset
|
|
287
|
+
@filename = filename
|
|
288
|
+
|
|
243
289
|
parse_selectors!(selectors) if selectors
|
|
244
290
|
parse_declarations!(block)
|
|
245
291
|
end
|
|
@@ -308,7 +354,7 @@ module CssParser
|
|
|
308
354
|
|
|
309
355
|
replacement =
|
|
310
356
|
if value.match(CssParser::RE_INHERIT)
|
|
311
|
-
BACKGROUND_PROPERTIES.
|
|
357
|
+
BACKGROUND_PROPERTIES.to_h { |key| [key, 'inherit'] }
|
|
312
358
|
else
|
|
313
359
|
{
|
|
314
360
|
'background-image' => value.slice!(CssParser::RE_IMAGE),
|
|
@@ -414,17 +460,17 @@ module CssParser
|
|
|
414
460
|
else
|
|
415
461
|
font_props['font-family'] = m
|
|
416
462
|
end
|
|
417
|
-
elsif
|
|
418
|
-
|
|
463
|
+
elsif /normal|inherit/i.match?(m)
|
|
464
|
+
FONT_WEIGHT_PROPERTIES.each do |font_prop|
|
|
419
465
|
font_props[font_prop] ||= m
|
|
420
466
|
end
|
|
421
|
-
elsif
|
|
467
|
+
elsif /italic|oblique/i.match?(m)
|
|
422
468
|
font_props['font-style'] = m
|
|
423
|
-
elsif
|
|
469
|
+
elsif /small-caps/i.match?(m)
|
|
424
470
|
font_props['font-variant'] = m
|
|
425
|
-
elsif
|
|
471
|
+
elsif /[1-9]00$|bold|bolder|lighter/i.match?(m)
|
|
426
472
|
font_props['font-weight'] = m
|
|
427
|
-
elsif
|
|
473
|
+
elsif CssParser::FONT_UNITS_RX.match?(m)
|
|
428
474
|
if m.include?('/')
|
|
429
475
|
font_props['font-size'], font_props['line-height'] = m.split('/', 2)
|
|
430
476
|
else
|
|
@@ -447,8 +493,8 @@ module CssParser
|
|
|
447
493
|
value = declaration.value.dup
|
|
448
494
|
|
|
449
495
|
replacement =
|
|
450
|
-
if
|
|
451
|
-
LIST_STYLE_PROPERTIES.
|
|
496
|
+
if CssParser::RE_INHERIT.match?(value)
|
|
497
|
+
LIST_STYLE_PROPERTIES.to_h { |key| [key, 'inherit'] }
|
|
452
498
|
else
|
|
453
499
|
{
|
|
454
500
|
'list-style-type' => value.slice!(CssParser::RE_LIST_STYLE_TYPE),
|
|
@@ -513,15 +559,15 @@ module CssParser
|
|
|
513
559
|
#
|
|
514
560
|
# TODO: this is extremely similar to create_background_shorthand! and should be combined
|
|
515
561
|
def create_border_shorthand! # :nodoc:
|
|
516
|
-
values = BORDER_STYLE_PROPERTIES.
|
|
562
|
+
values = BORDER_STYLE_PROPERTIES.filter_map do |property|
|
|
517
563
|
next unless (declaration = declarations[property])
|
|
518
564
|
next if declaration.important
|
|
519
565
|
# can't merge if any value contains a space (i.e. has multiple values)
|
|
520
566
|
# we temporarily remove any spaces after commas for the check (inside rgba, etc...)
|
|
521
|
-
next if declaration.value.gsub(/,\s/, ',').strip
|
|
567
|
+
next if /\s/.match?(declaration.value.gsub(/,\s/, ',').strip)
|
|
522
568
|
|
|
523
569
|
declaration.value
|
|
524
|
-
end
|
|
570
|
+
end
|
|
525
571
|
|
|
526
572
|
return if values.size != BORDER_STYLE_PROPERTIES.size
|
|
527
573
|
|
|
@@ -538,7 +584,7 @@ module CssParser
|
|
|
538
584
|
return if declarations.size < NUMBER_OF_DIMENSIONS
|
|
539
585
|
|
|
540
586
|
DIMENSIONS.each do |property, dimensions|
|
|
541
|
-
values =
|
|
587
|
+
values = DIMENSION_DIRECTIONS.each_with_index.with_object({}) do |(side, index), result|
|
|
542
588
|
next unless (declaration = declarations[dimensions[index]])
|
|
543
589
|
|
|
544
590
|
result[side] = declaration.value
|
|
@@ -561,7 +607,7 @@ module CssParser
|
|
|
561
607
|
def create_font_shorthand! # :nodoc:
|
|
562
608
|
return unless FONT_STYLE_PROPERTIES.all? { |prop| declarations.key?(prop) }
|
|
563
609
|
|
|
564
|
-
new_value =
|
|
610
|
+
new_value = +''
|
|
565
611
|
['font-style', 'font-variant', 'font-weight'].each do |property|
|
|
566
612
|
unless declarations[property].value == 'normal'
|
|
567
613
|
new_value << declarations[property].value << ' '
|
|
@@ -598,7 +644,7 @@ module CssParser
|
|
|
598
644
|
return [:top] if values.values.uniq.count == 1
|
|
599
645
|
|
|
600
646
|
# `/* top | right | bottom | left */`
|
|
601
|
-
return
|
|
647
|
+
return DIMENSION_DIRECTIONS if values[:left] != values[:right]
|
|
602
648
|
|
|
603
649
|
# Vertical are the same & horizontal are the same, `/* vertical | horizontal */`
|
|
604
650
|
return [:top, :left] if values[:top] == values[:bottom]
|
|
@@ -612,20 +658,32 @@ module CssParser
|
|
|
612
658
|
return unless block
|
|
613
659
|
|
|
614
660
|
continuation = nil
|
|
615
|
-
block.split(
|
|
616
|
-
decs = (continuation ? continuation
|
|
617
|
-
if decs
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
value = matches[2]
|
|
623
|
-
add_declaration!(property, value)
|
|
624
|
-
continuation = nil
|
|
661
|
+
block.split(SEMICOLON) do |decs|
|
|
662
|
+
decs = (continuation ? "#{continuation};#{decs}" : decs)
|
|
663
|
+
if unmatched_open_parenthesis?(decs)
|
|
664
|
+
# Semicolon happened within parenthesis, so it is a part of the value
|
|
665
|
+
# the rest of the value is in the next segment
|
|
666
|
+
continuation = decs
|
|
667
|
+
next
|
|
625
668
|
end
|
|
669
|
+
|
|
670
|
+
next unless (colon = decs.index(COLON))
|
|
671
|
+
|
|
672
|
+
property = decs[0, colon]
|
|
673
|
+
value = decs[(colon + 1)..]
|
|
674
|
+
property.strip!
|
|
675
|
+
value.strip!
|
|
676
|
+
next if property.empty? || value.empty? || value.casecmp?(IMPORTANT)
|
|
677
|
+
|
|
678
|
+
add_declaration!(property, value)
|
|
679
|
+
continuation = nil
|
|
626
680
|
end
|
|
627
681
|
end
|
|
628
682
|
|
|
683
|
+
def unmatched_open_parenthesis?(declarations)
|
|
684
|
+
(lparen_index = declarations.index(LPAREN)) && !declarations.index(RPAREN, lparen_index)
|
|
685
|
+
end
|
|
686
|
+
|
|
629
687
|
#--
|
|
630
688
|
# TODO: way too simplistic
|
|
631
689
|
#++
|
|
@@ -650,18 +708,4 @@ module CssParser
|
|
|
650
708
|
end
|
|
651
709
|
end
|
|
652
710
|
end
|
|
653
|
-
|
|
654
|
-
class OffsetAwareRuleSet < RuleSet
|
|
655
|
-
# File offset range
|
|
656
|
-
attr_reader :offset
|
|
657
|
-
|
|
658
|
-
# the local or remote location
|
|
659
|
-
attr_accessor :filename
|
|
660
|
-
|
|
661
|
-
def initialize(filename, offset, selectors, block, specificity = nil)
|
|
662
|
-
super(selectors, block, specificity)
|
|
663
|
-
@offset = offset
|
|
664
|
-
@filename = filename
|
|
665
|
-
end
|
|
666
|
-
end
|
|
667
711
|
end
|
data/lib/css_parser/version.rb
CHANGED
data/lib/css_parser.rb
CHANGED
|
@@ -6,7 +6,6 @@ require 'net/https'
|
|
|
6
6
|
require 'digest/md5'
|
|
7
7
|
require 'zlib'
|
|
8
8
|
require 'stringio'
|
|
9
|
-
require 'iconv' unless String.method_defined?(:encode)
|
|
10
9
|
|
|
11
10
|
require 'css_parser/version'
|
|
12
11
|
require 'css_parser/rule_set'
|
|
@@ -53,12 +52,10 @@ module CssParser
|
|
|
53
52
|
# TODO: declaration_hashes should be able to contain a RuleSet
|
|
54
53
|
# this should be a Class method
|
|
55
54
|
def self.merge(*rule_sets)
|
|
56
|
-
@folded_declaration_cache = {}
|
|
57
|
-
|
|
58
55
|
# in case called like CssParser.merge([rule_set, rule_set])
|
|
59
56
|
rule_sets.flatten! if rule_sets[0].is_a?(Array)
|
|
60
57
|
|
|
61
|
-
unless rule_sets.all?
|
|
58
|
+
unless rule_sets.all?(CssParser::RuleSet)
|
|
62
59
|
raise ArgumentError, 'all parameters must be CssParser::RuleSets.'
|
|
63
60
|
end
|
|
64
61
|
|
|
@@ -71,17 +68,17 @@ module CssParser
|
|
|
71
68
|
rule_set.expand_shorthand!
|
|
72
69
|
|
|
73
70
|
specificity = rule_set.specificity
|
|
74
|
-
specificity ||= rule_set.selectors.
|
|
71
|
+
specificity ||= rule_set.selectors.filter_map { |s| calculate_specificity(s) }.max || 0
|
|
75
72
|
|
|
76
73
|
rule_set.each_declaration do |property, value, is_important|
|
|
77
74
|
# Add the property to the list to be folded per http://www.w3.org/TR/CSS21/cascade.html#cascading-order
|
|
78
|
-
if
|
|
75
|
+
if !properties.key?(property)
|
|
79
76
|
properties[property] = {value: value, specificity: specificity, is_important: is_important}
|
|
80
77
|
elsif is_important
|
|
81
|
-
if
|
|
78
|
+
if !properties[property][:is_important] || properties[property][:specificity] <= specificity
|
|
82
79
|
properties[property] = {value: value, specificity: specificity, is_important: is_important}
|
|
83
80
|
end
|
|
84
|
-
elsif properties[property][:specificity] < specificity
|
|
81
|
+
elsif properties[property][:specificity] < specificity || properties[property][:specificity] == specificity
|
|
85
82
|
unless properties[property][:is_important]
|
|
86
83
|
properties[property] = {value: value, specificity: specificity, is_important: is_important}
|
|
87
84
|
end
|
|
@@ -111,7 +108,7 @@ module CssParser
|
|
|
111
108
|
#++
|
|
112
109
|
def self.calculate_specificity(selector)
|
|
113
110
|
a = 0
|
|
114
|
-
b = selector.scan(
|
|
111
|
+
b = selector.scan('#').length
|
|
115
112
|
c = selector.scan(NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX_NC).length
|
|
116
113
|
d = selector.scan(ELEMENTS_AND_PSEUDO_ELEMENTS_RX_NC).length
|
|
117
114
|
|
|
@@ -139,7 +136,7 @@ module CssParser
|
|
|
139
136
|
css.gsub(URI_RX) do
|
|
140
137
|
uri = Regexp.last_match(1).to_s.gsub(/["']+/, '')
|
|
141
138
|
# Don't process URLs that are already absolute
|
|
142
|
-
unless uri.match(%r{^[a-z]+://}i)
|
|
139
|
+
unless uri.match?(%r{^[a-z]+://}i)
|
|
143
140
|
begin
|
|
144
141
|
uri = base_uri.join(uri)
|
|
145
142
|
rescue
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: css_parser
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex Dunae
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: addressable
|
|
@@ -24,118 +23,6 @@ dependencies:
|
|
|
24
23
|
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
25
|
version: '0'
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: benchmark-ips
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - ">="
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - ">="
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: bump
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: maxitest
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
type: :development
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: memory_profiler
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - ">="
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '0'
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - ">="
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '0'
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: rake
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - ">="
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: '0'
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - ">="
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '0'
|
|
97
|
-
- !ruby/object:Gem::Dependency
|
|
98
|
-
name: rubocop
|
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
|
100
|
-
requirements:
|
|
101
|
-
- - "~>"
|
|
102
|
-
- !ruby/object:Gem::Version
|
|
103
|
-
version: '1.8'
|
|
104
|
-
type: :development
|
|
105
|
-
prerelease: false
|
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
-
requirements:
|
|
108
|
-
- - "~>"
|
|
109
|
-
- !ruby/object:Gem::Version
|
|
110
|
-
version: '1.8'
|
|
111
|
-
- !ruby/object:Gem::Dependency
|
|
112
|
-
name: rubocop-rake
|
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
|
114
|
-
requirements:
|
|
115
|
-
- - ">="
|
|
116
|
-
- !ruby/object:Gem::Version
|
|
117
|
-
version: '0'
|
|
118
|
-
type: :development
|
|
119
|
-
prerelease: false
|
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
-
requirements:
|
|
122
|
-
- - ">="
|
|
123
|
-
- !ruby/object:Gem::Version
|
|
124
|
-
version: '0'
|
|
125
|
-
- !ruby/object:Gem::Dependency
|
|
126
|
-
name: webrick
|
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
|
128
|
-
requirements:
|
|
129
|
-
- - ">="
|
|
130
|
-
- !ruby/object:Gem::Version
|
|
131
|
-
version: '0'
|
|
132
|
-
type: :development
|
|
133
|
-
prerelease: false
|
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
-
requirements:
|
|
136
|
-
- - ">="
|
|
137
|
-
- !ruby/object:Gem::Version
|
|
138
|
-
version: '0'
|
|
139
26
|
description: A set of classes for parsing CSS in Ruby.
|
|
140
27
|
email: code@dunae.ca
|
|
141
28
|
executables: []
|
|
@@ -155,7 +42,7 @@ metadata:
|
|
|
155
42
|
changelog_uri: https://github.com/premailer/css_parser/blob/master/CHANGELOG.md
|
|
156
43
|
source_code_uri: https://github.com/premailer/css_parser
|
|
157
44
|
bug_tracker_uri: https://github.com/premailer/css_parser/issues
|
|
158
|
-
|
|
45
|
+
rubygems_mfa_required: 'true'
|
|
159
46
|
rdoc_options: []
|
|
160
47
|
require_paths:
|
|
161
48
|
- lib
|
|
@@ -163,15 +50,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
163
50
|
requirements:
|
|
164
51
|
- - ">="
|
|
165
52
|
- !ruby/object:Gem::Version
|
|
166
|
-
version: '
|
|
53
|
+
version: '3.3'
|
|
167
54
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
168
55
|
requirements:
|
|
169
56
|
- - ">="
|
|
170
57
|
- !ruby/object:Gem::Version
|
|
171
58
|
version: '0'
|
|
172
59
|
requirements: []
|
|
173
|
-
rubygems_version:
|
|
174
|
-
signing_key:
|
|
60
|
+
rubygems_version: 4.0.3
|
|
175
61
|
specification_version: 4
|
|
176
62
|
summary: Ruby CSS parser.
|
|
177
63
|
test_files: []
|