css_parser 1.7.0 → 1.21.1
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 +189 -130
- data/lib/css_parser/regexps.rb +59 -36
- data/lib/css_parser/rule_set.rb +444 -265
- data/lib/css_parser/version.rb +3 -1
- data/lib/css_parser.rb +26 -37
- metadata +12 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebc43b9f09241b83ffe9a2878e0a2e5295c79f6d2e02bb20dcaf54d376f91e58
|
4
|
+
data.tar.gz: 5b29858923af894f1b84251c59de6a4638ae0929b63ac0b432a7607d9dd32111
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd7a00f2be5a2ddbf730eb115dbc0e15f9ac7a71ac92e1a056161a2b418e01d7abeb757c6f8d5fabafd9e4f7dba034b209ca571012ee44ee6b377b2e4bd8100f
|
7
|
+
data.tar.gz: ac8aeba706f1ae35126c6d5c4a150a52862af8654f1342e6c5b36ebce8c0b71c26e0d0931d09081b3b01a70685c3d2b173031a3e1ff32a8477799c3e39e013e4
|
data/lib/css_parser/parser.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'strscan'
|
4
|
+
|
2
5
|
module CssParser
|
3
6
|
# Exception class used for any errors encountered while downloading remote files.
|
4
7
|
class RemoteFileError < IOError; end
|
@@ -15,13 +18,13 @@ module CssParser
|
|
15
18
|
# [<tt>import</tt>] Follow <tt>@import</tt> rules. Boolean, default is <tt>true</tt>.
|
16
19
|
# [<tt>io_exceptions</tt>] Throw an exception if a link can not be found. Boolean, default is <tt>true</tt>.
|
17
20
|
class Parser
|
18
|
-
USER_AGENT
|
19
|
-
|
20
|
-
STRIP_CSS_COMMENTS_RX =
|
21
|
-
STRIP_HTML_COMMENTS_RX =
|
21
|
+
USER_AGENT = "Ruby CSS Parser/#{CssParser::VERSION} (https://github.com/premailer/css_parser)".freeze
|
22
|
+
RULESET_TOKENIZER_RX = /\s+|\\{2,}|\\?[{}\s"]|[()]|.[^\s"{}()\\]*/.freeze
|
23
|
+
STRIP_CSS_COMMENTS_RX = %r{/\*.*?\*/}m.freeze
|
24
|
+
STRIP_HTML_COMMENTS_RX = /<!--|-->/m.freeze
|
22
25
|
|
23
26
|
# Initial parsing
|
24
|
-
RE_AT_IMPORT_RULE =
|
27
|
+
RE_AT_IMPORT_RULE = /@import\s*(?:url\s*)?(?:\()?(?:\s*)["']?([^'"\s)]*)["']?\)?([\w\s,^\]()]*)\)?[;\n]?/.freeze
|
25
28
|
|
26
29
|
MAX_REDIRECTS = 3
|
27
30
|
|
@@ -35,10 +38,14 @@ module CssParser
|
|
35
38
|
class << self; attr_reader :folded_declaration_cache; end
|
36
39
|
|
37
40
|
def initialize(options = {})
|
38
|
-
@options = {
|
39
|
-
|
40
|
-
|
41
|
-
|
41
|
+
@options = {
|
42
|
+
absolute_paths: false,
|
43
|
+
import: true,
|
44
|
+
io_exceptions: true,
|
45
|
+
rule_set_exceptions: true,
|
46
|
+
capture_offsets: false,
|
47
|
+
user_agent: USER_AGENT
|
48
|
+
}.merge(options)
|
42
49
|
|
43
50
|
# array of RuleSets
|
44
51
|
@rules = []
|
@@ -70,21 +77,20 @@ module CssParser
|
|
70
77
|
# Returns an array of declarations.
|
71
78
|
def find_by_selector(selector, media_types = :all)
|
72
79
|
out = []
|
73
|
-
each_selector(media_types) do |sel, dec,
|
80
|
+
each_selector(media_types) do |sel, dec, _spec|
|
74
81
|
out << dec if sel.strip == selector.strip
|
75
82
|
end
|
76
83
|
out
|
77
84
|
end
|
78
|
-
|
85
|
+
alias [] find_by_selector
|
79
86
|
|
80
87
|
# Finds the rule sets that match the given selectors
|
81
88
|
def find_rule_sets(selectors, media_types = :all)
|
82
89
|
rule_sets = []
|
83
90
|
|
84
91
|
selectors.each do |selector|
|
85
|
-
selector.gsub
|
86
|
-
|
87
|
-
each_rule_set(media_types) do |rule_set, media_type|
|
92
|
+
selector = selector.gsub(/\s+/, ' ').strip
|
93
|
+
each_rule_set(media_types) do |rule_set, _media_type|
|
88
94
|
if !rule_sets.member?(rule_set) && rule_set.selectors.member?(selector)
|
89
95
|
rule_sets << rule_set
|
90
96
|
end
|
@@ -115,9 +121,9 @@ module CssParser
|
|
115
121
|
# parser = CssParser::Parser.new
|
116
122
|
# parser.add_block!(css)
|
117
123
|
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)}
|
124
|
+
options = {base_uri: nil, base_dir: nil, charset: nil, media_types: :all, only_media_types: :all}.merge(options)
|
125
|
+
options[:media_types] = [options[:media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
|
126
|
+
options[:only_media_types] = [options[:only_media_types]].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
|
121
127
|
|
122
128
|
block = cleanup_block(block, options)
|
123
129
|
|
@@ -129,19 +135,19 @@ module CssParser
|
|
129
135
|
if @options[:import]
|
130
136
|
block.scan(RE_AT_IMPORT_RULE).each do |import_rule|
|
131
137
|
media_types = []
|
132
|
-
if media_string = import_rule[-1]
|
133
|
-
media_string.split(
|
138
|
+
if (media_string = import_rule[-1])
|
139
|
+
media_string.split(',').each do |t|
|
134
140
|
media_types << CssParser.sanitize_media_query(t) unless t.empty?
|
135
141
|
end
|
136
142
|
else
|
137
143
|
media_types = [:all]
|
138
144
|
end
|
139
145
|
|
140
|
-
next unless options[:only_media_types].include?(:all) or media_types.
|
146
|
+
next unless options[:only_media_types].include?(:all) or media_types.empty? or !(media_types & options[:only_media_types]).empty?
|
141
147
|
|
142
148
|
import_path = import_rule[0].to_s.gsub(/['"]*/, '').strip
|
143
149
|
|
144
|
-
import_options = {
|
150
|
+
import_options = {media_types: media_types}
|
145
151
|
import_options[:capture_offsets] = true if options[:capture_offsets]
|
146
152
|
|
147
153
|
if options[:base_uri]
|
@@ -161,12 +167,44 @@ module CssParser
|
|
161
167
|
parse_block_into_rule_sets!(block, options)
|
162
168
|
end
|
163
169
|
|
164
|
-
# 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.
|
165
173
|
#
|
166
|
-
# +media_types+ can be a symbol or an array of symbols.
|
167
|
-
|
168
|
-
|
169
|
-
|
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
|
170
208
|
end
|
171
209
|
|
172
210
|
# Add a CSS rule by setting the +selectors+, +declarations+, +filename+, +offset+ and +media_types+.
|
@@ -175,29 +213,32 @@ module CssParser
|
|
175
213
|
# +offset+ should be Range object representing the start and end byte locations where the rule was found in the file.
|
176
214
|
# +media_types+ can be a symbol or an array of symbols.
|
177
215
|
def add_rule_with_offsets!(selectors, declarations, filename, offset, media_types = :all)
|
178
|
-
|
179
|
-
|
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
|
+
)
|
180
221
|
end
|
181
222
|
|
182
223
|
# Add a CssParser RuleSet object.
|
183
224
|
#
|
184
225
|
# +media_types+ can be a symbol or an array of symbols.
|
185
226
|
def add_rule_set!(ruleset, media_types = :all)
|
186
|
-
raise ArgumentError unless ruleset.
|
227
|
+
raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
|
187
228
|
|
188
|
-
media_types = [media_types] unless Array
|
189
|
-
media_types = media_types.flat_map { |mt| CssParser.sanitize_media_query(mt)}
|
229
|
+
media_types = [media_types] unless media_types.is_a?(Array)
|
230
|
+
media_types = media_types.flat_map { |mt| CssParser.sanitize_media_query(mt) }
|
190
231
|
|
191
|
-
@rules << {:
|
232
|
+
@rules << {media_types: media_types, rules: ruleset}
|
192
233
|
end
|
193
234
|
|
194
235
|
# Remove a CssParser RuleSet object.
|
195
236
|
#
|
196
237
|
# +media_types+ can be a symbol or an array of symbols.
|
197
238
|
def remove_rule_set!(ruleset, media_types = :all)
|
198
|
-
raise ArgumentError unless ruleset.
|
239
|
+
raise ArgumentError unless ruleset.is_a?(CssParser::RuleSet)
|
199
240
|
|
200
|
-
media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
|
241
|
+
media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
|
201
242
|
|
202
243
|
@rules.reject! do |rule|
|
203
244
|
rule[:media_types] == media_types && rule[:rules].to_s == ruleset.to_s
|
@@ -209,7 +250,7 @@ module CssParser
|
|
209
250
|
# +media_types+ can be a symbol or an array of symbols.
|
210
251
|
def each_rule_set(media_types = :all) # :yields: rule_set, media_types
|
211
252
|
media_types = [:all] if media_types.nil?
|
212
|
-
media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
|
253
|
+
media_types = [media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
|
213
254
|
|
214
255
|
@rules.each do |block|
|
215
256
|
if media_types.include?(:all) or block[:media_types].any? { |mt| media_types.include?(mt) }
|
@@ -222,7 +263,7 @@ module CssParser
|
|
222
263
|
def to_h(which_media = :all)
|
223
264
|
out = {}
|
224
265
|
styles_by_media_types = {}
|
225
|
-
each_selector(which_media) do |selectors, declarations,
|
266
|
+
each_selector(which_media) do |selectors, declarations, _specificity, media_types|
|
226
267
|
media_types.each do |media_type|
|
227
268
|
styles_by_media_types[media_type] ||= []
|
228
269
|
styles_by_media_types[media_type] << [selectors, declarations]
|
@@ -244,7 +285,7 @@ module CssParser
|
|
244
285
|
# +media_types+ can be a symbol or an array of symbols.
|
245
286
|
# See RuleSet#each_selector for +options+.
|
246
287
|
def each_selector(all_media_types = :all, options = {}) # :yields: selectors, declarations, specificity, media_types
|
247
|
-
return to_enum(
|
288
|
+
return to_enum(__method__, all_media_types, options) unless block_given?
|
248
289
|
|
249
290
|
each_rule_set(all_media_types) do |rule_set, media_types|
|
250
291
|
rule_set.each_selector(options) do |selectors, declarations, specificity|
|
@@ -255,9 +296,10 @@ module CssParser
|
|
255
296
|
|
256
297
|
# Output all CSS rules as a single stylesheet.
|
257
298
|
def to_s(which_media = :all)
|
258
|
-
out =
|
299
|
+
out = []
|
259
300
|
styles_by_media_types = {}
|
260
|
-
|
301
|
+
|
302
|
+
each_selector(which_media) do |selectors, declarations, _specificity, media_types|
|
261
303
|
media_types.each do |media_type|
|
262
304
|
styles_by_media_types[media_type] ||= []
|
263
305
|
styles_by_media_types[media_type] << [selectors, declarations]
|
@@ -266,20 +308,21 @@ module CssParser
|
|
266
308
|
|
267
309
|
styles_by_media_types.each_pair do |media_type, media_styles|
|
268
310
|
media_block = (media_type != :all)
|
269
|
-
out << "@media #{media_type} {
|
311
|
+
out << "@media #{media_type} {" if media_block
|
270
312
|
|
271
313
|
media_styles.each do |media_style|
|
272
314
|
if media_block
|
273
|
-
out
|
315
|
+
out.push(" #{media_style[0]} {\n #{media_style[1]}\n }")
|
274
316
|
else
|
275
|
-
out
|
317
|
+
out.push("#{media_style[0]} {\n#{media_style[1]}\n}")
|
276
318
|
end
|
277
319
|
end
|
278
320
|
|
279
|
-
out <<
|
321
|
+
out << '}' if media_block
|
280
322
|
end
|
281
323
|
|
282
|
-
out
|
324
|
+
out << ''
|
325
|
+
out.join("\n")
|
283
326
|
end
|
284
327
|
|
285
328
|
# A hash of { :media_query => rule_sets }
|
@@ -287,7 +330,7 @@ module CssParser
|
|
287
330
|
rules_by_media = {}
|
288
331
|
@rules.each do |block|
|
289
332
|
block[:media_types].each do |mt|
|
290
|
-
unless rules_by_media.
|
333
|
+
unless rules_by_media.key?(mt)
|
291
334
|
rules_by_media[mt] = []
|
292
335
|
end
|
293
336
|
rules_by_media[mt] << block[:rules]
|
@@ -299,15 +342,13 @@ module CssParser
|
|
299
342
|
|
300
343
|
# Merge declarations with the same selector.
|
301
344
|
def compact! # :nodoc:
|
302
|
-
|
303
|
-
|
304
|
-
compacted
|
345
|
+
[]
|
305
346
|
end
|
306
347
|
|
307
348
|
def parse_block_into_rule_sets!(block, options = {}) # :nodoc:
|
308
349
|
current_media_queries = [:all]
|
309
350
|
if options[:media_types]
|
310
|
-
current_media_queries = options[:media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt)}
|
351
|
+
current_media_queries = options[:media_types].flatten.collect { |mt| CssParser.sanitize_media_query(mt) }
|
311
352
|
end
|
312
353
|
|
313
354
|
in_declarations = 0
|
@@ -318,17 +359,21 @@ module CssParser
|
|
318
359
|
in_at_media_rule = false
|
319
360
|
in_media_block = false
|
320
361
|
|
321
|
-
current_selectors =
|
322
|
-
current_media_query =
|
323
|
-
current_declarations =
|
362
|
+
current_selectors = +''
|
363
|
+
current_media_query = +''
|
364
|
+
current_declarations = +''
|
324
365
|
|
325
366
|
# once we are in a rule, we will use this to store where we started if we are capturing offsets
|
326
367
|
rule_start = nil
|
327
|
-
|
368
|
+
start_offset = nil
|
369
|
+
end_offset = nil
|
328
370
|
|
329
|
-
|
371
|
+
scanner = StringScanner.new(block)
|
372
|
+
until scanner.eos?
|
330
373
|
# save the regex offset so that we know where in the file we are
|
331
|
-
|
374
|
+
start_offset = scanner.pos
|
375
|
+
token = scanner.scan(RULESET_TOKENIZER_RX)
|
376
|
+
end_offset = scanner.pos
|
332
377
|
|
333
378
|
if token.start_with?('"') # found un-escaped double quote
|
334
379
|
in_string = !in_string
|
@@ -349,82 +394,95 @@ module CssParser
|
|
349
394
|
current_declarations << token
|
350
395
|
|
351
396
|
if !in_string && token.include?('}')
|
352
|
-
current_declarations.gsub!(/\}
|
397
|
+
current_declarations.gsub!(/\}\s*$/, '')
|
353
398
|
|
354
399
|
in_declarations -= 1
|
355
400
|
current_declarations.strip!
|
356
401
|
|
357
402
|
unless current_declarations.empty?
|
403
|
+
add_rule_options = {
|
404
|
+
selectors: current_selectors, block: current_declarations,
|
405
|
+
media_types: current_media_queries
|
406
|
+
}
|
358
407
|
if options[:capture_offsets]
|
359
|
-
|
360
|
-
|
361
|
-
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
|
362
410
|
end
|
411
|
+
add_rule!(**add_rule_options)
|
363
412
|
end
|
364
413
|
|
365
|
-
current_selectors =
|
366
|
-
current_declarations =
|
414
|
+
current_selectors = +''
|
415
|
+
current_declarations = +''
|
367
416
|
|
368
417
|
# restart our search for selectors and declarations
|
369
418
|
rule_start = nil if options[:capture_offsets]
|
370
419
|
end
|
371
|
-
elsif
|
420
|
+
elsif /@media/i.match?(token)
|
372
421
|
# found '@media', reset current media_types
|
373
422
|
in_at_media_rule = true
|
374
423
|
current_media_queries = []
|
375
424
|
elsif in_at_media_rule
|
376
425
|
if token.include?('{')
|
377
|
-
block_depth
|
426
|
+
block_depth += 1
|
378
427
|
in_at_media_rule = false
|
379
428
|
in_media_block = true
|
380
429
|
current_media_queries << CssParser.sanitize_media_query(current_media_query)
|
381
|
-
current_media_query =
|
430
|
+
current_media_query = +''
|
382
431
|
elsif token.include?(',')
|
383
432
|
# new media query begins
|
384
433
|
token.tr!(',', ' ')
|
385
434
|
token.strip!
|
386
435
|
current_media_query << token << ' '
|
387
436
|
current_media_queries << CssParser.sanitize_media_query(current_media_query)
|
388
|
-
current_media_query =
|
437
|
+
current_media_query = +''
|
389
438
|
else
|
390
439
|
token.strip!
|
391
|
-
|
440
|
+
# special-case the ( and ) tokens to remove inner-whitespace
|
441
|
+
# (eg we'd prefer '(width: 500px)' to '( width: 500px )' )
|
442
|
+
case token
|
443
|
+
when '('
|
444
|
+
current_media_query << token
|
445
|
+
when ')'
|
446
|
+
current_media_query.sub!(/ ?$/, token)
|
447
|
+
else
|
448
|
+
current_media_query << token << ' '
|
449
|
+
end
|
392
450
|
end
|
393
|
-
elsif in_charset or
|
451
|
+
elsif in_charset or /@charset/i.match?(token)
|
394
452
|
# iterate until we are out of the charset declaration
|
395
453
|
in_charset = !token.include?(';')
|
396
|
-
|
397
|
-
|
398
|
-
block_depth = block_depth - 1
|
399
|
-
|
400
|
-
# reset the current media query scope
|
401
|
-
if in_media_block
|
402
|
-
current_media_queries = [:all]
|
403
|
-
in_media_block = false
|
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
|
454
|
+
elsif !in_string && token.include?('}')
|
455
|
+
block_depth -= 1
|
412
456
|
|
413
|
-
|
414
|
-
|
415
|
-
|
457
|
+
# reset the current media query scope
|
458
|
+
if in_media_block
|
459
|
+
current_media_queries = [:all]
|
460
|
+
in_media_block = false
|
416
461
|
end
|
462
|
+
elsif !in_string && token.include?('{')
|
463
|
+
current_selectors.strip!
|
464
|
+
in_declarations += 1
|
465
|
+
else
|
466
|
+
# if we are in a selector, add the token to the current selectors
|
467
|
+
current_selectors << token
|
468
|
+
|
469
|
+
# mark this as the beginning of the selector unless we have already marked it
|
470
|
+
rule_start = start_offset if options[:capture_offsets] && rule_start.nil? && /^[^\s]+$/.match?(token)
|
417
471
|
end
|
418
472
|
end
|
419
473
|
|
420
474
|
# check for unclosed braces
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
475
|
+
return unless in_declarations > 0
|
476
|
+
|
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
|
427
484
|
end
|
485
|
+
add_rule!(**add_rule_options)
|
428
486
|
end
|
429
487
|
|
430
488
|
# Load a remote CSS file.
|
@@ -437,11 +495,13 @@ module CssParser
|
|
437
495
|
def load_uri!(uri, options = {}, deprecated = nil)
|
438
496
|
uri = Addressable::URI.parse(uri) unless uri.respond_to? :scheme
|
439
497
|
|
440
|
-
opts = {:
|
498
|
+
opts = {base_uri: nil, media_types: :all}
|
441
499
|
|
442
500
|
if options.is_a? Hash
|
443
501
|
opts.merge!(options)
|
444
502
|
else
|
503
|
+
warn '[DEPRECATION] `load_uri!` with positional arguments is deprecated. ' \
|
504
|
+
'Please use keyword arguments instead.', uplevel: 1
|
445
505
|
opts[:base_uri] = options if options.is_a? String
|
446
506
|
opts[:media_types] = deprecated if deprecated
|
447
507
|
end
|
@@ -457,18 +517,19 @@ module CssParser
|
|
457
517
|
opts[:filename] = uri.to_s if opts[:capture_offsets]
|
458
518
|
|
459
519
|
src, = read_remote_file(uri) # skip charset
|
460
|
-
|
461
|
-
|
462
|
-
end
|
520
|
+
|
521
|
+
add_block!(src, opts) if src
|
463
522
|
end
|
464
523
|
|
465
524
|
# Load a local CSS file.
|
466
525
|
def load_file!(file_name, options = {}, deprecated = nil)
|
467
|
-
opts = {:
|
526
|
+
opts = {base_dir: nil, media_types: :all}
|
468
527
|
|
469
528
|
if options.is_a? Hash
|
470
529
|
opts.merge!(options)
|
471
530
|
else
|
531
|
+
warn '[DEPRECATION] `load_file!` with positional arguments is deprecated. ' \
|
532
|
+
'Please use keyword arguments instead.', uplevel: 1
|
472
533
|
opts[:base_dir] = options if options.is_a? String
|
473
534
|
opts[:media_types] = deprecated if deprecated
|
474
535
|
end
|
@@ -477,7 +538,7 @@ module CssParser
|
|
477
538
|
return unless File.readable?(file_name)
|
478
539
|
return unless circular_reference_check(file_name)
|
479
540
|
|
480
|
-
src =
|
541
|
+
src = File.read(file_name)
|
481
542
|
|
482
543
|
opts[:filename] = file_name if opts[:capture_offsets]
|
483
544
|
opts[:base_dir] = File.dirname(file_name)
|
@@ -487,11 +548,13 @@ module CssParser
|
|
487
548
|
|
488
549
|
# Load a local CSS string.
|
489
550
|
def load_string!(src, options = {}, deprecated = nil)
|
490
|
-
opts = {:
|
551
|
+
opts = {base_dir: nil, media_types: :all}
|
491
552
|
|
492
553
|
if options.is_a? Hash
|
493
554
|
opts.merge!(options)
|
494
555
|
else
|
556
|
+
warn '[DEPRECATION] `load_file!` with positional arguments is deprecated. ' \
|
557
|
+
'Please use keyword arguments instead.', uplevel: 1
|
495
558
|
opts[:base_dir] = options if options.is_a? String
|
496
559
|
opts[:media_types] = deprecated if deprecated
|
497
560
|
end
|
@@ -499,9 +562,8 @@ module CssParser
|
|
499
562
|
add_block!(src, opts)
|
500
563
|
end
|
501
564
|
|
502
|
-
|
503
|
-
|
504
565
|
protected
|
566
|
+
|
505
567
|
# Check that a path hasn't been loaded already
|
506
568
|
#
|
507
569
|
# Raises a CircularReferenceError exception if io_exceptions are on,
|
@@ -510,10 +572,11 @@ module CssParser
|
|
510
572
|
path = path.to_s
|
511
573
|
if @loaded_uris.include?(path)
|
512
574
|
raise CircularReferenceError, "can't load #{path} more than once" if @options[:io_exceptions]
|
513
|
-
|
575
|
+
|
576
|
+
false
|
514
577
|
else
|
515
578
|
@loaded_uris << path
|
516
|
-
|
579
|
+
true
|
517
580
|
end
|
518
581
|
end
|
519
582
|
|
@@ -533,7 +596,7 @@ module CssParser
|
|
533
596
|
# Returns a string.
|
534
597
|
def cleanup_block(block, options = {}) # :nodoc:
|
535
598
|
# Strip CSS comments
|
536
|
-
utf8_block = block.encode('UTF-8', '
|
599
|
+
utf8_block = block.encode('UTF-8', 'UTF-8', invalid: :replace, undef: :replace, replace: ' ')
|
537
600
|
utf8_block = ignore_pattern(utf8_block, STRIP_CSS_COMMENTS_RX, options)
|
538
601
|
|
539
602
|
# Strip HTML comments - they shouldn't really be in here but
|
@@ -541,7 +604,7 @@ module CssParser
|
|
541
604
|
utf8_block = ignore_pattern(utf8_block, STRIP_HTML_COMMENTS_RX, options)
|
542
605
|
|
543
606
|
# Strip lines containing just whitespace
|
544
|
-
utf8_block.gsub!(/^\s+$/,
|
607
|
+
utf8_block.gsub!(/^\s+$/, '') unless options[:capture_offsets]
|
545
608
|
|
546
609
|
utf8_block
|
547
610
|
end
|
@@ -577,11 +640,8 @@ module CssParser
|
|
577
640
|
if uri.scheme == 'file'
|
578
641
|
# local file
|
579
642
|
path = uri.path
|
580
|
-
path.gsub!(
|
581
|
-
|
582
|
-
src = fh.read
|
583
|
-
charset = fh.respond_to?(:charset) ? fh.charset : 'utf-8'
|
584
|
-
fh.close
|
643
|
+
path.gsub!(%r{^/}, '') if Gem.win_platform?
|
644
|
+
src = File.read(path, mode: 'rb')
|
585
645
|
else
|
586
646
|
# remote file
|
587
647
|
if uri.scheme == 'https'
|
@@ -593,49 +653,47 @@ module CssParser
|
|
593
653
|
http = Net::HTTP.new(uri.host, uri.port)
|
594
654
|
end
|
595
655
|
|
596
|
-
res = http.get(uri.request_uri, {'User-Agent' =>
|
656
|
+
res = http.get(uri.request_uri, {'User-Agent' => @options[:user_agent], 'Accept-Encoding' => 'gzip'})
|
597
657
|
src = res.body
|
598
658
|
charset = res.respond_to?(:charset) ? res.encoding : 'utf-8'
|
599
659
|
|
600
660
|
if res.code.to_i >= 400
|
601
661
|
@redirect_count = nil
|
602
|
-
raise RemoteFileError
|
662
|
+
raise RemoteFileError, uri.to_s if @options[:io_exceptions]
|
663
|
+
|
603
664
|
return '', nil
|
604
665
|
elsif res.code.to_i >= 300 and res.code.to_i < 400
|
605
|
-
|
666
|
+
unless res['Location'].nil?
|
606
667
|
return read_remote_file Addressable::URI.parse(Addressable::URI.escape(res['Location']))
|
607
668
|
end
|
608
669
|
end
|
609
670
|
|
610
671
|
case res['content-encoding']
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
672
|
+
when 'gzip'
|
673
|
+
io = Zlib::GzipReader.new(StringIO.new(res.body))
|
674
|
+
src = io.read
|
675
|
+
when 'deflate'
|
676
|
+
io = Zlib::Inflate.new
|
677
|
+
src = io.inflate(res.body)
|
617
678
|
end
|
618
679
|
end
|
619
680
|
|
620
681
|
if charset
|
621
|
-
|
622
|
-
src.encode!('UTF-8', charset)
|
623
|
-
else
|
624
|
-
ic = Iconv.new('UTF-8//IGNORE', charset)
|
625
|
-
src = ic.iconv(src)
|
626
|
-
end
|
682
|
+
src.encode!('UTF-8', charset)
|
627
683
|
end
|
628
684
|
rescue
|
629
685
|
@redirect_count = nil
|
630
|
-
raise RemoteFileError
|
686
|
+
raise RemoteFileError, uri.to_s if @options[:io_exceptions]
|
687
|
+
|
631
688
|
return nil, nil
|
632
689
|
end
|
633
690
|
|
634
691
|
@redirect_count = nil
|
635
|
-
|
692
|
+
[src, charset]
|
636
693
|
end
|
637
694
|
|
638
695
|
private
|
696
|
+
|
639
697
|
# Save a folded declaration block to the internal cache.
|
640
698
|
def save_folded_declaration(block_hash, folded_declaration) # :nodoc:
|
641
699
|
@folded_declaration_cache[block_hash] = folded_declaration
|
@@ -643,7 +701,7 @@ module CssParser
|
|
643
701
|
|
644
702
|
# Retrieve a folded declaration block from the internal cache.
|
645
703
|
def get_folded_declaration(block_hash) # :nodoc:
|
646
|
-
|
704
|
+
@folded_declaration_cache[block_hash] ||= nil
|
647
705
|
end
|
648
706
|
|
649
707
|
def reset! # :nodoc:
|
@@ -657,14 +715,15 @@ module CssParser
|
|
657
715
|
# passed hash
|
658
716
|
def css_node_to_h(hash, key, val)
|
659
717
|
hash[key.strip] = '' and return hash if val.nil?
|
718
|
+
|
660
719
|
lines = val.split(';')
|
661
720
|
nodes = {}
|
662
721
|
lines.each do |line|
|
663
722
|
parts = line.split(':', 2)
|
664
|
-
if
|
723
|
+
if parts[1].include?(':')
|
665
724
|
nodes[parts[0]] = css_node_to_h(hash, parts[0], parts[1])
|
666
725
|
else
|
667
|
-
nodes[parts[0].to_s.strip] =parts[1].to_s.strip
|
726
|
+
nodes[parts[0].to_s.strip] = parts[1].to_s.strip
|
668
727
|
end
|
669
728
|
end
|
670
729
|
hash[key.strip] = nodes
|