css_parser 1.4.10 → 1.5.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/css_parser/parser.rb +99 -25
- data/lib/css_parser/rule_set.rb +15 -0
- data/lib/css_parser/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 842917dce4f3455e696297fb182f1b1783ac3a5a
|
4
|
+
data.tar.gz: ef48928b85b441a68fe9b10e3947643f90a147ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01d11dfbc01781b5ca23598800f53062211f0d7b06074683720ca11354d85d7ea2e9cb2624bfc97265e6ee89ee3c28edb95d02361512a1407c77bd9c8b54d381
|
7
|
+
data.tar.gz: 194c4de30884cc386108ffee89cd1c0426ce4ae2bcfb467515c3fc1ff19dd387680a19306c7ace0f24854adef85846f0eeeca8a01651602c3e744a38cf72ca61
|
data/lib/css_parser/parser.rb
CHANGED
@@ -36,7 +36,8 @@ module CssParser
|
|
36
36
|
def initialize(options = {})
|
37
37
|
@options = {:absolute_paths => false,
|
38
38
|
:import => true,
|
39
|
-
:io_exceptions => true
|
39
|
+
:io_exceptions => true,
|
40
|
+
:capture_offsets => false}.merge(options)
|
40
41
|
|
41
42
|
# array of RuleSets
|
42
43
|
@rules = []
|
@@ -117,7 +118,7 @@ module CssParser
|
|
117
118
|
options[:media_types] = [options[:media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
|
118
119
|
options[:only_media_types] = [options[:only_media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
|
119
120
|
|
120
|
-
block = cleanup_block(block)
|
121
|
+
block = cleanup_block(block, options)
|
121
122
|
|
122
123
|
if options[:base_uri] and @options[:absolute_paths]
|
123
124
|
block = CssParser.convert_uris(block, options[:base_uri])
|
@@ -139,17 +140,22 @@ module CssParser
|
|
139
140
|
|
140
141
|
import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
|
141
142
|
|
143
|
+
import_options = { :media_types => media_types }
|
144
|
+
import_options[:capture_offsets] = true if options[:capture_offsets]
|
145
|
+
|
142
146
|
if options[:base_uri]
|
143
147
|
import_uri = Addressable::URI.parse(options[:base_uri].to_s) + Addressable::URI.parse(import_path)
|
144
|
-
|
148
|
+
import_options[:base_uri] = options[:base_uri]
|
149
|
+
load_uri!(import_uri, import_options)
|
145
150
|
elsif options[:base_dir]
|
146
|
-
|
151
|
+
import_options[:base_dir] = options[:base_dir]
|
152
|
+
load_file!(import_path, import_options)
|
147
153
|
end
|
148
154
|
end
|
149
155
|
end
|
150
156
|
|
151
157
|
# Remove @import declarations
|
152
|
-
block
|
158
|
+
block = ignore_pattern(block, RE_AT_IMPORT_RULE, options)
|
153
159
|
|
154
160
|
parse_block_into_rule_sets!(block, options)
|
155
161
|
end
|
@@ -162,6 +168,16 @@ module CssParser
|
|
162
168
|
add_rule_set!(rule_set, media_types)
|
163
169
|
end
|
164
170
|
|
171
|
+
# Add a CSS rule by setting the +selectors+, +declarations+, +filename+, +offset+ and +media_types+.
|
172
|
+
#
|
173
|
+
# +filename+ can be a string or uri pointing to the file or url location.
|
174
|
+
# +offset+ should be Range object representing the start and end byte locations where the rule was found in the file.
|
175
|
+
# +media_types+ can be a symbol or an array of symbols.
|
176
|
+
def add_rule_with_offsets!(selectors, declarations, filename, offset, media_types = :all)
|
177
|
+
rule_set = OffsetAwareRuleSet.new(filename, offset, selectors, declarations)
|
178
|
+
add_rule_set!(rule_set, media_types)
|
179
|
+
end
|
180
|
+
|
165
181
|
# Add a CssParser RuleSet object.
|
166
182
|
#
|
167
183
|
# +media_types+ can be a symbol or an array of symbols.
|
@@ -289,9 +305,16 @@ module CssParser
|
|
289
305
|
current_media_query = ''
|
290
306
|
current_declarations = ''
|
291
307
|
|
292
|
-
|
308
|
+
# once we are in a rule, we will use this to store where we started if we are capturing offsets
|
309
|
+
rule_start = nil
|
310
|
+
offset = nil
|
311
|
+
|
312
|
+
block.scan(/(([\\]{2,})|([\\]?[{}\s"])|(.[^\s"{}\\]*))/) do |matches|
|
293
313
|
token = matches[0]
|
294
314
|
|
315
|
+
# save the regex offset so that we know where in the file we are
|
316
|
+
offset = Regexp.last_match.offset(0) if options[:capture_offsets]
|
317
|
+
|
295
318
|
if token =~ /\A"/ # found un-escaped double quote
|
296
319
|
in_string = !in_string
|
297
320
|
end
|
@@ -316,11 +339,18 @@ module CssParser
|
|
316
339
|
in_declarations -= 1
|
317
340
|
|
318
341
|
unless current_declarations.strip.empty?
|
319
|
-
|
342
|
+
if options[:capture_offsets]
|
343
|
+
add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
|
344
|
+
else
|
345
|
+
add_rule!(current_selectors, current_declarations, current_media_queries)
|
346
|
+
end
|
320
347
|
end
|
321
348
|
|
322
349
|
current_selectors = ''
|
323
350
|
current_declarations = ''
|
351
|
+
|
352
|
+
# restart our search for selectors and declarations
|
353
|
+
rule_start = nil if options[:capture_offsets]
|
324
354
|
end
|
325
355
|
elsif token =~ /@media/i
|
326
356
|
# found '@media', reset current media_types
|
@@ -356,11 +386,14 @@ module CssParser
|
|
356
386
|
end
|
357
387
|
else
|
358
388
|
if token =~ /\{/ and not in_string
|
359
|
-
current_selectors.
|
360
|
-
current_selectors.gsub!(/[\s]*$/, '')
|
389
|
+
current_selectors.strip!
|
361
390
|
in_declarations += 1
|
362
391
|
else
|
392
|
+
# if we are in a selector, add the token to the current selectors
|
363
393
|
current_selectors += token
|
394
|
+
|
395
|
+
# mark this as the beginning of the selector unless we have already marked it
|
396
|
+
rule_start = offset.first if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
|
364
397
|
end
|
365
398
|
end
|
366
399
|
end
|
@@ -368,7 +401,11 @@ module CssParser
|
|
368
401
|
|
369
402
|
# check for unclosed braces
|
370
403
|
if in_declarations > 0
|
371
|
-
|
404
|
+
if options[:capture_offsets]
|
405
|
+
add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
|
406
|
+
else
|
407
|
+
add_rule!(current_selectors, current_declarations, current_media_queries)
|
408
|
+
end
|
372
409
|
end
|
373
410
|
end
|
374
411
|
|
@@ -381,7 +418,6 @@ module CssParser
|
|
381
418
|
# Deprecated: originally accepted three params: `uri`, `base_uri` and `media_types`
|
382
419
|
def load_uri!(uri, options = {}, deprecated = nil)
|
383
420
|
uri = Addressable::URI.parse(uri) unless uri.respond_to? :scheme
|
384
|
-
#base_uri = nil, media_types = :all, options = {}
|
385
421
|
|
386
422
|
opts = {:base_uri => nil, :media_types => :all}
|
387
423
|
|
@@ -399,6 +435,9 @@ module CssParser
|
|
399
435
|
|
400
436
|
opts[:base_uri] = uri if opts[:base_uri].nil?
|
401
437
|
|
438
|
+
# pass on the uri if we are capturing file offsets
|
439
|
+
opts[:filename] = uri.to_s if opts[:capture_offsets]
|
440
|
+
|
402
441
|
src, = read_remote_file(uri) # skip charset
|
403
442
|
if src
|
404
443
|
add_block!(src, opts)
|
@@ -406,20 +445,40 @@ module CssParser
|
|
406
445
|
end
|
407
446
|
|
408
447
|
# Load a local CSS file.
|
409
|
-
def load_file!(file_name,
|
410
|
-
|
448
|
+
def load_file!(file_name, options = {}, deprecated = nil)
|
449
|
+
opts = {:base_dir => nil, :media_types => :all}
|
450
|
+
|
451
|
+
if options.is_a? Hash
|
452
|
+
opts.merge!(options)
|
453
|
+
else
|
454
|
+
opts[:base_dir] = options if options.is_a? String
|
455
|
+
opts[:media_types] = deprecated if deprecated
|
456
|
+
end
|
457
|
+
|
458
|
+
file_name = File.expand_path(file_name, opts[:base_dir])
|
411
459
|
return unless File.readable?(file_name)
|
412
460
|
return unless circular_reference_check(file_name)
|
413
461
|
|
414
462
|
src = IO.read(file_name)
|
415
|
-
base_dir = File.dirname(file_name)
|
416
463
|
|
417
|
-
|
464
|
+
opts[:filename] = file_name if opts[:capture_offsets]
|
465
|
+
opts[:base_dir] = File.dirname(file_name)
|
466
|
+
|
467
|
+
add_block!(src, opts)
|
418
468
|
end
|
419
469
|
|
420
470
|
# Load a local CSS string.
|
421
|
-
def load_string!(src,
|
422
|
-
|
471
|
+
def load_string!(src, options = {}, deprecated = nil)
|
472
|
+
opts = {:base_dir => nil, :media_types => :all}
|
473
|
+
|
474
|
+
if options.is_a? Hash
|
475
|
+
opts.merge!(options)
|
476
|
+
else
|
477
|
+
opts[:base_dir] = options if options.is_a? String
|
478
|
+
opts[:media_types] = deprecated if deprecated
|
479
|
+
end
|
480
|
+
|
481
|
+
add_block!(src, opts)
|
423
482
|
end
|
424
483
|
|
425
484
|
|
@@ -440,20 +499,31 @@ module CssParser
|
|
440
499
|
end
|
441
500
|
end
|
442
501
|
|
502
|
+
# Remove a pattern from a given string
|
503
|
+
#
|
504
|
+
# Returns a string.
|
505
|
+
def ignore_pattern(css, regex, options)
|
506
|
+
# if we are capturing file offsets, replace the characters with spaces to retail the original positions
|
507
|
+
return css.gsub(regex) { |m| ' ' * m.length } if options[:capture_offsets]
|
508
|
+
|
509
|
+
# otherwise just strip it out
|
510
|
+
css.gsub(regex, '')
|
511
|
+
end
|
512
|
+
|
443
513
|
# Strip comments and clean up blank lines from a block of CSS.
|
444
514
|
#
|
445
515
|
# Returns a string.
|
446
|
-
def cleanup_block(block) # :nodoc:
|
516
|
+
def cleanup_block(block, options = {}) # :nodoc:
|
447
517
|
# Strip CSS comments
|
448
|
-
utf8_block = block.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
|
449
|
-
utf8_block
|
518
|
+
utf8_block = block.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: ' ')
|
519
|
+
utf8_block = ignore_pattern(utf8_block, STRIP_CSS_COMMENTS_RX, options)
|
450
520
|
|
451
521
|
# Strip HTML comments - they shouldn't really be in here but
|
452
522
|
# some people are just crazy...
|
453
|
-
utf8_block
|
523
|
+
utf8_block = ignore_pattern(utf8_block, STRIP_HTML_COMMENTS_RX, options)
|
454
524
|
|
455
525
|
# Strip lines containing just whitespace
|
456
|
-
utf8_block.gsub!(/^\s+$/, "")
|
526
|
+
utf8_block.gsub!(/^\s+$/, "") unless options[:capture_offsets]
|
457
527
|
|
458
528
|
utf8_block
|
459
529
|
end
|
@@ -483,12 +553,16 @@ module CssParser
|
|
483
553
|
|
484
554
|
src = '', charset = nil
|
485
555
|
|
486
|
-
uri = Addressable::URI.parse(uri.to_s)
|
487
556
|
begin
|
557
|
+
uri = Addressable::URI.parse(uri.to_s)
|
558
|
+
|
488
559
|
if uri.scheme == 'file'
|
489
560
|
# local file
|
490
|
-
|
561
|
+
path = uri.path
|
562
|
+
path.gsub!(/^\//, '') if Gem.win_platform?
|
563
|
+
fh = open(path, 'rb')
|
491
564
|
src = fh.read
|
565
|
+
charset = fh.respond_to?(:charset) ? fh.charset : 'utf-8'
|
492
566
|
fh.close
|
493
567
|
else
|
494
568
|
# remote file
|
@@ -503,7 +577,7 @@ module CssParser
|
|
503
577
|
|
504
578
|
res = http.get(uri.request_uri, {'User-Agent' => USER_AGENT, 'Accept-Encoding' => 'gzip'})
|
505
579
|
src = res.body
|
506
|
-
charset =
|
580
|
+
charset = res.respond_to?(:charset) ? res.encoding : 'utf-8'
|
507
581
|
|
508
582
|
if res.code.to_i >= 400
|
509
583
|
@redirect_count = nil
|
data/lib/css_parser/rule_set.rb
CHANGED
@@ -513,4 +513,19 @@ module CssParser
|
|
513
513
|
@selectors = selectors.split(',').map { |s| s.gsub(/\s+/, ' ').strip }
|
514
514
|
end
|
515
515
|
end
|
516
|
+
|
517
|
+
class OffsetAwareRuleSet < RuleSet
|
518
|
+
|
519
|
+
# File offset range
|
520
|
+
attr_reader :offset
|
521
|
+
|
522
|
+
# the local or remote location
|
523
|
+
attr_accessor :filename
|
524
|
+
|
525
|
+
def initialize(filename, offset, selectors, block, specificity = nil)
|
526
|
+
super(selectors, block, specificity)
|
527
|
+
@offset = offset
|
528
|
+
@filename = filename
|
529
|
+
end
|
530
|
+
end
|
516
531
|
end
|
data/lib/css_parser/version.rb
CHANGED
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.5.0.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Dunae
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -51,9 +51,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
51
51
|
version: '0'
|
52
52
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
|
-
- - "
|
54
|
+
- - ">"
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version:
|
56
|
+
version: 1.3.1
|
57
57
|
requirements: []
|
58
58
|
rubyforge_project:
|
59
59
|
rubygems_version: 2.6.8
|