crass 0.2.1 → 1.0.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/.travis.yml +4 -0
- data/HISTORY.md +22 -1
- data/LICENSE +1 -1
- data/README.md +64 -72
- data/Rakefile +4 -0
- data/crass.gemspec +2 -2
- data/lib/crass.rb +1 -1
- data/lib/crass/parser.rb +231 -96
- data/lib/crass/scanner.rb +21 -21
- data/lib/crass/token-scanner.rb +8 -1
- data/lib/crass/tokenizer.rb +133 -131
- data/lib/crass/version.rb +1 -1
- data/test/css-parsing-tests/An+B.json +156 -0
- data/test/css-parsing-tests/LICENSE +8 -0
- data/test/css-parsing-tests/README.rst +301 -0
- data/test/css-parsing-tests/color3.json +142 -0
- data/test/css-parsing-tests/color3_hsl.json +3890 -0
- data/test/css-parsing-tests/color3_keywords.json +803 -0
- data/test/css-parsing-tests/component_value_list.json +432 -0
- data/test/css-parsing-tests/declaration_list.json +44 -0
- data/test/css-parsing-tests/make_color3_hsl.py +17 -0
- data/test/css-parsing-tests/make_color3_keywords.py +191 -0
- data/test/css-parsing-tests/one_component_value.json +27 -0
- data/test/css-parsing-tests/one_declaration.json +46 -0
- data/test/css-parsing-tests/one_rule.json +36 -0
- data/test/css-parsing-tests/rule_list.json +48 -0
- data/test/css-parsing-tests/stylesheet.json +44 -0
- data/test/css-parsing-tests/stylesheet_bytes.json +146 -0
- data/test/shared/parse_rules.rb +377 -434
- data/test/support/common.rb +124 -0
- data/test/support/serialization/animate.css +3158 -0
- data/test/support/serialization/html5-boilerplate.css +268 -0
- data/test/support/serialization/misc.css +9 -0
- data/test/test_css_parsing_tests.rb +150 -0
- data/test/test_parse_properties.rb +136 -211
- data/test/test_parse_rules.rb +0 -52
- data/test/test_parse_stylesheet.rb +0 -39
- data/test/test_serialization.rb +13 -4
- metadata +44 -7
- data/test/test_tokenizer.rb +0 -1562
data/lib/crass/scanner.rb
CHANGED
@@ -18,12 +18,13 @@ module Crass
|
|
18
18
|
# position, not a byte position, so it accounts for multi-byte characters.
|
19
19
|
attr_accessor :pos
|
20
20
|
|
21
|
+
# String being scanned.
|
22
|
+
attr_reader :string
|
23
|
+
|
21
24
|
# Creates a Scanner instance for the given _input_ string or IO instance.
|
22
25
|
def initialize(input)
|
23
|
-
string
|
24
|
-
|
25
|
-
@chars = string.chars.to_a
|
26
|
-
@scanner = StringScanner.new(string)
|
26
|
+
@string = input.is_a?(IO) ? input.read : input.to_s
|
27
|
+
@scanner = StringScanner.new(@string)
|
27
28
|
|
28
29
|
reset
|
29
30
|
end
|
@@ -31,11 +32,11 @@ module Crass
|
|
31
32
|
# Consumes the next character and returns it, advancing the pointer, or
|
32
33
|
# an empty string if the end of the string has been reached.
|
33
34
|
def consume
|
34
|
-
if @pos
|
35
|
-
|
36
|
-
else
|
37
|
-
@pos += 1
|
35
|
+
if @pos < @len
|
36
|
+
@pos += 1
|
38
37
|
@current = @scanner.getch
|
38
|
+
else
|
39
|
+
''
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
@@ -43,8 +44,12 @@ module Crass
|
|
43
44
|
# the end of the string. Returns an empty string is the end of the string
|
44
45
|
# has already been reached.
|
45
46
|
def consume_rest
|
46
|
-
|
47
|
-
|
47
|
+
result = @scanner.rest
|
48
|
+
|
49
|
+
@current = result[-1]
|
50
|
+
@pos = @len
|
51
|
+
|
52
|
+
result
|
48
53
|
end
|
49
54
|
|
50
55
|
# Returns `true` if the end of the string has been reached, `false`
|
@@ -62,8 +67,8 @@ module Crass
|
|
62
67
|
# Returns the substring between {#marker} and {#pos}, without altering the
|
63
68
|
# pointer.
|
64
69
|
def marked
|
65
|
-
if result = @
|
66
|
-
result
|
70
|
+
if result = @string[@marker, @pos - @marker]
|
71
|
+
result
|
67
72
|
else
|
68
73
|
''
|
69
74
|
end
|
@@ -73,7 +78,7 @@ module Crass
|
|
73
78
|
# doesn't consume them. The number of characters returned may be less than
|
74
79
|
# _length_ if the end of the string is reached.
|
75
80
|
def peek(length = 1)
|
76
|
-
@
|
81
|
+
@string[pos, length]
|
77
82
|
end
|
78
83
|
|
79
84
|
# Moves the pointer back one character without changing the value of
|
@@ -87,7 +92,7 @@ module Crass
|
|
87
92
|
# Resets the pointer to the beginning of the string.
|
88
93
|
def reset
|
89
94
|
@current = nil
|
90
|
-
@len = @
|
95
|
+
@len = @string.size
|
91
96
|
@marker = 0
|
92
97
|
@pos = 0
|
93
98
|
end
|
@@ -98,7 +103,7 @@ module Crass
|
|
98
103
|
def scan(pattern)
|
99
104
|
if match = @scanner.scan(pattern)
|
100
105
|
@pos += match.size
|
101
|
-
@current =
|
106
|
+
@current = match[-1]
|
102
107
|
end
|
103
108
|
|
104
109
|
match
|
@@ -110,16 +115,11 @@ module Crass
|
|
110
115
|
def scan_until(pattern)
|
111
116
|
if match = @scanner.scan_until(pattern)
|
112
117
|
@pos += match.size
|
113
|
-
@current =
|
118
|
+
@current = match[-1]
|
114
119
|
end
|
115
120
|
|
116
121
|
match
|
117
122
|
end
|
118
123
|
end
|
119
124
|
|
120
|
-
# Returns the string being scanned.
|
121
|
-
def string
|
122
|
-
@scanner.string
|
123
|
-
end
|
124
|
-
|
125
125
|
end
|
data/lib/crass/token-scanner.rb
CHANGED
@@ -19,13 +19,20 @@ module Crass
|
|
19
19
|
@tokens[start...@pos] || []
|
20
20
|
end
|
21
21
|
|
22
|
-
# Consumes the next token and returns it, advancing the pointer.
|
22
|
+
# Consumes the next token and returns it, advancing the pointer. Returns
|
23
|
+
# `nil` if there is no next token.
|
23
24
|
def consume
|
24
25
|
@current = @tokens[@pos]
|
25
26
|
@pos += 1 if @current
|
26
27
|
@current
|
27
28
|
end
|
28
29
|
|
30
|
+
# Returns the next token without consuming it, or `nil` if there is no next
|
31
|
+
# token.
|
32
|
+
def peek
|
33
|
+
@tokens[@pos]
|
34
|
+
end
|
35
|
+
|
29
36
|
# Reconsumes the current token, moving the pointer back one position.
|
30
37
|
#
|
31
38
|
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#reconsume-the-current-input-token
|
data/lib/crass/tokenizer.rb
CHANGED
@@ -5,7 +5,7 @@ module Crass
|
|
5
5
|
|
6
6
|
# Tokenizes a CSS string.
|
7
7
|
#
|
8
|
-
# http://
|
8
|
+
# 4. http://dev.w3.org/csswg/css-syntax/#tokenization
|
9
9
|
class Tokenizer
|
10
10
|
RE_COMMENT_CLOSE = /\*\//
|
11
11
|
RE_DIGIT = /[0-9]+/
|
@@ -30,9 +30,11 @@ module Crass
|
|
30
30
|
)?
|
31
31
|
\z/x
|
32
32
|
|
33
|
+
RE_QUOTED_URL_START = /\A[\n\u0009\u0020]?["']/
|
33
34
|
RE_UNICODE_RANGE_START = /\+(?:[0-9A-Fa-f]|\?)/
|
34
35
|
RE_UNICODE_RANGE_END = /-[0-9A-Fa-f]/
|
35
36
|
RE_WHITESPACE = /[\n\u0009\u0020]+/
|
37
|
+
RE_WHITESPACE_ANCHORED = /\A[\n\u0009\u0020]+\z/
|
36
38
|
|
37
39
|
# -- Class Methods ---------------------------------------------------------
|
38
40
|
|
@@ -64,23 +66,34 @@ module Crass
|
|
64
66
|
|
65
67
|
# Consumes a token and returns the token that was consumed.
|
66
68
|
#
|
67
|
-
# http://
|
69
|
+
# 4.3.1. http://dev.w3.org/csswg/css-syntax/#consume-a-token
|
68
70
|
def consume
|
69
71
|
return nil if @s.eos?
|
70
72
|
|
71
73
|
@s.mark
|
74
|
+
|
75
|
+
# Consume comments.
|
76
|
+
if comment_token = consume_comments
|
77
|
+
if @options[:preserve_comments]
|
78
|
+
return comment_token
|
79
|
+
else
|
80
|
+
return consume
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Consume whitespace.
|
72
85
|
return create_token(:whitespace) if @s.scan(RE_WHITESPACE)
|
73
86
|
|
74
87
|
char = @s.consume
|
75
88
|
|
76
89
|
case char.to_sym
|
77
90
|
when :'"'
|
78
|
-
consume_string
|
91
|
+
consume_string
|
79
92
|
|
80
93
|
when :'#'
|
81
|
-
if @s.peek =~ RE_NAME || valid_escape?
|
94
|
+
if @s.peek =~ RE_NAME || valid_escape?(@s.peek(2))
|
82
95
|
create_token(:hash,
|
83
|
-
:type => start_identifier? ? :id : :unrestricted,
|
96
|
+
:type => start_identifier?(@s.peek(3)) ? :id : :unrestricted,
|
84
97
|
:value => consume_name)
|
85
98
|
else
|
86
99
|
create_token(:delim, :value => char)
|
@@ -95,7 +108,7 @@ module Crass
|
|
95
108
|
end
|
96
109
|
|
97
110
|
when :"'"
|
98
|
-
consume_string
|
111
|
+
consume_string
|
99
112
|
|
100
113
|
when :'('
|
101
114
|
create_token(:'(')
|
@@ -108,8 +121,8 @@ module Crass
|
|
108
121
|
@s.consume
|
109
122
|
create_token(:substring_match)
|
110
123
|
|
124
|
+
# Non-standard: Preserve the IE * hack.
|
111
125
|
elsif @options[:preserve_hacks] && @s.peek =~ RE_NAME_START
|
112
|
-
# NON-STANDARD: IE * hack
|
113
126
|
@s.reconsume
|
114
127
|
consume_ident
|
115
128
|
|
@@ -118,7 +131,7 @@ module Crass
|
|
118
131
|
end
|
119
132
|
|
120
133
|
when :+
|
121
|
-
if start_number?
|
134
|
+
if start_number?
|
122
135
|
@s.reconsume
|
123
136
|
consume_numeric
|
124
137
|
else
|
@@ -129,47 +142,31 @@ module Crass
|
|
129
142
|
create_token(:comma)
|
130
143
|
|
131
144
|
when :-
|
132
|
-
|
145
|
+
nextTwoChars = @s.peek(2)
|
146
|
+
nextThreeChars = char + nextTwoChars
|
147
|
+
|
148
|
+
if start_number?(nextThreeChars)
|
133
149
|
@s.reconsume
|
134
150
|
consume_numeric
|
135
|
-
elsif
|
136
|
-
@s.reconsume
|
137
|
-
consume_ident
|
138
|
-
elsif @s.peek(2) == '->'
|
151
|
+
elsif nextTwoChars == '->'
|
139
152
|
@s.consume
|
140
153
|
@s.consume
|
141
154
|
create_token(:cdc)
|
155
|
+
elsif start_identifier?(nextThreeChars)
|
156
|
+
@s.reconsume
|
157
|
+
consume_ident
|
142
158
|
else
|
143
159
|
create_token(:delim, :value => char)
|
144
160
|
end
|
145
161
|
|
146
162
|
when :'.'
|
147
|
-
if start_number?
|
163
|
+
if start_number?
|
148
164
|
@s.reconsume
|
149
165
|
consume_numeric
|
150
166
|
else
|
151
167
|
create_token(:delim, :value => char)
|
152
168
|
end
|
153
169
|
|
154
|
-
when :/
|
155
|
-
if @s.peek == '*'
|
156
|
-
@s.consume
|
157
|
-
|
158
|
-
if text = @s.scan_until(RE_COMMENT_CLOSE)
|
159
|
-
text.slice!(-2, 2)
|
160
|
-
else
|
161
|
-
text = @s.consume_rest
|
162
|
-
end
|
163
|
-
|
164
|
-
if @options[:preserve_comments]
|
165
|
-
create_token(:comment, :value => text)
|
166
|
-
else
|
167
|
-
consume
|
168
|
-
end
|
169
|
-
else
|
170
|
-
create_token(:delim, :value => char)
|
171
|
-
end
|
172
|
-
|
173
170
|
when :':'
|
174
171
|
create_token(:colon)
|
175
172
|
|
@@ -188,7 +185,7 @@ module Crass
|
|
188
185
|
end
|
189
186
|
|
190
187
|
when :'@'
|
191
|
-
if start_identifier?
|
188
|
+
if start_identifier?(@s.peek(3))
|
192
189
|
create_token(:at_keyword, :value => consume_name)
|
193
190
|
else
|
194
191
|
create_token(:delim, :value => char)
|
@@ -198,10 +195,11 @@ module Crass
|
|
198
195
|
create_token(:'[')
|
199
196
|
|
200
197
|
when :'\\'
|
201
|
-
if valid_escape?
|
198
|
+
if valid_escape?
|
202
199
|
@s.reconsume
|
203
200
|
consume_ident
|
204
201
|
else
|
202
|
+
# Parse error.
|
205
203
|
create_token(:delim,
|
206
204
|
:error => true,
|
207
205
|
:value => char)
|
@@ -273,14 +271,14 @@ module Crass
|
|
273
271
|
|
274
272
|
# Consumes the remnants of a bad URL and returns the consumed text.
|
275
273
|
#
|
276
|
-
# http://
|
274
|
+
# 4.3.15. http://dev.w3.org/csswg/css-syntax/#consume-the-remnants-of-a-bad-url
|
277
275
|
def consume_bad_url
|
278
276
|
text = ''
|
279
277
|
|
280
278
|
until @s.eos?
|
281
|
-
if valid_escape?
|
279
|
+
if valid_escape?
|
282
280
|
text << consume_escaped
|
283
|
-
elsif valid_escape?
|
281
|
+
elsif valid_escape?(@s.peek(2))
|
284
282
|
@s.consume
|
285
283
|
text << consume_escaped
|
286
284
|
else
|
@@ -297,19 +295,38 @@ module Crass
|
|
297
295
|
text
|
298
296
|
end
|
299
297
|
|
298
|
+
# Consumes comments and returns them, or `nil` if no comments were consumed.
|
299
|
+
#
|
300
|
+
# 4.3.2. http://dev.w3.org/csswg/css-syntax/#consume-comments
|
301
|
+
def consume_comments
|
302
|
+
if @s.peek(2) == '/*'
|
303
|
+
@s.consume
|
304
|
+
@s.consume
|
305
|
+
|
306
|
+
if text = @s.scan_until(RE_COMMENT_CLOSE)
|
307
|
+
text.slice!(-2, 2)
|
308
|
+
else
|
309
|
+
# Parse error.
|
310
|
+
text = @s.consume_rest
|
311
|
+
end
|
312
|
+
|
313
|
+
return create_token(:comment, :value => text)
|
314
|
+
end
|
315
|
+
|
316
|
+
nil
|
317
|
+
end
|
318
|
+
|
300
319
|
# Consumes an escaped code point and returns its unescaped value.
|
301
320
|
#
|
302
321
|
# This method assumes that the `\` has already been consumed, and that the
|
303
322
|
# next character in the input has already been verified not to be a newline
|
304
323
|
# or EOF.
|
305
324
|
#
|
306
|
-
# http://
|
325
|
+
# 4.3.8. http://dev.w3.org/csswg/css-syntax/#consume-an-escaped-code-point
|
307
326
|
def consume_escaped
|
308
|
-
|
309
|
-
when @s.eos?
|
310
|
-
"\ufffd"
|
327
|
+
return "\ufffd" if @s.eos?
|
311
328
|
|
312
|
-
|
329
|
+
if hex_str = @s.scan(RE_HEX)
|
313
330
|
@s.consume if @s.peek =~ RE_WHITESPACE
|
314
331
|
|
315
332
|
codepoint = hex_str.hex
|
@@ -318,19 +335,18 @@ module Crass
|
|
318
335
|
codepoint.between?(0xD800, 0xDFFF) ||
|
319
336
|
codepoint > 0x10FFFF
|
320
337
|
|
321
|
-
"\ufffd"
|
338
|
+
return "\ufffd"
|
322
339
|
else
|
323
|
-
codepoint.chr(Encoding::UTF_8)
|
340
|
+
return codepoint.chr(Encoding::UTF_8)
|
324
341
|
end
|
325
|
-
|
326
|
-
else
|
327
|
-
@s.consume
|
328
342
|
end
|
343
|
+
|
344
|
+
@s.consume
|
329
345
|
end
|
330
346
|
|
331
347
|
# Consumes an ident-like token and returns it.
|
332
348
|
#
|
333
|
-
# http://
|
349
|
+
# 4.3.4. http://dev.w3.org/csswg/css-syntax/#consume-an-ident-like-token
|
334
350
|
def consume_ident
|
335
351
|
value = consume_name
|
336
352
|
|
@@ -338,7 +354,13 @@ module Crass
|
|
338
354
|
@s.consume
|
339
355
|
|
340
356
|
if value.downcase == 'url'
|
341
|
-
|
357
|
+
@s.consume while @s.peek(2) =~ RE_WHITESPACE_ANCHORED
|
358
|
+
|
359
|
+
if @s.peek(2) =~ RE_QUOTED_URL_START
|
360
|
+
create_token(:function, :value => value)
|
361
|
+
else
|
362
|
+
consume_url
|
363
|
+
end
|
342
364
|
else
|
343
365
|
create_token(:function, :value => value)
|
344
366
|
end
|
@@ -349,37 +371,39 @@ module Crass
|
|
349
371
|
|
350
372
|
# Consumes a name and returns it.
|
351
373
|
#
|
352
|
-
# http://
|
374
|
+
# 4.3.12. http://dev.w3.org/csswg/css-syntax/#consume-a-name
|
353
375
|
def consume_name
|
354
376
|
result = ''
|
355
377
|
|
356
|
-
|
378
|
+
until @s.eos?
|
357
379
|
if match = @s.scan(RE_NAME)
|
358
380
|
result << match
|
359
381
|
next
|
360
382
|
end
|
361
383
|
|
362
|
-
char = @s.
|
384
|
+
char = @s.consume
|
363
385
|
|
364
|
-
if
|
365
|
-
@s.consume
|
386
|
+
if valid_escape?
|
366
387
|
result << consume_escaped
|
367
388
|
|
368
|
-
#
|
369
|
-
elsif
|
389
|
+
# Non-standard: IE * hack
|
390
|
+
elsif char == '*' && @options[:preserve_hacks]
|
370
391
|
result << @s.consume
|
371
392
|
|
372
393
|
else
|
394
|
+
@s.reconsume
|
373
395
|
return result
|
374
396
|
end
|
375
397
|
end
|
398
|
+
|
399
|
+
result
|
376
400
|
end
|
377
401
|
|
378
402
|
# Consumes a number and returns a 3-element array containing the number's
|
379
403
|
# original representation, its numeric value, and its type (either
|
380
404
|
# `:integer` or `:number`).
|
381
405
|
#
|
382
|
-
# http://
|
406
|
+
# 4.3.13. http://dev.w3.org/csswg/css-syntax/#consume-a-number
|
383
407
|
def consume_number
|
384
408
|
repr = ''
|
385
409
|
type = :integer
|
@@ -402,11 +426,11 @@ module Crass
|
|
402
426
|
|
403
427
|
# Consumes a numeric token and returns it.
|
404
428
|
#
|
405
|
-
# http://
|
429
|
+
# 4.3.3. http://dev.w3.org/csswg/css-syntax/#consume-a-numeric-token
|
406
430
|
def consume_numeric
|
407
431
|
number = consume_number
|
408
432
|
|
409
|
-
if start_identifier?
|
433
|
+
if start_identifier?(@s.peek(3))
|
410
434
|
create_token(:dimension,
|
411
435
|
:repr => number[0],
|
412
436
|
:type => number[2],
|
@@ -432,9 +456,10 @@ module Crass
|
|
432
456
|
# Consumes a string token that ends at the given character, and returns the
|
433
457
|
# token.
|
434
458
|
#
|
435
|
-
# http://
|
436
|
-
def consume_string(ending)
|
437
|
-
|
459
|
+
# 4.3.5. http://dev.w3.org/csswg/css-syntax/#consume-a-string-token
|
460
|
+
def consume_string(ending = nil)
|
461
|
+
ending = @s.current if ending.nil?
|
462
|
+
value = ''
|
438
463
|
|
439
464
|
until @s.eos?
|
440
465
|
case char = @s.consume
|
@@ -442,6 +467,7 @@ module Crass
|
|
442
467
|
break
|
443
468
|
|
444
469
|
when "\n"
|
470
|
+
# Parse error.
|
445
471
|
@s.reconsume
|
446
472
|
return create_token(:bad_string,
|
447
473
|
:error => true,
|
@@ -471,7 +497,7 @@ module Crass
|
|
471
497
|
# Consumes a Unicode range token and returns it. Assumes the initial "u+" or
|
472
498
|
# "U+" has already been consumed.
|
473
499
|
#
|
474
|
-
# http://
|
500
|
+
# 4.3.7. http://dev.w3.org/csswg/css-syntax/#consume-a-unicode-range-token
|
475
501
|
def consume_unicode_range
|
476
502
|
value = @s.scan(RE_HEX) || ''
|
477
503
|
|
@@ -503,71 +529,46 @@ module Crass
|
|
503
529
|
# Consumes a URL token and returns it. Assumes the original "url(" has
|
504
530
|
# already been consumed.
|
505
531
|
#
|
506
|
-
# http://
|
532
|
+
# 4.3.6. http://dev.w3.org/csswg/css-syntax/#consume-a-url-token
|
507
533
|
def consume_url
|
508
534
|
value = ''
|
509
535
|
|
510
536
|
@s.scan(RE_WHITESPACE)
|
511
537
|
|
512
|
-
if @s.eos?
|
513
|
-
return create_token(:url, :value => value)
|
514
|
-
end
|
515
|
-
|
516
|
-
# Quoted URL.
|
517
|
-
next_char = @s.peek
|
518
|
-
|
519
|
-
if next_char == "'" || next_char == '"'
|
520
|
-
string = consume_string(@s.consume)
|
521
|
-
|
522
|
-
if string[:node] == :bad_string
|
523
|
-
return create_token(:bad_url,
|
524
|
-
:value => string[:value] + consume_bad_url)
|
525
|
-
end
|
526
|
-
|
527
|
-
value = string[:value]
|
528
|
-
@s.scan(RE_WHITESPACE)
|
529
|
-
|
530
|
-
if @s.eos? || @s.peek == ')'
|
531
|
-
@s.consume
|
532
|
-
return create_token(:url, :value => value)
|
533
|
-
else
|
534
|
-
return create_token(:bad_url, :value => value + consume_bad_url)
|
535
|
-
end
|
536
|
-
end
|
537
|
-
|
538
|
-
# Unquoted URL.
|
539
538
|
until @s.eos?
|
540
539
|
case char = @s.consume
|
541
|
-
|
542
|
-
break
|
543
|
-
|
544
|
-
when RE_WHITESPACE
|
545
|
-
@s.scan(RE_WHITESPACE)
|
546
|
-
|
547
|
-
if @s.eos? || @s.peek == ')'
|
548
|
-
@s.consume
|
540
|
+
when ')'
|
549
541
|
break
|
550
|
-
else
|
551
|
-
return create_token(:bad_url, :value => value + consume_bad_url)
|
552
|
-
end
|
553
542
|
|
554
|
-
|
555
|
-
|
556
|
-
:error => true,
|
557
|
-
:value => value + consume_bad_url)
|
543
|
+
when RE_WHITESPACE
|
544
|
+
@s.scan(RE_WHITESPACE)
|
558
545
|
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
546
|
+
if @s.eos? || @s.peek == ')'
|
547
|
+
@s.consume
|
548
|
+
break
|
549
|
+
else
|
550
|
+
return create_token(:bad_url, :value => value + consume_bad_url)
|
551
|
+
end
|
552
|
+
|
553
|
+
when '"', "'", '(', RE_NON_PRINTABLE
|
554
|
+
# Parse error.
|
563
555
|
return create_token(:bad_url,
|
564
556
|
:error => true,
|
565
|
-
:value => value + consume_bad_url
|
566
|
-
|
567
|
-
|
557
|
+
:value => value + consume_bad_url)
|
558
|
+
|
559
|
+
when '\\'
|
560
|
+
if valid_escape?
|
561
|
+
value << consume_escaped
|
562
|
+
else
|
563
|
+
# Parse error.
|
564
|
+
return create_token(:bad_url,
|
565
|
+
:error => true,
|
566
|
+
:value => value + consume_bad_url
|
567
|
+
)
|
568
|
+
end
|
568
569
|
|
569
|
-
|
570
|
-
|
570
|
+
else
|
571
|
+
value << char
|
571
572
|
end
|
572
573
|
end
|
573
574
|
|
@@ -576,7 +577,7 @@ module Crass
|
|
576
577
|
|
577
578
|
# Converts a valid CSS number string into a number and returns the number.
|
578
579
|
#
|
579
|
-
# http://
|
580
|
+
# 4.3.14. http://dev.w3.org/csswg/css-syntax/#convert-a-string-to-a-number
|
580
581
|
def convert_string_to_number(str)
|
581
582
|
matches = RE_NUMBER_STR.match(str)
|
582
583
|
|
@@ -603,7 +604,7 @@ module Crass
|
|
603
604
|
|
604
605
|
# Preprocesses _input_ to prepare it for the tokenizer.
|
605
606
|
#
|
606
|
-
# http://
|
607
|
+
# 3.3. http://dev.w3.org/csswg/css-syntax/#input-preprocessing
|
607
608
|
def preprocess(input)
|
608
609
|
input = input.to_s.encode('UTF-8',
|
609
610
|
:invalid => :replace,
|
@@ -615,16 +616,17 @@ module Crass
|
|
615
616
|
end
|
616
617
|
|
617
618
|
# Returns `true` if the given three-character _text_ would start an
|
618
|
-
# identifier. If _text_ is `nil`, the next
|
619
|
-
# stream will be checked, but will not be consumed.
|
619
|
+
# identifier. If _text_ is `nil`, the current and next two characters in the
|
620
|
+
# input stream will be checked, but will not be consumed.
|
620
621
|
#
|
621
|
-
# http://
|
622
|
+
# 4.3.10. http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
|
622
623
|
def start_identifier?(text = nil)
|
623
|
-
text = @s.peek(
|
624
|
+
text = @s.current + @s.peek(2) if text.nil?
|
624
625
|
|
625
626
|
case text[0]
|
626
627
|
when '-'
|
627
|
-
|
628
|
+
nextChar = text[1]
|
629
|
+
!!(nextChar == '-' || nextChar =~ RE_NAME_START || valid_escape?(text[1, 2]))
|
628
630
|
|
629
631
|
when RE_NAME_START
|
630
632
|
true
|
@@ -638,12 +640,12 @@ module Crass
|
|
638
640
|
end
|
639
641
|
|
640
642
|
# Returns `true` if the given three-character _text_ would start a number.
|
641
|
-
# If _text_ is `nil`, the next
|
642
|
-
# checked, but will not be consumed.
|
643
|
+
# If _text_ is `nil`, the current and next two characters in the input
|
644
|
+
# stream will be checked, but will not be consumed.
|
643
645
|
#
|
644
|
-
# http://
|
646
|
+
# 4.3.11. http://dev.w3.org/csswg/css-syntax/#starts-with-a-number
|
645
647
|
def start_number?(text = nil)
|
646
|
-
text = @s.peek(
|
648
|
+
text = @s.current + @s.peek(2) if text.nil?
|
647
649
|
|
648
650
|
case text[0]
|
649
651
|
when '+', '-'
|
@@ -674,12 +676,12 @@ module Crass
|
|
674
676
|
end
|
675
677
|
|
676
678
|
# Returns `true` if the given two-character _text_ is the beginning of a
|
677
|
-
# valid escape sequence. If _text_ is `nil`, the
|
678
|
-
# input stream will be checked, but will not be consumed.
|
679
|
+
# valid escape sequence. If _text_ is `nil`, the current and next character
|
680
|
+
# in the input stream will be checked, but will not be consumed.
|
679
681
|
#
|
680
|
-
# http://
|
682
|
+
# 4.3.9. http://dev.w3.org/csswg/css-syntax/#starts-with-a-valid-escape
|
681
683
|
def valid_escape?(text = nil)
|
682
|
-
text = @s.peek
|
684
|
+
text = @s.current + @s.peek if text.nil?
|
683
685
|
!!(text[0] == '\\' && text[1] != "\n")
|
684
686
|
end
|
685
687
|
end
|