css_parser 1.7.1 → 1.8.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.rb +23 -33
- data/lib/css_parser/parser.rb +94 -98
- data/lib/css_parser/regexps.rb +36 -33
- data/lib/css_parser/rule_set.rb +343 -230
- data/lib/css_parser/version.rb +3 -1
- metadata +116 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c049fe40a4b774b56e00c790cf18b78c8a94298185435471893cedff65a79c17
|
4
|
+
data.tar.gz: fd0600e1859e8bd188a12df4ccd252a1f75f4ae2f98707249c7750e622d5e2b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53b2f435c485d393778679d3202943a99b83607af4ce5edac9a15aac4461fb252371b43d98b7bf1886254b143da9bff5a61492d7a36e74e44e77b44f8ba38318
|
7
|
+
data.tar.gz: ab5fb0504811ebd05eb09e830447f832b67aad5e5559dd4be9e562c20d5bb4cc786165eb961b39867e8316c72ce90015de16e1857154784b4bf0fc700b92a34b
|
data/lib/css_parser.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'addressable/uri'
|
3
4
|
require 'uri'
|
4
5
|
require 'net/https'
|
@@ -13,7 +14,6 @@ require 'css_parser/regexps'
|
|
13
14
|
require 'css_parser/parser'
|
14
15
|
|
15
16
|
module CssParser
|
16
|
-
|
17
17
|
# Merge multiple CSS RuleSets by cascading according to the CSS 2.1 cascading rules
|
18
18
|
# (http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order).
|
19
19
|
#
|
@@ -56,10 +56,10 @@ module CssParser
|
|
56
56
|
@folded_declaration_cache = {}
|
57
57
|
|
58
58
|
# in case called like CssParser.merge([rule_set, rule_set])
|
59
|
-
rule_sets.flatten! if rule_sets[0].
|
59
|
+
rule_sets.flatten! if rule_sets[0].is_a?(Array)
|
60
60
|
|
61
|
-
unless rule_sets.all? {|rs| rs.
|
62
|
-
raise ArgumentError,
|
61
|
+
unless rule_sets.all? { |rs| rs.is_a?(CssParser::RuleSet) }
|
62
|
+
raise ArgumentError, 'all parameters must be CssParser::RuleSets.'
|
63
63
|
end
|
64
64
|
|
65
65
|
return rule_sets[0] if rule_sets.length == 1
|
@@ -71,38 +71,27 @@ module CssParser
|
|
71
71
|
rule_set.expand_shorthand!
|
72
72
|
|
73
73
|
specificity = rule_set.specificity
|
74
|
-
|
75
|
-
if rule_set.selectors.length == 0
|
76
|
-
specificity = 0
|
77
|
-
else
|
78
|
-
specificity = rule_set.selectors.map { |s| calculate_specificity(s) }.compact.max || 0
|
79
|
-
end
|
80
|
-
end
|
74
|
+
specificity ||= rule_set.selectors.map { |s| calculate_specificity(s) }.compact.max || 0
|
81
75
|
|
82
76
|
rule_set.each_declaration do |property, value, is_important|
|
83
77
|
# Add the property to the list to be folded per http://www.w3.org/TR/CSS21/cascade.html#cascading-order
|
84
|
-
if not properties.
|
85
|
-
properties[property] = {:
|
78
|
+
if not properties.key?(property)
|
79
|
+
properties[property] = {value: value, specificity: specificity, is_important: is_important}
|
86
80
|
elsif is_important
|
87
81
|
if not properties[property][:is_important] or properties[property][:specificity] <= specificity
|
88
|
-
properties[property] = {:
|
82
|
+
properties[property] = {value: value, specificity: specificity, is_important: is_important}
|
89
83
|
end
|
90
84
|
elsif properties[property][:specificity] < specificity or properties[property][:specificity] == specificity
|
91
85
|
unless properties[property][:is_important]
|
92
|
-
properties[property] = {:
|
86
|
+
properties[property] = {value: value, specificity: specificity, is_important: is_important}
|
93
87
|
end
|
94
88
|
end
|
95
|
-
|
89
|
+
end
|
96
90
|
end
|
97
91
|
|
98
|
-
merged = RuleSet.new(nil, nil)
|
99
|
-
|
100
|
-
|
101
|
-
if details[:is_important]
|
102
|
-
merged[property.strip] = details[:value].strip.gsub(/\;\Z/, '') + '!important'
|
103
|
-
else
|
104
|
-
merged[property.strip] = details[:value].strip
|
105
|
-
end
|
92
|
+
merged = properties.each_with_object(RuleSet.new(nil, nil)) do |(property, details), rule_set|
|
93
|
+
value = details[:value].strip
|
94
|
+
rule_set[property.strip] = details[:is_important] ? "#{value.gsub(/;\Z/, '')}!important" : value
|
106
95
|
end
|
107
96
|
|
108
97
|
merged.create_shorthand!
|
@@ -128,7 +117,7 @@ module CssParser
|
|
128
117
|
|
129
118
|
"#{a}#{b}#{c}#{d}".to_i
|
130
119
|
rescue
|
131
|
-
|
120
|
+
0
|
132
121
|
end
|
133
122
|
|
134
123
|
# Make <tt>url()</tt> links absolute.
|
@@ -145,23 +134,24 @@ module CssParser
|
|
145
134
|
# "http://example.org/style/basic.css").inspect
|
146
135
|
# => "body { background: url('http://example.org/style/yellow.png?abc=123') };"
|
147
136
|
def self.convert_uris(css, base_uri)
|
148
|
-
base_uri = Addressable::URI.parse(base_uri) unless base_uri.
|
137
|
+
base_uri = Addressable::URI.parse(base_uri) unless base_uri.is_a?(Addressable::URI)
|
149
138
|
|
150
139
|
css.gsub(URI_RX) do
|
151
|
-
uri =
|
152
|
-
uri.gsub!(/["']+/, '')
|
140
|
+
uri = Regexp.last_match(1).to_s.gsub(/["']+/, '')
|
153
141
|
# Don't process URLs that are already absolute
|
154
|
-
unless uri
|
142
|
+
unless uri.match(%r{^[a-z]+://}i)
|
155
143
|
begin
|
156
|
-
uri = base_uri
|
157
|
-
rescue
|
144
|
+
uri = base_uri.join(uri)
|
145
|
+
rescue
|
146
|
+
nil
|
147
|
+
end
|
158
148
|
end
|
159
|
-
"url('#{uri
|
149
|
+
"url('#{uri}')"
|
160
150
|
end
|
161
151
|
end
|
162
152
|
|
163
153
|
def self.sanitize_media_query(raw)
|
164
|
-
mq = raw.to_s.gsub(
|
154
|
+
mq = raw.to_s.gsub(/\s+/, ' ')
|
165
155
|
mq.strip!
|
166
156
|
mq = 'all' if mq.empty?
|
167
157
|
mq.to_sym
|
data/lib/css_parser/parser.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module CssParser
|
3
4
|
# Exception class used for any errors encountered while downloading remote files.
|
4
5
|
class RemoteFileError < IOError; end
|
@@ -15,13 +16,13 @@ module CssParser
|
|
15
16
|
# [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
|
16
17
|
# [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
|
17
18
|
class Parser
|
18
|
-
USER_AGENT
|
19
|
+
USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)"
|
19
20
|
|
20
|
-
STRIP_CSS_COMMENTS_RX =
|
21
|
-
STRIP_HTML_COMMENTS_RX =
|
21
|
+
STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
|
22
|
+
STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
|
22
23
|
|
23
24
|
# Initial parsing
|
24
|
-
RE_AT_IMPORT_RULE =
|
25
|
+
RE_AT_IMPORT_RULE = /@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s)]*)["']?\)?([\w\s,^\]()]*)\)?[;\n]?/.freeze
|
25
26
|
|
26
27
|
MAX_REDIRECTS = 3
|
27
28
|
|
@@ -35,10 +36,10 @@ module CssParser
|
|
35
36
|
class << self; attr_reader :folded_declaration_cache; end
|
36
37
|
|
37
38
|
def initialize(options = {})
|
38
|
-
@options = {:
|
39
|
-
:
|
40
|
-
:
|
41
|
-
:
|
39
|
+
@options = {absolute_paths: false,
|
40
|
+
import: true,
|
41
|
+
io_exceptions: true,
|
42
|
+
capture_offsets: false}.merge(options)
|
42
43
|
|
43
44
|
# array of RuleSets
|
44
45
|
@rules = []
|
@@ -70,21 +71,20 @@ module CssParser
|
|
70
71
|
# Returns an array of declarations.
|
71
72
|
def find_by_selector(selector, media_types = :all)
|
72
73
|
out = []
|
73
|
-
each_selector(media_types) do |sel, dec,
|
74
|
+
each_selector(media_types) do |sel, dec, _spec|
|
74
75
|
out << dec if sel.strip == selector.strip
|
75
76
|
end
|
76
77
|
out
|
77
78
|
end
|
78
|
-
|
79
|
+
alias [] find_by_selector
|
79
80
|
|
80
81
|
# Finds the rule sets that match the given selectors
|
81
82
|
def find_rule_sets(selectors, media_types = :all)
|
82
83
|
rule_sets = []
|
83
84
|
|
84
85
|
selectors.each do |selector|
|
85
|
-
selector.gsub
|
86
|
-
|
87
|
-
each_rule_set(media_types) do |rule_set, media_type|
|
86
|
+
selector = selector.gsub(/\s+/, ' ').strip
|
87
|
+
each_rule_set(media_types) do |rule_set, _media_type|
|
88
88
|
if !rule_sets.member?(rule_set) && rule_set.selectors.member?(selector)
|
89
89
|
rule_sets << rule_set
|
90
90
|
end
|
@@ -115,9 +115,9 @@ module CssParser
|
|
115
115
|
# parser = CssParser::Parser.new
|
116
116
|
# parser.add_block!(css)
|
117
117
|
def add_block!(block, options = {})
|
118
|
-
options = {:
|
119
|
-
options[:media_types] = [options[:media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
|
120
|
-
options[:only_media_types] = [options[:only_media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
|
118
|
+
options = {base_uri: nil, base_dir: nil, charset: nil, media_types: :all, only_media_types: :all}.merge(options)
|
119
|
+
options[:media_types] = [options[:media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
|
120
|
+
options[:only_media_types] = [options[:only_media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
|
121
121
|
|
122
122
|
block = cleanup_block(block, options)
|
123
123
|
|
@@ -129,19 +129,19 @@ module CssParser
|
|
129
129
|
if @options[:import]
|
130
130
|
block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
|
131
131
|
media_types = []
|
132
|
-
if media_string = import_rule[-1]
|
133
|
-
media_string.split(
|
132
|
+
if (media_string = import_rule[-1])
|
133
|
+
media_string.split(/,/).each do |t|
|
134
134
|
media_types << CssParser.sanitize_media_query(t) unless t.empty?
|
135
135
|
end
|
136
136
|
else
|
137
137
|
media_types = [:all]
|
138
138
|
end
|
139
139
|
|
140
|
-
next unless options[:only_media_types].include?(:all) or media_types.
|
140
|
+
next unless options[:only_media_types].include?(:all) or media_types.empty? or !(media_types & options[:only_media_types]).empty?
|
141
141
|
|
142
142
|
import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
|
143
143
|
|
144
|
-
import_options = {
|
144
|
+
import_options = {media_types: media_types}
|
145
145
|
import_options[:capture_offsets] = true if options[:capture_offsets]
|
146
146
|
|
147
147
|
if options[:base_uri]
|
@@ -183,21 +183,21 @@ module CssParser
|
|
183
183
|
#
|
184
184
|
# +media_types+ can be a symbol or an array of symbols.
|
185
185
|
def add_rule_set!(ruleset, media_types = :all)
|
186
|
-
raise ArgumentError unless ruleset.
|
186
|
+
raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
|
187
187
|
|
188
|
-
media_types = [media_types] unless Array
|
189
|
-
media_types = media_types.flat_map { |mt| CssParser.sanitize_media_query(mt)}
|
188
|
+
media_types = [media_types] unless media_types.is_a?(Array)
|
189
|
+
media_types = media_types.flat_map { |mt| CssParser.sanitize_media_query(mt) }
|
190
190
|
|
191
|
-
@rules << {:
|
191
|
+
@rules << {media_types: media_types, rules: ruleset}
|
192
192
|
end
|
193
193
|
|
194
194
|
# Remove a CssParser RuleSet object.
|
195
195
|
#
|
196
196
|
# +media_types+ can be a symbol or an array of symbols.
|
197
197
|
def remove_rule_set!(ruleset, media_types = :all)
|
198
|
-
raise ArgumentError unless ruleset.
|
198
|
+
raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
|
199
199
|
|
200
|
-
media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
|
200
|
+
media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
|
201
201
|
|
202
202
|
@rules.reject! do |rule|
|
203
203
|
rule[:media_types] == media_types && rule[:rules].to_s == ruleset.to_s
|
@@ -209,7 +209,7 @@ module CssParser
|
|
209
209
|
# +media_types+ can be a symbol or an array of symbols.
|
210
210
|
def each_rule_set(media_types = :all) # :yields: rule_set, media_types
|
211
211
|
media_types = [:all] if media_types.nil?
|
212
|
-
media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
|
212
|
+
media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
|
213
213
|
|
214
214
|
@rules.each do |block|
|
215
215
|
if media_types.include?(:all) or block[:media_types].any? { |mt| media_types.include?(mt) }
|
@@ -222,7 +222,7 @@ module CssParser
|
|
222
222
|
def to_h(which_media = :all)
|
223
223
|
out = {}
|
224
224
|
styles_by_media_types = {}
|
225
|
-
each_selector(which_media) do |selectors, declarations,
|
225
|
+
each_selector(which_media) do |selectors, declarations, _specificity, media_types|
|
226
226
|
media_types.each do |media_type|
|
227
227
|
styles_by_media_types[media_type] ||= []
|
228
228
|
styles_by_media_types[media_type] << [selectors, declarations]
|
@@ -244,7 +244,7 @@ module CssParser
|
|
244
244
|
# +media_types+ can be a symbol or an array of symbols.
|
245
245
|
# See RuleSet#each_selector for +options+.
|
246
246
|
def each_selector(all_media_types = :all, options = {}) # :yields: selectors, declarations, specificity, media_types
|
247
|
-
return to_enum(
|
247
|
+
return to_enum(__method__, all_media_types, options) unless block_given?
|
248
248
|
|
249
249
|
each_rule_set(all_media_types) do |rule_set, media_types|
|
250
250
|
rule_set.each_selector(options) do |selectors, declarations, specificity|
|
@@ -255,9 +255,10 @@ module CssParser
|
|
255
255
|
|
256
256
|
# Output all CSS rules as a single stylesheet.
|
257
257
|
def to_s(which_media = :all)
|
258
|
-
out =
|
258
|
+
out = []
|
259
259
|
styles_by_media_types = {}
|
260
|
-
|
260
|
+
|
261
|
+
each_selector(which_media) do |selectors, declarations, _specificity, media_types|
|
261
262
|
media_types.each do |media_type|
|
262
263
|
styles_by_media_types[media_type] ||= []
|
263
264
|
styles_by_media_types[media_type] << [selectors, declarations]
|
@@ -266,20 +267,21 @@ module CssParser
|
|
266
267
|
|
267
268
|
styles_by_media_types.each_pair do |media_type, media_styles|
|
268
269
|
media_block = (media_type != :all)
|
269
|
-
out << "@media #{media_type} {
|
270
|
+
out << "@media #{media_type} {" if media_block
|
270
271
|
|
271
272
|
media_styles.each do |media_style|
|
272
273
|
if media_block
|
273
|
-
out
|
274
|
+
out.push(" #{media_style[0]} {\n #{media_style[1]}\n }")
|
274
275
|
else
|
275
|
-
out
|
276
|
+
out.push("#{media_style[0]} {\n#{media_style[1]}\n}")
|
276
277
|
end
|
277
278
|
end
|
278
279
|
|
279
|
-
out <<
|
280
|
+
out << '}' if media_block
|
280
281
|
end
|
281
282
|
|
282
|
-
out
|
283
|
+
out << ''
|
284
|
+
out.join("\n")
|
283
285
|
end
|
284
286
|
|
285
287
|
# A hash of { :media_query => rule_sets }
|
@@ -287,7 +289,7 @@ module CssParser
|
|
287
289
|
rules_by_media = {}
|
288
290
|
@rules.each do |block|
|
289
291
|
block[:media_types].each do |mt|
|
290
|
-
unless rules_by_media.
|
292
|
+
unless rules_by_media.key?(mt)
|
291
293
|
rules_by_media[mt] = []
|
292
294
|
end
|
293
295
|
rules_by_media[mt] << block[:rules]
|
@@ -299,15 +301,13 @@ module CssParser
|
|
299
301
|
|
300
302
|
# Merge declarations with the same selector.
|
301
303
|
def compact! # :nodoc:
|
302
|
-
|
303
|
-
|
304
|
-
compacted
|
304
|
+
[]
|
305
305
|
end
|
306
306
|
|
307
307
|
def parse_block_into_rule_sets!(block, options = {}) # :nodoc:
|
308
308
|
current_media_queries = [:all]
|
309
309
|
if options[:media_types]
|
310
|
-
current_media_queries = options[:media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
|
310
|
+
current_media_queries = options[:media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
|
311
311
|
end
|
312
312
|
|
313
313
|
in_declarations = 0
|
@@ -326,7 +326,7 @@ module CssParser
|
|
326
326
|
rule_start = nil
|
327
327
|
offset = nil
|
328
328
|
|
329
|
-
block.scan(/\s
|
329
|
+
block.scan(/\s+|\\{2,}|\\?[{}\s"]|.[^\s"{}\\]*/) do |token|
|
330
330
|
# save the regex offset so that we know where in the file we are
|
331
331
|
offset = Regexp.last_match.offset(0) if options[:capture_offsets]
|
332
332
|
|
@@ -349,7 +349,7 @@ module CssParser
|
|
349
349
|
current_declarations << token
|
350
350
|
|
351
351
|
if !in_string && token.include?('}')
|
352
|
-
current_declarations.gsub!(/\}
|
352
|
+
current_declarations.gsub!(/\}\s*$/, '')
|
353
353
|
|
354
354
|
in_declarations -= 1
|
355
355
|
current_declarations.strip!
|
@@ -374,7 +374,7 @@ module CssParser
|
|
374
374
|
current_media_queries = []
|
375
375
|
elsif in_at_media_rule
|
376
376
|
if token.include?('{')
|
377
|
-
block_depth
|
377
|
+
block_depth += 1
|
378
378
|
in_at_media_rule = false
|
379
379
|
in_media_block = true
|
380
380
|
current_media_queries << CssParser.sanitize_media_query(current_media_query)
|
@@ -393,38 +393,34 @@ module CssParser
|
|
393
393
|
elsif in_charset or token =~ /@charset/i
|
394
394
|
# iterate until we are out of the charset declaration
|
395
395
|
in_charset = !token.include?(';')
|
396
|
-
|
397
|
-
|
398
|
-
block_depth = block_depth - 1
|
396
|
+
elsif !in_string && token.include?('}')
|
397
|
+
block_depth -= 1
|
399
398
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
end
|
405
|
-
else
|
406
|
-
if !in_string && token.include?('{')
|
407
|
-
current_selectors.strip!
|
408
|
-
in_declarations += 1
|
409
|
-
else
|
410
|
-
# if we are in a selector, add the token to the current selectors
|
411
|
-
current_selectors << token
|
412
|
-
|
413
|
-
# mark this as the beginning of the selector unless we have already marked it
|
414
|
-
rule_start = offset.first if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
|
415
|
-
end
|
399
|
+
# reset the current media query scope
|
400
|
+
if in_media_block
|
401
|
+
current_media_queries = [:all]
|
402
|
+
in_media_block = false
|
416
403
|
end
|
404
|
+
elsif !in_string && token.include?('{')
|
405
|
+
current_selectors.strip!
|
406
|
+
in_declarations += 1
|
407
|
+
else
|
408
|
+
# if we are in a selector, add the token to the current selectors
|
409
|
+
current_selectors << token
|
410
|
+
|
411
|
+
# mark this as the beginning of the selector unless we have already marked it
|
412
|
+
rule_start = offset.first if options[:capture_offsets] && rule_start.nil? && token =~ /^[^\s]+$/
|
417
413
|
end
|
418
414
|
end
|
419
415
|
|
420
416
|
# check for unclosed braces
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
add_rule!(current_selectors, current_declarations, current_media_queries)
|
426
|
-
end
|
417
|
+
return unless in_declarations > 0
|
418
|
+
|
419
|
+
unless options[:capture_offsets]
|
420
|
+
return add_rule!(current_selectors, current_declarations, current_media_queries)
|
427
421
|
end
|
422
|
+
|
423
|
+
add_rule_with_offsets!(current_selectors, current_declarations, options[:filename], (rule_start..offset.last), current_media_queries)
|
428
424
|
end
|
429
425
|
|
430
426
|
# Load a remote CSS file.
|
@@ -437,7 +433,7 @@ module CssParser
|
|
437
433
|
def load_uri!(uri, options = {}, deprecated = nil)
|
438
434
|
uri = Addressable::URI.parse(uri) unless uri.respond_to? :scheme
|
439
435
|
|
440
|
-
opts = {:
|
436
|
+
opts = {base_uri: nil, media_types: :all}
|
441
437
|
|
442
438
|
if options.is_a? Hash
|
443
439
|
opts.merge!(options)
|
@@ -457,14 +453,13 @@ module CssParser
|
|
457
453
|
opts[:filename] = uri.to_s if opts[:capture_offsets]
|
458
454
|
|
459
455
|
src, = read_remote_file(uri) # skip charset
|
460
|
-
|
461
|
-
|
462
|
-
end
|
456
|
+
|
457
|
+
add_block!(src, opts) if src
|
463
458
|
end
|
464
459
|
|
465
460
|
# Load a local CSS file.
|
466
461
|
def load_file!(file_name, options = {}, deprecated = nil)
|
467
|
-
opts = {:
|
462
|
+
opts = {base_dir: nil, media_types: :all}
|
468
463
|
|
469
464
|
if options.is_a? Hash
|
470
465
|
opts.merge!(options)
|
@@ -487,7 +482,7 @@ module CssParser
|
|
487
482
|
|
488
483
|
# Load a local CSS string.
|
489
484
|
def load_string!(src, options = {}, deprecated = nil)
|
490
|
-
opts = {:
|
485
|
+
opts = {base_dir: nil, media_types: :all}
|
491
486
|
|
492
487
|
if options.is_a? Hash
|
493
488
|
opts.merge!(options)
|
@@ -499,9 +494,8 @@ module CssParser
|
|
499
494
|
add_block!(src, opts)
|
500
495
|
end
|
501
496
|
|
502
|
-
|
503
|
-
|
504
497
|
protected
|
498
|
+
|
505
499
|
# Check that a path hasn't been loaded already
|
506
500
|
#
|
507
501
|
# Raises a CircularReferenceError exception if io_exceptions are on,
|
@@ -510,10 +504,11 @@ module CssParser
|
|
510
504
|
path = path.to_s
|
511
505
|
if @loaded_uris.include?(path)
|
512
506
|
raise CircularReferenceError, "can't load #{path} more than once" if @options[:io_exceptions]
|
513
|
-
|
507
|
+
|
508
|
+
false
|
514
509
|
else
|
515
510
|
@loaded_uris << path
|
516
|
-
|
511
|
+
true
|
517
512
|
end
|
518
513
|
end
|
519
514
|
|
@@ -541,7 +536,7 @@ module CssParser
|
|
541
536
|
utf8_block = ignore_pattern(utf8_block, STRIP_HTML_COMMENTS_RX, options)
|
542
537
|
|
543
538
|
# Strip lines containing just whitespace
|
544
|
-
utf8_block.gsub!(/^\s+$/,
|
539
|
+
utf8_block.gsub!(/^\s+$/, '') unless options[:capture_offsets]
|
545
540
|
|
546
541
|
utf8_block
|
547
542
|
end
|
@@ -577,11 +572,8 @@ module CssParser
|
|
577
572
|
if uri.scheme == 'file'
|
578
573
|
# local file
|
579
574
|
path = uri.path
|
580
|
-
path.gsub!(
|
581
|
-
|
582
|
-
src = fh.read
|
583
|
-
charset = fh.respond_to?(:charset) ? fh.charset : 'utf-8'
|
584
|
-
fh.close
|
575
|
+
path.gsub!(%r{^/}, '') if Gem.win_platform?
|
576
|
+
src = File.read(path, mode: 'rb')
|
585
577
|
else
|
586
578
|
# remote file
|
587
579
|
if uri.scheme == 'https'
|
@@ -599,21 +591,22 @@ module CssParser
|
|
599
591
|
|
600
592
|
if res.code.to_i >= 400
|
601
593
|
@redirect_count = nil
|
602
|
-
raise RemoteFileError
|
594
|
+
raise RemoteFileError, uri.to_s if @options[:io_exceptions]
|
595
|
+
|
603
596
|
return '', nil
|
604
597
|
elsif res.code.to_i >= 300 and res.code.to_i < 400
|
605
|
-
|
598
|
+
unless res['Location'].nil?
|
606
599
|
return read_remote_file Addressable::URI.parse(Addressable::URI.escape(res['Location']))
|
607
600
|
end
|
608
601
|
end
|
609
602
|
|
610
603
|
case res['content-encoding']
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
604
|
+
when 'gzip'
|
605
|
+
io = Zlib::GzipReader.new(StringIO.new(res.body))
|
606
|
+
src = io.read
|
607
|
+
when 'deflate'
|
608
|
+
io = Zlib::Inflate.new
|
609
|
+
src = io.inflate(res.body)
|
617
610
|
end
|
618
611
|
end
|
619
612
|
|
@@ -627,15 +620,17 @@ module CssParser
|
|
627
620
|
end
|
628
621
|
rescue
|
629
622
|
@redirect_count = nil
|
630
|
-
raise RemoteFileError
|
623
|
+
raise RemoteFileError, uri.to_s if @options[:io_exceptions]
|
624
|
+
|
631
625
|
return nil, nil
|
632
626
|
end
|
633
627
|
|
634
628
|
@redirect_count = nil
|
635
|
-
|
629
|
+
[src, charset]
|
636
630
|
end
|
637
631
|
|
638
632
|
private
|
633
|
+
|
639
634
|
# Save a folded declaration block to the internal cache.
|
640
635
|
def save_folded_declaration(block_hash, folded_declaration) # :nodoc:
|
641
636
|
@folded_declaration_cache[block_hash] = folded_declaration
|
@@ -643,7 +638,7 @@ module CssParser
|
|
643
638
|
|
644
639
|
# Retrieve a folded declaration block from the internal cache.
|
645
640
|
def get_folded_declaration(block_hash) # :nodoc:
|
646
|
-
|
641
|
+
@folded_declaration_cache[block_hash] ||= nil
|
647
642
|
end
|
648
643
|
|
649
644
|
def reset! # :nodoc:
|
@@ -657,14 +652,15 @@ module CssParser
|
|
657
652
|
# passed hash
|
658
653
|
def css_node_to_h(hash, key, val)
|
659
654
|
hash[key.strip] = '' and return hash if val.nil?
|
655
|
+
|
660
656
|
lines = val.split(';')
|
661
657
|
nodes = {}
|
662
658
|
lines.each do |line|
|
663
659
|
parts = line.split(':', 2)
|
664
|
-
if
|
660
|
+
if parts[1] =~ /:/
|
665
661
|
nodes[parts[0]] = css_node_to_h(hash, parts[0], parts[1])
|
666
662
|
else
|
667
|
-
nodes[parts[0].to_s.strip] =parts[1].to_s.strip
|
663
|
+
nodes[parts[0].to_s.strip] = parts[1].to_s.strip
|
668
664
|
end
|
669
665
|
end
|
670
666
|
hash[key.strip] = nodes
|