css_parser 1.5.0 → 1.12.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 +5 -5
- data/lib/css_parser/parser.rb +122 -118
- data/lib/css_parser/regexps.rb +55 -34
- data/lib/css_parser/rule_set.rb +404 -268
- data/lib/css_parser/version.rb +3 -1
- data/lib/css_parser.rb +28 -36
- metadata +120 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7c20c504cf408fbaf9e5579e3011c485c9e1a7cb0eddc421b9bfc83bf91a9b2c
|
4
|
+
data.tar.gz: 416f126ad73ba30e3754db0e5af9519c416546fadb33c904a98c1bbf4a594f92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c963b193f920250c1f0ee217194720f5865a2a9d7724e7f34b7d8a054fbef78382732d69f22a3a16a320d3f162f14fa0b284fbe9fba9edcbf45d22547594cb07
|
7
|
+
data.tar.gz: afc7f111ffc2e4a42c9c6be5f7e3eb2b52239f7057d0e462ff731e45e5af2d9e6ea05aa7fcb62563538c048225cc8e8f9a8c674fa633fa234e432d8e741bd26a
|
data/lib/css_parser/parser.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CssParser
|
2
4
|
# Exception class used for any errors encountered while downloading remote files.
|
3
5
|
class RemoteFileError < IOError; end
|
@@ -14,13 +16,13 @@ module CssParser
|
|
14
16
|
# [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
|
15
17
|
# [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
|
16
18
|
class Parser
|
17
|
-
USER_AGENT
|
19
|
+
USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)"
|
18
20
|
|
19
|
-
STRIP_CSS_COMMENTS_RX =
|
20
|
-
STRIP_HTML_COMMENTS_RX =
|
21
|
+
STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
|
22
|
+
STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
|
21
23
|
|
22
24
|
# Initial parsing
|
23
|
-
RE_AT_IMPORT_RULE =
|
25
|
+
RE_AT_IMPORT_RULE = /@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s)]*)["']?\)?([\w\s,^\]()]*)\)?[;\n]?/.freeze
|
24
26
|
|
25
27
|
MAX_REDIRECTS = 3
|
26
28
|
|
@@ -34,10 +36,11 @@ module CssParser
|
|
34
36
|
class << self; attr_reader :folded_declaration_cache; end
|
35
37
|
|
36
38
|
def initialize(options = {})
|
37
|
-
@options = {:
|
38
|
-
:
|
39
|
-
:
|
40
|
-
:
|
39
|
+
@options = {absolute_paths: false,
|
40
|
+
import: true,
|
41
|
+
io_exceptions: true,
|
42
|
+
rule_set_exceptions: true,
|
43
|
+
capture_offsets: false}.merge(options)
|
41
44
|
|
42
45
|
# array of RuleSets
|
43
46
|
@rules = []
|
@@ -69,21 +72,20 @@ module CssParser
|
|
69
72
|
# Returns an array of declarations.
|
70
73
|
def find_by_selector(selector, media_types = :all)
|
71
74
|
out = []
|
72
|
-
each_selector(media_types) do |sel, dec,
|
75
|
+
each_selector(media_types) do |sel, dec, _spec|
|
73
76
|
out << dec if sel.strip == selector.strip
|
74
77
|
end
|
75
78
|
out
|
76
79
|
end
|
77
|
-
|
80
|
+
alias [] find_by_selector
|
78
81
|
|
79
82
|
# Finds the rule sets that match the given selectors
|
80
83
|
def find_rule_sets(selectors, media_types = :all)
|
81
84
|
rule_sets = []
|
82
85
|
|
83
86
|
selectors.each do |selector|
|
84
|
-
selector.gsub
|
85
|
-
|
86
|
-
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|
|
87
89
|
if !rule_sets.member?(rule_set) && rule_set.selectors.member?(selector)
|
88
90
|
rule_sets << rule_set
|
89
91
|
end
|
@@ -114,9 +116,9 @@ module CssParser
|
|
114
116
|
# parser = CssParser::Parser.new
|
115
117
|
# parser.add_block!(css)
|
116
118
|
def add_block!(block, options = {})
|
117
|
-
options = {:
|
118
|
-
options[:media_types] = [options[:media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
|
119
|
-
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) }
|
120
122
|
|
121
123
|
block = cleanup_block(block, options)
|
122
124
|
|
@@ -128,19 +130,19 @@ module CssParser
|
|
128
130
|
if @options[:import]
|
129
131
|
block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
|
130
132
|
media_types = []
|
131
|
-
if media_string = import_rule[-1]
|
132
|
-
media_string.split(
|
133
|
+
if (media_string = import_rule[-1])
|
134
|
+
media_string.split(/,/).each do |t|
|
133
135
|
media_types << CssParser.sanitize_media_query(t) unless t.empty?
|
134
136
|
end
|
135
137
|
else
|
136
138
|
media_types = [:all]
|
137
139
|
end
|
138
140
|
|
139
|
-
next unless options[:only_media_types].include?(:all) or media_types.
|
141
|
+
next unless options[:only_media_types].include?(:all) or media_types.empty? or !(media_types & options[:only_media_types]).empty?
|
140
142
|
|
141
143
|
import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
|
142
144
|
|
143
|
-
import_options = {
|
145
|
+
import_options = {media_types: media_types}
|
144
146
|
import_options[:capture_offsets] = true if options[:capture_offsets]
|
145
147
|
|
146
148
|
if options[:base_uri]
|
@@ -166,6 +168,8 @@ module CssParser
|
|
166
168
|
def add_rule!(selectors, declarations, media_types = :all)
|
167
169
|
rule_set = RuleSet.new(selectors, declarations)
|
168
170
|
add_rule_set!(rule_set, media_types)
|
171
|
+
rescue ArgumentError => e
|
172
|
+
raise e if @options[:rule_set_exceptions]
|
169
173
|
end
|
170
174
|
|
171
175
|
# Add a CSS rule by setting the +selectors+, +declarations+, +filename+, +offset+ and +media_types+.
|
@@ -182,20 +186,21 @@ module CssParser
|
|
182
186
|
#
|
183
187
|
# +media_types+ can be a symbol or an array of symbols.
|
184
188
|
def add_rule_set!(ruleset, media_types = :all)
|
185
|
-
raise ArgumentError unless ruleset.
|
189
|
+
raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
|
186
190
|
|
187
|
-
media_types = [media_types]
|
191
|
+
media_types = [media_types] unless media_types.is_a?(Array)
|
192
|
+
media_types = media_types.flat_map { |mt| CssParser.sanitize_media_query(mt) }
|
188
193
|
|
189
|
-
@rules << {:
|
194
|
+
@rules << {media_types: media_types, rules: ruleset}
|
190
195
|
end
|
191
196
|
|
192
197
|
# Remove a CssParser RuleSet object.
|
193
198
|
#
|
194
199
|
# +media_types+ can be a symbol or an array of symbols.
|
195
200
|
def remove_rule_set!(ruleset, media_types = :all)
|
196
|
-
raise ArgumentError unless ruleset.
|
201
|
+
raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
|
197
202
|
|
198
|
-
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) }
|
199
204
|
|
200
205
|
@rules.reject! do |rule|
|
201
206
|
rule[:media_types] == media_types && rule[:rules].to_s == ruleset.to_s
|
@@ -207,7 +212,7 @@ module CssParser
|
|
207
212
|
# +media_types+ can be a symbol or an array of symbols.
|
208
213
|
def each_rule_set(media_types = :all) # :yields: rule_set, media_types
|
209
214
|
media_types = [:all] if media_types.nil?
|
210
|
-
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) }
|
211
216
|
|
212
217
|
@rules.each do |block|
|
213
218
|
if media_types.include?(:all) or block[:media_types].any? { |mt| media_types.include?(mt) }
|
@@ -220,7 +225,7 @@ module CssParser
|
|
220
225
|
def to_h(which_media = :all)
|
221
226
|
out = {}
|
222
227
|
styles_by_media_types = {}
|
223
|
-
each_selector(which_media) do |selectors, declarations,
|
228
|
+
each_selector(which_media) do |selectors, declarations, _specificity, media_types|
|
224
229
|
media_types.each do |media_type|
|
225
230
|
styles_by_media_types[media_type] ||= []
|
226
231
|
styles_by_media_types[media_type] << [selectors, declarations]
|
@@ -242,6 +247,8 @@ module CssParser
|
|
242
247
|
# +media_types+ can be a symbol or an array of symbols.
|
243
248
|
# See RuleSet#each_selector for +options+.
|
244
249
|
def each_selector(all_media_types = :all, options = {}) # :yields: selectors, declarations, specificity, media_types
|
250
|
+
return to_enum(__method__, all_media_types, options) unless block_given?
|
251
|
+
|
245
252
|
each_rule_set(all_media_types) do |rule_set, media_types|
|
246
253
|
rule_set.each_selector(options) do |selectors, declarations, specificity|
|
247
254
|
yield selectors, declarations, specificity, media_types
|
@@ -251,9 +258,10 @@ module CssParser
|
|
251
258
|
|
252
259
|
# Output all CSS rules as a single stylesheet.
|
253
260
|
def to_s(which_media = :all)
|
254
|
-
out =
|
261
|
+
out = []
|
255
262
|
styles_by_media_types = {}
|
256
|
-
|
263
|
+
|
264
|
+
each_selector(which_media) do |selectors, declarations, _specificity, media_types|
|
257
265
|
media_types.each do |media_type|
|
258
266
|
styles_by_media_types[media_type] ||= []
|
259
267
|
styles_by_media_types[media_type] << [selectors, declarations]
|
@@ -262,20 +270,21 @@ module CssParser
|
|
262
270
|
|
263
271
|
styles_by_media_types.each_pair do |media_type, media_styles|
|
264
272
|
media_block = (media_type != :all)
|
265
|
-
out
|
273
|
+
out << "@media #{media_type} {" if media_block
|
266
274
|
|
267
275
|
media_styles.each do |media_style|
|
268
276
|
if media_block
|
269
|
-
out
|
277
|
+
out.push(" #{media_style[0]} {\n #{media_style[1]}\n }")
|
270
278
|
else
|
271
|
-
out
|
279
|
+
out.push("#{media_style[0]} {\n#{media_style[1]}\n}")
|
272
280
|
end
|
273
281
|
end
|
274
282
|
|
275
|
-
out
|
283
|
+
out << '}' if media_block
|
276
284
|
end
|
277
285
|
|
278
|
-
out
|
286
|
+
out << ''
|
287
|
+
out.join("\n")
|
279
288
|
end
|
280
289
|
|
281
290
|
# A hash of { :media_query => rule_sets }
|
@@ -283,7 +292,7 @@ module CssParser
|
|
283
292
|
rules_by_media = {}
|
284
293
|
@rules.each do |block|
|
285
294
|
block[:media_types].each do |mt|
|
286
|
-
unless rules_by_media.
|
295
|
+
unless rules_by_media.key?(mt)
|
287
296
|
rules_by_media[mt] = []
|
288
297
|
end
|
289
298
|
rules_by_media[mt] << block[:rules]
|
@@ -295,15 +304,13 @@ module CssParser
|
|
295
304
|
|
296
305
|
# Merge declarations with the same selector.
|
297
306
|
def compact! # :nodoc:
|
298
|
-
|
299
|
-
|
300
|
-
compacted
|
307
|
+
[]
|
301
308
|
end
|
302
309
|
|
303
310
|
def parse_block_into_rule_sets!(block, options = {}) # :nodoc:
|
304
311
|
current_media_queries = [:all]
|
305
312
|
if options[:media_types]
|
306
|
-
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) }
|
307
314
|
end
|
308
315
|
|
309
316
|
in_declarations = 0
|
@@ -314,44 +321,43 @@ module CssParser
|
|
314
321
|
in_at_media_rule = false
|
315
322
|
in_media_block = false
|
316
323
|
|
317
|
-
current_selectors =
|
318
|
-
current_media_query =
|
319
|
-
current_declarations =
|
324
|
+
current_selectors = String.new
|
325
|
+
current_media_query = String.new
|
326
|
+
current_declarations = String.new
|
320
327
|
|
321
328
|
# once we are in a rule, we will use this to store where we started if we are capturing offsets
|
322
329
|
rule_start = nil
|
323
330
|
offset = nil
|
324
331
|
|
325
|
-
block.scan(
|
326
|
-
token = matches[0]
|
327
|
-
|
332
|
+
block.scan(/\s+|\\{2,}|\\?[{}\s"]|.[^\s"{}\\]*/) do |token|
|
328
333
|
# save the regex offset so that we know where in the file we are
|
329
334
|
offset = Regexp.last_match.offset(0) if options[:capture_offsets]
|
330
335
|
|
331
|
-
if token
|
336
|
+
if token.start_with?('"') # found un-escaped double quote
|
332
337
|
in_string = !in_string
|
333
338
|
end
|
334
339
|
|
335
340
|
if in_declarations > 0
|
336
341
|
# too deep, malformed declaration block
|
337
342
|
if in_declarations > 1
|
338
|
-
in_declarations -= 1 if token
|
343
|
+
in_declarations -= 1 if token.include?('}')
|
339
344
|
next
|
340
345
|
end
|
341
346
|
|
342
|
-
if
|
347
|
+
if !in_string && token.include?('{')
|
343
348
|
in_declarations += 1
|
344
349
|
next
|
345
350
|
end
|
346
351
|
|
347
|
-
current_declarations
|
352
|
+
current_declarations << token
|
348
353
|
|
349
|
-
if
|
350
|
-
current_declarations.gsub!(/\}
|
354
|
+
if !in_string && token.include?('}')
|
355
|
+
current_declarations.gsub!(/\}\s*$/, '')
|
351
356
|
|
352
357
|
in_declarations -= 1
|
358
|
+
current_declarations.strip!
|
353
359
|
|
354
|
-
unless current_declarations.
|
360
|
+
unless current_declarations.empty?
|
355
361
|
if options[:capture_offsets]
|
356
362
|
add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
|
357
363
|
else
|
@@ -359,8 +365,8 @@ module CssParser
|
|
359
365
|
end
|
360
366
|
end
|
361
367
|
|
362
|
-
current_selectors =
|
363
|
-
current_declarations =
|
368
|
+
current_selectors = String.new
|
369
|
+
current_declarations = String.new
|
364
370
|
|
365
371
|
# restart our search for selectors and declarations
|
366
372
|
rule_start = nil if options[:capture_offsets]
|
@@ -370,56 +376,54 @@ module CssParser
|
|
370
376
|
in_at_media_rule = true
|
371
377
|
current_media_queries = []
|
372
378
|
elsif in_at_media_rule
|
373
|
-
if token
|
374
|
-
block_depth
|
379
|
+
if token.include?('{')
|
380
|
+
block_depth += 1
|
375
381
|
in_at_media_rule = false
|
376
382
|
in_media_block = true
|
377
383
|
current_media_queries << CssParser.sanitize_media_query(current_media_query)
|
378
|
-
current_media_query =
|
379
|
-
elsif token
|
384
|
+
current_media_query = String.new
|
385
|
+
elsif token.include?(',')
|
380
386
|
# new media query begins
|
381
|
-
token.
|
382
|
-
|
387
|
+
token.tr!(',', ' ')
|
388
|
+
token.strip!
|
389
|
+
current_media_query << token << ' '
|
383
390
|
current_media_queries << CssParser.sanitize_media_query(current_media_query)
|
384
|
-
current_media_query =
|
391
|
+
current_media_query = String.new
|
385
392
|
else
|
386
|
-
|
393
|
+
token.strip!
|
394
|
+
current_media_query << token << ' '
|
387
395
|
end
|
388
396
|
elsif in_charset or token =~ /@charset/i
|
389
397
|
# iterate until we are out of the charset declaration
|
390
|
-
in_charset =
|
398
|
+
in_charset = !token.include?(';')
|
399
|
+
elsif !in_string && token.include?('}')
|
400
|
+
block_depth -= 1
|
401
|
+
|
402
|
+
# reset the current media query scope
|
403
|
+
if in_media_block
|
404
|
+
current_media_queries = [:all]
|
405
|
+
in_media_block = false
|
406
|
+
end
|
407
|
+
elsif !in_string && token.include?('{')
|
408
|
+
current_selectors.strip!
|
409
|
+
in_declarations += 1
|
391
410
|
else
|
392
|
-
if token
|
393
|
-
|
411
|
+
# if we are in a selector, add the token to the current selectors
|
412
|
+
current_selectors << token
|
394
413
|
|
395
|
-
|
396
|
-
|
397
|
-
current_media_queries = [:all]
|
398
|
-
in_media_block = false
|
399
|
-
end
|
400
|
-
else
|
401
|
-
if token =~ /\{/ and not in_string
|
402
|
-
current_selectors.strip!
|
403
|
-
in_declarations += 1
|
404
|
-
else
|
405
|
-
# if we are in a selector, add the token to the current selectors
|
406
|
-
current_selectors += token
|
407
|
-
|
408
|
-
# mark this as the beginning of the selector unless we have already marked it
|
409
|
-
rule_start = offset.first if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
|
410
|
-
end
|
411
|
-
end
|
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]+$/
|
412
416
|
end
|
413
417
|
end
|
414
418
|
|
415
419
|
# check for unclosed braces
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
add_rule!(current_selectors, current_declarations, current_media_queries)
|
421
|
-
end
|
420
|
+
return unless in_declarations > 0
|
421
|
+
|
422
|
+
unless options[:capture_offsets]
|
423
|
+
return add_rule!(current_selectors, current_declarations, current_media_queries)
|
422
424
|
end
|
425
|
+
|
426
|
+
add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
|
423
427
|
end
|
424
428
|
|
425
429
|
# Load a remote CSS file.
|
@@ -432,7 +436,7 @@ module CssParser
|
|
432
436
|
def load_uri!(uri, options = {}, deprecated = nil)
|
433
437
|
uri = Addressable::URI.parse(uri) unless uri.respond_to? :scheme
|
434
438
|
|
435
|
-
opts = {:
|
439
|
+
opts = {base_uri: nil, media_types: :all}
|
436
440
|
|
437
441
|
if options.is_a? Hash
|
438
442
|
opts.merge!(options)
|
@@ -452,14 +456,13 @@ module CssParser
|
|
452
456
|
opts[:filename] = uri.to_s if opts[:capture_offsets]
|
453
457
|
|
454
458
|
src, = read_remote_file(uri) # skip charset
|
455
|
-
|
456
|
-
|
457
|
-
end
|
459
|
+
|
460
|
+
add_block!(src, opts) if src
|
458
461
|
end
|
459
462
|
|
460
463
|
# Load a local CSS file.
|
461
464
|
def load_file!(file_name, options = {}, deprecated = nil)
|
462
|
-
opts = {:
|
465
|
+
opts = {base_dir: nil, media_types: :all}
|
463
466
|
|
464
467
|
if options.is_a? Hash
|
465
468
|
opts.merge!(options)
|
@@ -482,7 +485,7 @@ module CssParser
|
|
482
485
|
|
483
486
|
# Load a local CSS string.
|
484
487
|
def load_string!(src, options = {}, deprecated = nil)
|
485
|
-
opts = {:
|
488
|
+
opts = {base_dir: nil, media_types: :all}
|
486
489
|
|
487
490
|
if options.is_a? Hash
|
488
491
|
opts.merge!(options)
|
@@ -494,9 +497,8 @@ module CssParser
|
|
494
497
|
add_block!(src, opts)
|
495
498
|
end
|
496
499
|
|
497
|
-
|
498
|
-
|
499
500
|
protected
|
501
|
+
|
500
502
|
# Check that a path hasn't been loaded already
|
501
503
|
#
|
502
504
|
# Raises a CircularReferenceError exception if io_exceptions are on,
|
@@ -505,10 +507,11 @@ module CssParser
|
|
505
507
|
path = path.to_s
|
506
508
|
if @loaded_uris.include?(path)
|
507
509
|
raise CircularReferenceError, "can't load #{path} more than once" if @options[:io_exceptions]
|
508
|
-
|
510
|
+
|
511
|
+
false
|
509
512
|
else
|
510
513
|
@loaded_uris << path
|
511
|
-
|
514
|
+
true
|
512
515
|
end
|
513
516
|
end
|
514
517
|
|
@@ -528,7 +531,7 @@ module CssParser
|
|
528
531
|
# Returns a string.
|
529
532
|
def cleanup_block(block, options = {}) # :nodoc:
|
530
533
|
# Strip CSS comments
|
531
|
-
utf8_block = block.encode('UTF-8', '
|
534
|
+
utf8_block = block.encode('UTF-8', 'UTF-8', invalid: :replace, undef: :replace, replace: ' ')
|
532
535
|
utf8_block = ignore_pattern(utf8_block, STRIP_CSS_COMMENTS_RX, options)
|
533
536
|
|
534
537
|
# Strip HTML comments - they shouldn't really be in here but
|
@@ -536,7 +539,7 @@ module CssParser
|
|
536
539
|
utf8_block = ignore_pattern(utf8_block, STRIP_HTML_COMMENTS_RX, options)
|
537
540
|
|
538
541
|
# Strip lines containing just whitespace
|
539
|
-
utf8_block.gsub!(/^\s+$/,
|
542
|
+
utf8_block.gsub!(/^\s+$/, '') unless options[:capture_offsets]
|
540
543
|
|
541
544
|
utf8_block
|
542
545
|
end
|
@@ -572,11 +575,8 @@ module CssParser
|
|
572
575
|
if uri.scheme == 'file'
|
573
576
|
# local file
|
574
577
|
path = uri.path
|
575
|
-
path.gsub!(
|
576
|
-
|
577
|
-
src = fh.read
|
578
|
-
charset = fh.respond_to?(:charset) ? fh.charset : 'utf-8'
|
579
|
-
fh.close
|
578
|
+
path.gsub!(%r{^/}, '') if Gem.win_platform?
|
579
|
+
src = File.read(path, mode: 'rb')
|
580
580
|
else
|
581
581
|
# remote file
|
582
582
|
if uri.scheme == 'https'
|
@@ -594,21 +594,22 @@ module CssParser
|
|
594
594
|
|
595
595
|
if res.code.to_i >= 400
|
596
596
|
@redirect_count = nil
|
597
|
-
raise RemoteFileError
|
597
|
+
raise RemoteFileError, uri.to_s if @options[:io_exceptions]
|
598
|
+
|
598
599
|
return '', nil
|
599
600
|
elsif res.code.to_i >= 300 and res.code.to_i < 400
|
600
|
-
|
601
|
+
unless res['Location'].nil?
|
601
602
|
return read_remote_file Addressable::URI.parse(Addressable::URI.escape(res['Location']))
|
602
603
|
end
|
603
604
|
end
|
604
605
|
|
605
606
|
case res['content-encoding']
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
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)
|
612
613
|
end
|
613
614
|
end
|
614
615
|
|
@@ -622,15 +623,17 @@ module CssParser
|
|
622
623
|
end
|
623
624
|
rescue
|
624
625
|
@redirect_count = nil
|
625
|
-
raise RemoteFileError
|
626
|
+
raise RemoteFileError, uri.to_s if @options[:io_exceptions]
|
627
|
+
|
626
628
|
return nil, nil
|
627
629
|
end
|
628
630
|
|
629
631
|
@redirect_count = nil
|
630
|
-
|
632
|
+
[src, charset]
|
631
633
|
end
|
632
634
|
|
633
635
|
private
|
636
|
+
|
634
637
|
# Save a folded declaration block to the internal cache.
|
635
638
|
def save_folded_declaration(block_hash, folded_declaration) # :nodoc:
|
636
639
|
@folded_declaration_cache[block_hash] = folded_declaration
|
@@ -638,7 +641,7 @@ module CssParser
|
|
638
641
|
|
639
642
|
# Retrieve a folded declaration block from the internal cache.
|
640
643
|
def get_folded_declaration(block_hash) # :nodoc:
|
641
|
-
|
644
|
+
@folded_declaration_cache[block_hash] ||= nil
|
642
645
|
end
|
643
646
|
|
644
647
|
def reset! # :nodoc:
|
@@ -652,14 +655,15 @@ module CssParser
|
|
652
655
|
# passed hash
|
653
656
|
def css_node_to_h(hash, key, val)
|
654
657
|
hash[key.strip] = '' and return hash if val.nil?
|
658
|
+
|
655
659
|
lines = val.split(';')
|
656
660
|
nodes = {}
|
657
661
|
lines.each do |line|
|
658
662
|
parts = line.split(':', 2)
|
659
|
-
if
|
663
|
+
if parts[1] =~ /:/
|
660
664
|
nodes[parts[0]] = css_node_to_h(hash, parts[0], parts[1])
|
661
665
|
else
|
662
|
-
nodes[parts[0].to_s.strip] =parts[1].to_s.strip
|
666
|
+
nodes[parts[0].to_s.strip] = parts[1].to_s.strip
|
663
667
|
end
|
664
668
|
end
|
665
669
|
hash[key.strip] = nodes
|