css_parser 1.17.1 → 1.21.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 +86 -37
- data/lib/css_parser/regexps.rb +1 -1
- data/lib/css_parser/rule_set.rb +84 -43
- data/lib/css_parser/version.rb +1 -1
- data/lib/css_parser.rb +4 -5
- metadata +4 -116
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c8880ebd6978e8a8a3108ea93b4d567252aee152b28f237dab31809f1488f42e
|
4
|
+
data.tar.gz: 4e8379acf246b7fe2573a2ada4fc49c01e9c3b2de43fe80d07f42390f8f16450
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 480947bb06a6c40ab955f76ff9eb7ba9ac5caf160d7fe343d16901e9f37210c9ccab22a07a4d3165ec26bec4cd28547f530718978d2d969ed398c8195351a0c0
|
7
|
+
data.tar.gz: e7a757c9dc096e71d5f617343f0900bacddea42bef4b26ca68f2830728a480ace908807dc4db82b6b0b77a11c1b2c9295ad814bb2aaa80fd069c959cd58afe65
|
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,7 +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)"
|
21
|
+
USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)".freeze
|
22
|
+
RULESET_TOKENIZER_RX = /\s+|\\{2,}|\\?[{}\s"]|[()]|.[^\s"{}()\\]*/.freeze
|
20
23
|
STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
|
21
24
|
STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
|
22
25
|
|
@@ -133,7 +136,7 @@ module CssParser
|
|
133
136
|
block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
|
134
137
|
media_types = []
|
135
138
|
if (media_string = import_rule[-1])
|
136
|
-
media_string.split(
|
139
|
+
media_string.split(',').each do |t|
|
137
140
|
media_types << CssParser.sanitize_media_query(t) unless t.empty?
|
138
141
|
end
|
139
142
|
else
|
@@ -164,14 +167,44 @@ module CssParser
|
|
164
167
|
parse_block_into_rule_sets!(block, options)
|
165
168
|
end
|
166
169
|
|
167
|
-
# Add a CSS rule by setting the +selectors+, +declarations+
|
170
|
+
# Add a CSS rule by setting the +selectors+, +declarations+
|
171
|
+
# and +media_types+. Optional pass +filename+ , +offset+ for source
|
172
|
+
# reference too.
|
168
173
|
#
|
169
|
-
# +media_types+ can be a symbol or an array of symbols.
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
174
|
+
# +media_types+ can be a symbol or an array of symbols. default to :all
|
175
|
+
# optional fields for source location for source location
|
176
|
+
# +filename+ can be a string or uri pointing to the file or url location.
|
177
|
+
# +offset+ should be Range object representing the start and end byte locations where the rule was found in the file.
|
178
|
+
def add_rule!(*args, selectors: nil, block: nil, filename: nil, offset: nil, media_types: :all) # rubocop:disable Metrics/ParameterLists
|
179
|
+
if args.any?
|
180
|
+
media_types = nil
|
181
|
+
if selectors || block || filename || offset || media_types
|
182
|
+
raise ArgumentError, "don't mix positional and keyword arguments arguments"
|
183
|
+
end
|
184
|
+
|
185
|
+
warn '[DEPRECATION] `add_rule!` with positional arguments is deprecated. ' \
|
186
|
+
'Please use keyword arguments instead.', uplevel: 1
|
187
|
+
|
188
|
+
case args.length
|
189
|
+
when 2
|
190
|
+
selectors, block = args
|
191
|
+
when 3
|
192
|
+
selectors, block, media_types = args
|
193
|
+
else
|
194
|
+
raise ArgumentError
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
begin
|
199
|
+
rule_set = RuleSet.new(
|
200
|
+
selectors: selectors, block: block,
|
201
|
+
offset: offset, filename: filename
|
202
|
+
)
|
203
|
+
|
204
|
+
add_rule_set!(rule_set, media_types)
|
205
|
+
rescue ArgumentError => e
|
206
|
+
raise e if @options[:rule_set_exceptions]
|
207
|
+
end
|
175
208
|
end
|
176
209
|
|
177
210
|
# Add a CSS rule by setting the +selectors+, +declarations+, +filename+, +offset+ and +media_types+.
|
@@ -180,8 +213,11 @@ module CssParser
|
|
180
213
|
# +offset+ should be Range object representing the start and end byte locations where the rule was found in the file.
|
181
214
|
# +media_types+ can be a symbol or an array of symbols.
|
182
215
|
def add_rule_with_offsets!(selectors, declarations, filename, offset, media_types = :all)
|
183
|
-
|
184
|
-
|
216
|
+
warn '[DEPRECATION] `add_rule_with_offsets!` is deprecated. Please use `add_rule!` instead.', uplevel: 1
|
217
|
+
add_rule!(
|
218
|
+
selectors: selectors, block: declarations, media_types: media_types,
|
219
|
+
filename: filename, offset: offset
|
220
|
+
)
|
185
221
|
end
|
186
222
|
|
187
223
|
# Add a CssParser RuleSet object.
|
@@ -323,17 +359,21 @@ module CssParser
|
|
323
359
|
in_at_media_rule = false
|
324
360
|
in_media_block = false
|
325
361
|
|
326
|
-
current_selectors =
|
327
|
-
current_media_query =
|
328
|
-
current_declarations =
|
362
|
+
current_selectors = +''
|
363
|
+
current_media_query = +''
|
364
|
+
current_declarations = +''
|
329
365
|
|
330
366
|
# once we are in a rule, we will use this to store where we started if we are capturing offsets
|
331
367
|
rule_start = nil
|
332
|
-
|
368
|
+
start_offset = nil
|
369
|
+
end_offset = nil
|
333
370
|
|
334
|
-
|
371
|
+
scanner = StringScanner.new(block)
|
372
|
+
until scanner.eos?
|
335
373
|
# save the regex offset so that we know where in the file we are
|
336
|
-
|
374
|
+
start_offset = scanner.pos
|
375
|
+
token = scanner.scan(RULESET_TOKENIZER_RX)
|
376
|
+
end_offset = scanner.pos
|
337
377
|
|
338
378
|
if token.start_with?('"') # found un-escaped double quote
|
339
379
|
in_string = !in_string
|
@@ -360,20 +400,24 @@ module CssParser
|
|
360
400
|
current_declarations.strip!
|
361
401
|
|
362
402
|
unless current_declarations.empty?
|
403
|
+
add_rule_options = {
|
404
|
+
selectors: current_selectors, block: current_declarations,
|
405
|
+
media_types: current_media_queries
|
406
|
+
}
|
363
407
|
if options[:capture_offsets]
|
364
|
-
|
365
|
-
|
366
|
-
add_rule!(current_selectors, current_declarations, current_media_queries)
|
408
|
+
add_rule_options[:filename] = options[:filename]
|
409
|
+
add_rule_options[:offset] = rule_start..end_offset
|
367
410
|
end
|
411
|
+
add_rule!(**add_rule_options)
|
368
412
|
end
|
369
413
|
|
370
|
-
current_selectors =
|
371
|
-
current_declarations =
|
414
|
+
current_selectors = +''
|
415
|
+
current_declarations = +''
|
372
416
|
|
373
417
|
# restart our search for selectors and declarations
|
374
418
|
rule_start = nil if options[:capture_offsets]
|
375
419
|
end
|
376
|
-
elsif
|
420
|
+
elsif /@media/i.match?(token)
|
377
421
|
# found '@media', reset current media_types
|
378
422
|
in_at_media_rule = true
|
379
423
|
current_media_queries = []
|
@@ -383,14 +427,14 @@ module CssParser
|
|
383
427
|
in_at_media_rule = false
|
384
428
|
in_media_block = true
|
385
429
|
current_media_queries << CssParser.sanitize_media_query(current_media_query)
|
386
|
-
current_media_query =
|
430
|
+
current_media_query = +''
|
387
431
|
elsif token.include?(',')
|
388
432
|
# new media query begins
|
389
433
|
token.tr!(',', ' ')
|
390
434
|
token.strip!
|
391
435
|
current_media_query << token << ' '
|
392
436
|
current_media_queries << CssParser.sanitize_media_query(current_media_query)
|
393
|
-
current_media_query =
|
437
|
+
current_media_query = +''
|
394
438
|
else
|
395
439
|
token.strip!
|
396
440
|
# special-case the ( and ) tokens to remove inner-whitespace
|
@@ -423,18 +467,22 @@ module CssParser
|
|
423
467
|
current_selectors << token
|
424
468
|
|
425
469
|
# mark this as the beginning of the selector unless we have already marked it
|
426
|
-
rule_start =
|
470
|
+
rule_start = start_offset if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
|
427
471
|
end
|
428
472
|
end
|
429
473
|
|
430
474
|
# check for unclosed braces
|
431
475
|
return unless in_declarations > 0
|
432
476
|
|
433
|
-
|
434
|
-
|
477
|
+
add_rule_options = {
|
478
|
+
selectors: current_selectors, block: current_declarations,
|
479
|
+
media_types: current_media_queries
|
480
|
+
}
|
481
|
+
if options[:capture_offsets]
|
482
|
+
add_rule_options[:filename] = options[:filename]
|
483
|
+
add_rule_options[:offset] = rule_start..end_offset
|
435
484
|
end
|
436
|
-
|
437
|
-
add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
|
485
|
+
add_rule!(**add_rule_options)
|
438
486
|
end
|
439
487
|
|
440
488
|
# Load a remote CSS file.
|
@@ -452,6 +500,8 @@ module CssParser
|
|
452
500
|
if options.is_a? Hash
|
453
501
|
opts.merge!(options)
|
454
502
|
else
|
503
|
+
warn '[DEPRECATION] `load_uri!` with positional arguments is deprecated. ' \
|
504
|
+
'Please use keyword arguments instead.', uplevel: 1
|
455
505
|
opts[:base_uri] = options if options.is_a? String
|
456
506
|
opts[:media_types] = deprecated if deprecated
|
457
507
|
end
|
@@ -478,6 +528,8 @@ module CssParser
|
|
478
528
|
if options.is_a? Hash
|
479
529
|
opts.merge!(options)
|
480
530
|
else
|
531
|
+
warn '[DEPRECATION] `load_file!` with positional arguments is deprecated. ' \
|
532
|
+
'Please use keyword arguments instead.', uplevel: 1
|
481
533
|
opts[:base_dir] = options if options.is_a? String
|
482
534
|
opts[:media_types] = deprecated if deprecated
|
483
535
|
end
|
@@ -501,6 +553,8 @@ module CssParser
|
|
501
553
|
if options.is_a? Hash
|
502
554
|
opts.merge!(options)
|
503
555
|
else
|
556
|
+
warn '[DEPRECATION] `load_file!` with positional arguments is deprecated. ' \
|
557
|
+
'Please use keyword arguments instead.', uplevel: 1
|
504
558
|
opts[:base_dir] = options if options.is_a? String
|
505
559
|
opts[:media_types] = deprecated if deprecated
|
506
560
|
end
|
@@ -625,12 +679,7 @@ module CssParser
|
|
625
679
|
end
|
626
680
|
|
627
681
|
if charset
|
628
|
-
|
629
|
-
src.encode!('UTF-8', charset)
|
630
|
-
else
|
631
|
-
ic = Iconv.new('UTF-8//IGNORE', charset)
|
632
|
-
src = ic.iconv(src)
|
633
|
-
end
|
682
|
+
src.encode!('UTF-8', charset)
|
634
683
|
end
|
635
684
|
rescue
|
636
685
|
@redirect_count = nil
|
@@ -671,7 +720,7 @@ module CssParser
|
|
671
720
|
nodes = {}
|
672
721
|
lines.each do |line|
|
673
722
|
parts = line.split(':', 2)
|
674
|
-
if parts[1]
|
723
|
+
if parts[1].include?(':')
|
675
724
|
nodes[parts[0]] = css_node_to_h(hash, parts[0], parts[1])
|
676
725
|
else
|
677
726
|
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
|
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 = {}
|
@@ -142,7 +150,7 @@ module CssParser
|
|
142
150
|
|
143
151
|
if preserve_importance
|
144
152
|
importance = get_value(property).important
|
145
|
-
replacement_declarations.
|
153
|
+
replacement_declarations.each_value { |value| value.important = importance }
|
146
154
|
end
|
147
155
|
|
148
156
|
replacement_keys = declarations.keys
|
@@ -190,7 +198,7 @@ module CssParser
|
|
190
198
|
end
|
191
199
|
|
192
200
|
def to_s(options = {})
|
193
|
-
str = declarations.reduce(
|
201
|
+
str = declarations.reduce(+'') do |memo, (prop, value)|
|
194
202
|
importance = options[:force_important] || value.important ? ' !important' : ''
|
195
203
|
memo << "#{prop}: #{value.value}#{importance}; "
|
196
204
|
end
|
@@ -223,6 +231,12 @@ module CssParser
|
|
223
231
|
|
224
232
|
extend Forwardable
|
225
233
|
|
234
|
+
# optional field for storing source reference
|
235
|
+
# File offset range
|
236
|
+
attr_reader :offset
|
237
|
+
# the local or remote location
|
238
|
+
attr_accessor :filename
|
239
|
+
|
226
240
|
# Array of selector strings.
|
227
241
|
attr_reader :selectors
|
228
242
|
|
@@ -237,9 +251,38 @@ module CssParser
|
|
237
251
|
alias []= add_declaration!
|
238
252
|
alias remove_declaration! delete
|
239
253
|
|
240
|
-
def initialize(selectors, block,
|
254
|
+
def initialize(*args, selectors: nil, block: nil, offset: nil, filename: nil, specificity: nil) # rubocop:disable Metrics/ParameterLists
|
255
|
+
if args.any?
|
256
|
+
if selectors || block || offset || filename || specificity
|
257
|
+
raise ArgumentError, "don't mix positional and keyword arguments"
|
258
|
+
end
|
259
|
+
|
260
|
+
warn '[DEPRECATION] positional arguments are deprecated use keyword instead.', uplevel: 1
|
261
|
+
|
262
|
+
case args.length
|
263
|
+
when 2
|
264
|
+
selectors, block = args
|
265
|
+
when 3
|
266
|
+
selectors, block, specificity = args
|
267
|
+
when 4
|
268
|
+
filename, offset, selectors, block = args
|
269
|
+
when 5
|
270
|
+
filename, offset, selectors, block, specificity = args
|
271
|
+
else
|
272
|
+
raise ArgumentError
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
241
276
|
@selectors = []
|
242
277
|
@specificity = specificity
|
278
|
+
|
279
|
+
unless offset.nil? == filename.nil?
|
280
|
+
raise ArgumentError, 'require both offset and filename or no offset and no filename'
|
281
|
+
end
|
282
|
+
|
283
|
+
@offset = offset
|
284
|
+
@filename = filename
|
285
|
+
|
243
286
|
parse_selectors!(selectors) if selectors
|
244
287
|
parse_declarations!(block)
|
245
288
|
end
|
@@ -308,7 +351,7 @@ module CssParser
|
|
308
351
|
|
309
352
|
replacement =
|
310
353
|
if value.match(CssParser::RE_INHERIT)
|
311
|
-
BACKGROUND_PROPERTIES.
|
354
|
+
BACKGROUND_PROPERTIES.to_h { |key| [key, 'inherit'] }
|
312
355
|
else
|
313
356
|
{
|
314
357
|
'background-image' => value.slice!(CssParser::RE_IMAGE),
|
@@ -414,17 +457,17 @@ module CssParser
|
|
414
457
|
else
|
415
458
|
font_props['font-family'] = m
|
416
459
|
end
|
417
|
-
elsif
|
418
|
-
|
460
|
+
elsif /normal|inherit/i.match?(m)
|
461
|
+
FONT_WEIGHT_PROPERTIES.each do |font_prop|
|
419
462
|
font_props[font_prop] ||= m
|
420
463
|
end
|
421
|
-
elsif
|
464
|
+
elsif /italic|oblique/i.match?(m)
|
422
465
|
font_props['font-style'] = m
|
423
|
-
elsif
|
466
|
+
elsif /small-caps/i.match?(m)
|
424
467
|
font_props['font-variant'] = m
|
425
|
-
elsif
|
468
|
+
elsif /[1-9]00$|bold|bolder|lighter/i.match?(m)
|
426
469
|
font_props['font-weight'] = m
|
427
|
-
elsif
|
470
|
+
elsif CssParser::FONT_UNITS_RX.match?(m)
|
428
471
|
if m.include?('/')
|
429
472
|
font_props['font-size'], font_props['line-height'] = m.split('/', 2)
|
430
473
|
else
|
@@ -447,8 +490,8 @@ module CssParser
|
|
447
490
|
value = declaration.value.dup
|
448
491
|
|
449
492
|
replacement =
|
450
|
-
if
|
451
|
-
LIST_STYLE_PROPERTIES.
|
493
|
+
if CssParser::RE_INHERIT.match?(value)
|
494
|
+
LIST_STYLE_PROPERTIES.to_h { |key| [key, 'inherit'] }
|
452
495
|
else
|
453
496
|
{
|
454
497
|
'list-style-type' => value.slice!(CssParser::RE_LIST_STYLE_TYPE),
|
@@ -513,15 +556,15 @@ module CssParser
|
|
513
556
|
#
|
514
557
|
# TODO: this is extremely similar to create_background_shorthand! and should be combined
|
515
558
|
def create_border_shorthand! # :nodoc:
|
516
|
-
values = BORDER_STYLE_PROPERTIES.
|
559
|
+
values = BORDER_STYLE_PROPERTIES.filter_map do |property|
|
517
560
|
next unless (declaration = declarations[property])
|
518
561
|
next if declaration.important
|
519
562
|
# can't merge if any value contains a space (i.e. has multiple values)
|
520
563
|
# we temporarily remove any spaces after commas for the check (inside rgba, etc...)
|
521
|
-
next if declaration.value.gsub(/,\s/, ',').strip
|
564
|
+
next if /\s/.match?(declaration.value.gsub(/,\s/, ',').strip)
|
522
565
|
|
523
566
|
declaration.value
|
524
|
-
end
|
567
|
+
end
|
525
568
|
|
526
569
|
return if values.size != BORDER_STYLE_PROPERTIES.size
|
527
570
|
|
@@ -538,7 +581,7 @@ module CssParser
|
|
538
581
|
return if declarations.size < NUMBER_OF_DIMENSIONS
|
539
582
|
|
540
583
|
DIMENSIONS.each do |property, dimensions|
|
541
|
-
values =
|
584
|
+
values = DIMENSION_DIRECTIONS.each_with_index.with_object({}) do |(side, index), result|
|
542
585
|
next unless (declaration = declarations[dimensions[index]])
|
543
586
|
|
544
587
|
result[side] = declaration.value
|
@@ -561,7 +604,7 @@ module CssParser
|
|
561
604
|
def create_font_shorthand! # :nodoc:
|
562
605
|
return unless FONT_STYLE_PROPERTIES.all? { |prop| declarations.key?(prop) }
|
563
606
|
|
564
|
-
new_value =
|
607
|
+
new_value = +''
|
565
608
|
['font-style', 'font-variant', 'font-weight'].each do |property|
|
566
609
|
unless declarations[property].value == 'normal'
|
567
610
|
new_value << declarations[property].value << ' '
|
@@ -598,7 +641,7 @@ module CssParser
|
|
598
641
|
return [:top] if values.values.uniq.count == 1
|
599
642
|
|
600
643
|
# `/* top | right | bottom | left */`
|
601
|
-
return
|
644
|
+
return DIMENSION_DIRECTIONS if values[:left] != values[:right]
|
602
645
|
|
603
646
|
# Vertical are the same & horizontal are the same, `/* vertical | horizontal */`
|
604
647
|
return [:top, :left] if values[:top] == values[:bottom]
|
@@ -612,20 +655,32 @@ module CssParser
|
|
612
655
|
return unless block
|
613
656
|
|
614
657
|
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
|
658
|
+
block.split(SEMICOLON) do |decs|
|
659
|
+
decs = (continuation ? "#{continuation};#{decs}" : decs)
|
660
|
+
if unmatched_open_parenthesis?(decs)
|
661
|
+
# Semicolon happened within parenthesis, so it is a part of the value
|
662
|
+
# the rest of the value is in the next segment
|
663
|
+
continuation = decs
|
664
|
+
next
|
625
665
|
end
|
666
|
+
|
667
|
+
next unless (colon = decs.index(COLON))
|
668
|
+
|
669
|
+
property = decs[0, colon]
|
670
|
+
value = decs[(colon + 1)..]
|
671
|
+
property.strip!
|
672
|
+
value.strip!
|
673
|
+
next if property.empty? || value.empty? || value.casecmp?(IMPORTANT)
|
674
|
+
|
675
|
+
add_declaration!(property, value)
|
676
|
+
continuation = nil
|
626
677
|
end
|
627
678
|
end
|
628
679
|
|
680
|
+
def unmatched_open_parenthesis?(declarations)
|
681
|
+
(lparen_index = declarations.index(LPAREN)) && !declarations.index(RPAREN, lparen_index)
|
682
|
+
end
|
683
|
+
|
629
684
|
#--
|
630
685
|
# TODO: way too simplistic
|
631
686
|
#++
|
@@ -650,18 +705,4 @@ module CssParser
|
|
650
705
|
end
|
651
706
|
end
|
652
707
|
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
708
|
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'
|
@@ -58,7 +57,7 @@ module CssParser
|
|
58
57
|
# in case called like CssParser.merge([rule_set, rule_set])
|
59
58
|
rule_sets.flatten! if rule_sets[0].is_a?(Array)
|
60
59
|
|
61
|
-
unless rule_sets.all?
|
60
|
+
unless rule_sets.all?(CssParser::RuleSet)
|
62
61
|
raise ArgumentError, 'all parameters must be CssParser::RuleSets.'
|
63
62
|
end
|
64
63
|
|
@@ -71,7 +70,7 @@ module CssParser
|
|
71
70
|
rule_set.expand_shorthand!
|
72
71
|
|
73
72
|
specificity = rule_set.specificity
|
74
|
-
specificity ||= rule_set.selectors.
|
73
|
+
specificity ||= rule_set.selectors.filter_map { |s| calculate_specificity(s) }.max || 0
|
75
74
|
|
76
75
|
rule_set.each_declaration do |property, value, is_important|
|
77
76
|
# Add the property to the list to be folded per http://www.w3.org/TR/CSS21/cascade.html#cascading-order
|
@@ -111,7 +110,7 @@ module CssParser
|
|
111
110
|
#++
|
112
111
|
def self.calculate_specificity(selector)
|
113
112
|
a = 0
|
114
|
-
b = selector.scan(
|
113
|
+
b = selector.scan('#').length
|
115
114
|
c = selector.scan(NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX_NC).length
|
116
115
|
d = selector.scan(ELEMENTS_AND_PSEUDO_ELEMENTS_RX_NC).length
|
117
116
|
|
@@ -139,7 +138,7 @@ module CssParser
|
|
139
138
|
css.gsub(URI_RX) do
|
140
139
|
uri = Regexp.last_match(1).to_s.gsub(/["']+/, '')
|
141
140
|
# Don't process URLs that are already absolute
|
142
|
-
unless uri.match(%r{^[a-z]+://}i)
|
141
|
+
unless uri.match?(%r{^[a-z]+://}i)
|
143
142
|
begin
|
144
143
|
uri = base_uri.join(uri)
|
145
144
|
rescue
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: css_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.21.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Dunae
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -24,118 +24,6 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
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
27
|
description: A set of classes for parsing CSS in Ruby.
|
140
28
|
email: code@dunae.ca
|
141
29
|
executables: []
|
@@ -164,14 +52,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
164
52
|
requirements:
|
165
53
|
- - ">="
|
166
54
|
- !ruby/object:Gem::Version
|
167
|
-
version: '
|
55
|
+
version: '3.0'
|
168
56
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
57
|
requirements:
|
170
58
|
- - ">="
|
171
59
|
- !ruby/object:Gem::Version
|
172
60
|
version: '0'
|
173
61
|
requirements: []
|
174
|
-
rubygems_version: 3.
|
62
|
+
rubygems_version: 3.4.19
|
175
63
|
signing_key:
|
176
64
|
specification_version: 4
|
177
65
|
summary: Ruby CSS parser.
|