crass 0.2.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,268 @@
|
|
1
|
+
/*! HTML5 Boilerplate v4.3.0 | MIT License | http://h5bp.com/ */
|
2
|
+
|
3
|
+
/*
|
4
|
+
* What follows is the result of much research on cross-browser styling.
|
5
|
+
* Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,
|
6
|
+
* Kroc Camen, and the H5BP dev community and team.
|
7
|
+
*/
|
8
|
+
|
9
|
+
/* ==========================================================================
|
10
|
+
Base styles: opinionated defaults
|
11
|
+
========================================================================== */
|
12
|
+
|
13
|
+
html {
|
14
|
+
color: #222;
|
15
|
+
font-size: 1em;
|
16
|
+
line-height: 1.4;
|
17
|
+
}
|
18
|
+
|
19
|
+
/*
|
20
|
+
* Remove text-shadow in selection highlight: h5bp.com/i
|
21
|
+
* These selection rule sets have to be separate.
|
22
|
+
* Customize the background color to match your design.
|
23
|
+
*/
|
24
|
+
|
25
|
+
::-moz-selection {
|
26
|
+
background: #b3d4fc;
|
27
|
+
text-shadow: none;
|
28
|
+
}
|
29
|
+
|
30
|
+
::selection {
|
31
|
+
background: #b3d4fc;
|
32
|
+
text-shadow: none;
|
33
|
+
}
|
34
|
+
|
35
|
+
/*
|
36
|
+
* A better looking default horizontal rule
|
37
|
+
*/
|
38
|
+
|
39
|
+
hr {
|
40
|
+
display: block;
|
41
|
+
height: 1px;
|
42
|
+
border: 0;
|
43
|
+
border-top: 1px solid #ccc;
|
44
|
+
margin: 1em 0;
|
45
|
+
padding: 0;
|
46
|
+
}
|
47
|
+
|
48
|
+
/*
|
49
|
+
* Remove the gap between images, videos, audio and canvas and the bottom of
|
50
|
+
* their containers: h5bp.com/i/440
|
51
|
+
*/
|
52
|
+
|
53
|
+
audio,
|
54
|
+
canvas,
|
55
|
+
img,
|
56
|
+
svg,
|
57
|
+
video {
|
58
|
+
vertical-align: middle;
|
59
|
+
}
|
60
|
+
|
61
|
+
/*
|
62
|
+
* Remove default fieldset styles.
|
63
|
+
*/
|
64
|
+
|
65
|
+
fieldset {
|
66
|
+
border: 0;
|
67
|
+
margin: 0;
|
68
|
+
padding: 0;
|
69
|
+
}
|
70
|
+
|
71
|
+
/*
|
72
|
+
* Allow only vertical resizing of textareas.
|
73
|
+
*/
|
74
|
+
|
75
|
+
textarea {
|
76
|
+
resize: vertical;
|
77
|
+
}
|
78
|
+
|
79
|
+
/* ==========================================================================
|
80
|
+
Browser Upgrade Prompt
|
81
|
+
========================================================================== */
|
82
|
+
|
83
|
+
.browserupgrade {
|
84
|
+
margin: 0.2em 0;
|
85
|
+
background: #ccc;
|
86
|
+
color: #000;
|
87
|
+
padding: 0.2em 0;
|
88
|
+
}
|
89
|
+
|
90
|
+
/* ==========================================================================
|
91
|
+
Author's custom styles
|
92
|
+
========================================================================== */
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
/* ==========================================================================
|
111
|
+
Helper classes
|
112
|
+
========================================================================== */
|
113
|
+
|
114
|
+
/*
|
115
|
+
* Hide visually and from screen readers: h5bp.com/u
|
116
|
+
*/
|
117
|
+
|
118
|
+
.hidden {
|
119
|
+
display: none !important;
|
120
|
+
visibility: hidden;
|
121
|
+
}
|
122
|
+
|
123
|
+
/*
|
124
|
+
* Hide only visually, but have it available for screen readers: h5bp.com/v
|
125
|
+
*/
|
126
|
+
|
127
|
+
.visuallyhidden {
|
128
|
+
border: 0;
|
129
|
+
clip: rect(0 0 0 0);
|
130
|
+
height: 1px;
|
131
|
+
margin: -1px;
|
132
|
+
overflow: hidden;
|
133
|
+
padding: 0;
|
134
|
+
position: absolute;
|
135
|
+
width: 1px;
|
136
|
+
}
|
137
|
+
|
138
|
+
/*
|
139
|
+
* Extends the .visuallyhidden class to allow the element to be focusable
|
140
|
+
* when navigated to via the keyboard: h5bp.com/p
|
141
|
+
*/
|
142
|
+
|
143
|
+
.visuallyhidden.focusable:active,
|
144
|
+
.visuallyhidden.focusable:focus {
|
145
|
+
clip: auto;
|
146
|
+
height: auto;
|
147
|
+
margin: 0;
|
148
|
+
overflow: visible;
|
149
|
+
position: static;
|
150
|
+
width: auto;
|
151
|
+
}
|
152
|
+
|
153
|
+
/*
|
154
|
+
* Hide visually and from screen readers, but maintain layout
|
155
|
+
*/
|
156
|
+
|
157
|
+
.invisible {
|
158
|
+
visibility: hidden;
|
159
|
+
}
|
160
|
+
|
161
|
+
/*
|
162
|
+
* Clearfix: contain floats
|
163
|
+
*
|
164
|
+
* For modern browsers
|
165
|
+
* 1. The space content is one way to avoid an Opera bug when the
|
166
|
+
* `contenteditable` attribute is included anywhere else in the document.
|
167
|
+
* Otherwise it causes space to appear at the top and bottom of elements
|
168
|
+
* that receive the `clearfix` class.
|
169
|
+
* 2. The use of `table` rather than `block` is only necessary if using
|
170
|
+
* `:before` to contain the top-margins of child elements.
|
171
|
+
*/
|
172
|
+
|
173
|
+
.clearfix:before,
|
174
|
+
.clearfix:after {
|
175
|
+
content: " "; /* 1 */
|
176
|
+
display: table; /* 2 */
|
177
|
+
}
|
178
|
+
|
179
|
+
.clearfix:after {
|
180
|
+
clear: both;
|
181
|
+
}
|
182
|
+
|
183
|
+
/* ==========================================================================
|
184
|
+
EXAMPLE Media Queries for Responsive Design.
|
185
|
+
These examples override the primary ('mobile first') styles.
|
186
|
+
Modify as content requires.
|
187
|
+
========================================================================== */
|
188
|
+
|
189
|
+
@media only screen and (min-width: 35em) {
|
190
|
+
/* Style adjustments for viewports that meet the condition */
|
191
|
+
}
|
192
|
+
|
193
|
+
@media print,
|
194
|
+
(-o-min-device-pixel-ratio: 5/4),
|
195
|
+
(-webkit-min-device-pixel-ratio: 1.25),
|
196
|
+
(min-resolution: 120dpi) {
|
197
|
+
/* Style adjustments for high resolution devices */
|
198
|
+
}
|
199
|
+
|
200
|
+
/* ==========================================================================
|
201
|
+
Print styles.
|
202
|
+
Inlined to avoid the additional HTTP request: h5bp.com/r
|
203
|
+
========================================================================== */
|
204
|
+
|
205
|
+
@media print {
|
206
|
+
*,
|
207
|
+
*:before,
|
208
|
+
*:after {
|
209
|
+
background: transparent !important;
|
210
|
+
color: #000 !important; /* Black prints faster: h5bp.com/s */
|
211
|
+
box-shadow: none !important;
|
212
|
+
text-shadow: none !important;
|
213
|
+
}
|
214
|
+
|
215
|
+
a,
|
216
|
+
a:visited {
|
217
|
+
text-decoration: underline;
|
218
|
+
}
|
219
|
+
|
220
|
+
a[href]:after {
|
221
|
+
content: " (" attr(href) ")";
|
222
|
+
}
|
223
|
+
|
224
|
+
abbr[title]:after {
|
225
|
+
content: " (" attr(title) ")";
|
226
|
+
}
|
227
|
+
|
228
|
+
/*
|
229
|
+
* Don't show links that are fragment identifiers,
|
230
|
+
* or use the `javascript:` pseudo protocol
|
231
|
+
*/
|
232
|
+
|
233
|
+
a[href^="#"]:after,
|
234
|
+
a[href^="javascript:"]:after {
|
235
|
+
content: "";
|
236
|
+
}
|
237
|
+
|
238
|
+
pre,
|
239
|
+
blockquote {
|
240
|
+
border: 1px solid #999;
|
241
|
+
page-break-inside: avoid;
|
242
|
+
}
|
243
|
+
|
244
|
+
thead {
|
245
|
+
display: table-header-group; /* h5bp.com/t */
|
246
|
+
}
|
247
|
+
|
248
|
+
tr,
|
249
|
+
img {
|
250
|
+
page-break-inside: avoid;
|
251
|
+
}
|
252
|
+
|
253
|
+
img {
|
254
|
+
max-width: 100% !important;
|
255
|
+
}
|
256
|
+
|
257
|
+
p,
|
258
|
+
h2,
|
259
|
+
h3 {
|
260
|
+
orphans: 3;
|
261
|
+
widows: 3;
|
262
|
+
}
|
263
|
+
|
264
|
+
h2,
|
265
|
+
h3 {
|
266
|
+
page-break-after: avoid;
|
267
|
+
}
|
268
|
+
}
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# This file loads and runs Simon Sapin's CSS parsing tests, which live under the
|
4
|
+
# test/css-parsing-tests directory. The original test repo can be found at:
|
5
|
+
#
|
6
|
+
# https://github.com/SimonSapin/css-parsing-tests/
|
7
|
+
|
8
|
+
require 'json'
|
9
|
+
require_relative 'support/common'
|
10
|
+
|
11
|
+
def load_css_tests(filename)
|
12
|
+
JSON.parse(File.read(File.join(File.dirname(__FILE__), "/css-parsing-tests/#{filename}")))
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'CSS Parsing Tests' do
|
16
|
+
describe 'component_value_list' do
|
17
|
+
make_my_diffs_pretty!
|
18
|
+
parallelize_me!
|
19
|
+
|
20
|
+
tests = load_css_tests('component_value_list.json')
|
21
|
+
|
22
|
+
tests.each_slice(2) do |test|
|
23
|
+
css = test[0]
|
24
|
+
expected = test[1]
|
25
|
+
|
26
|
+
it "should parse: #{css.gsub("\n", "\\n")}" do
|
27
|
+
parser = Crass::Parser.new(css)
|
28
|
+
assert_equal(expected, translate_tokens(parser.parse_component_values))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'declaration_list' do
|
34
|
+
make_my_diffs_pretty!
|
35
|
+
parallelize_me!
|
36
|
+
|
37
|
+
tests = load_css_tests('declaration_list.json')
|
38
|
+
|
39
|
+
tests.each_slice(2) do |test|
|
40
|
+
css = test[0]
|
41
|
+
expected = test[1]
|
42
|
+
|
43
|
+
it "should parse: #{css.gsub("\n", "\\n")}" do
|
44
|
+
parser = Crass::Parser.new(css)
|
45
|
+
assert_equal(expected, translate_tokens(parser.parse_declarations(parser.tokens, {:strict => true})))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'one_component_value' do
|
51
|
+
make_my_diffs_pretty!
|
52
|
+
parallelize_me!
|
53
|
+
|
54
|
+
tests = load_css_tests('one_component_value.json')
|
55
|
+
|
56
|
+
tests.each_slice(2) do |test|
|
57
|
+
css = test[0]
|
58
|
+
expected = test[1]
|
59
|
+
|
60
|
+
it "should parse: #{css.gsub("\n", "\\n")}" do
|
61
|
+
parser = Crass::Parser.new(css)
|
62
|
+
assert_equal(expected, translate_tokens(parser.parse_component_value)[0])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'one_declaration' do
|
68
|
+
make_my_diffs_pretty!
|
69
|
+
parallelize_me!
|
70
|
+
|
71
|
+
tests = load_css_tests('one_declaration.json')
|
72
|
+
|
73
|
+
tests.each_slice(2) do |test|
|
74
|
+
css = test[0]
|
75
|
+
expected = test[1]
|
76
|
+
|
77
|
+
it "should parse: #{css.gsub("\n", "\\n")}" do
|
78
|
+
parser = Crass::Parser.new(css)
|
79
|
+
assert_equal(expected, translate_tokens(parser.parse_declaration)[0])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe 'one_rule' do
|
85
|
+
make_my_diffs_pretty!
|
86
|
+
parallelize_me!
|
87
|
+
|
88
|
+
tests = load_css_tests('one_rule.json')
|
89
|
+
|
90
|
+
tests.each_slice(2) do |test|
|
91
|
+
css = test[0]
|
92
|
+
expected = test[1]
|
93
|
+
|
94
|
+
it "should parse: #{css.gsub("\n", "\\n")}" do
|
95
|
+
parser = Crass::Parser.new(css)
|
96
|
+
assert_equal(expected, translate_tokens(parser.parse_rule)[0])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'rule_list' do
|
102
|
+
make_my_diffs_pretty!
|
103
|
+
parallelize_me!
|
104
|
+
|
105
|
+
tests = load_css_tests('rule_list.json')
|
106
|
+
|
107
|
+
tests.each_slice(2) do |test|
|
108
|
+
css = test[0]
|
109
|
+
expected = test[1]
|
110
|
+
|
111
|
+
it "should parse: #{css.gsub("\n", "\\n")}" do
|
112
|
+
parser = Crass::Parser.new(css)
|
113
|
+
rules = parser.consume_rules
|
114
|
+
|
115
|
+
# Remove non-standard whitespace tokens.
|
116
|
+
rules.reject! do |token|
|
117
|
+
node = token[:node]
|
118
|
+
node == :whitespace
|
119
|
+
end
|
120
|
+
|
121
|
+
assert_equal(expected, translate_tokens(rules))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe 'stylesheet' do
|
127
|
+
make_my_diffs_pretty!
|
128
|
+
parallelize_me!
|
129
|
+
|
130
|
+
tests = load_css_tests('stylesheet.json')
|
131
|
+
|
132
|
+
tests.each_slice(2) do |test|
|
133
|
+
css = test[0]
|
134
|
+
expected = test[1]
|
135
|
+
|
136
|
+
it "should parse: #{css.gsub("\n", "\\n")}" do
|
137
|
+
parser = Crass::Parser.new(css)
|
138
|
+
rules = parser.consume_rules(:top_level => true)
|
139
|
+
|
140
|
+
# Remove non-standard whitespace tokens.
|
141
|
+
rules.reject! do |token|
|
142
|
+
node = token[:node]
|
143
|
+
node == :whitespace
|
144
|
+
end
|
145
|
+
|
146
|
+
assert_equal(expected, translate_tokens(rules))
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -29,169 +29,42 @@ describe 'Crass::Parser' do
|
|
29
29
|
assert_tokens(";; /**/ ; ;", tree, 0, :preserve_comments => true)
|
30
30
|
end
|
31
31
|
|
32
|
-
it 'should parse a list of declarations' do
|
33
|
-
tree = parse("a:b; c:d 42!important;\n")
|
34
|
-
assert_equal(4, tree.size)
|
35
|
-
|
36
|
-
prop = tree[0]
|
37
|
-
assert_equal(:property, prop[:node])
|
38
|
-
assert_equal("a", prop[:name])
|
39
|
-
assert_equal("b", prop[:value])
|
40
|
-
assert_equal(false, prop[:important])
|
41
|
-
assert_tokens("a:b;", prop[:tokens])
|
42
|
-
|
43
|
-
assert_equal([
|
44
|
-
{:node=>:ident, :pos=>2, :raw=>"b", :value=>"b"}
|
45
|
-
], prop[:children])
|
46
|
-
|
47
|
-
assert_tokens(" ", tree[1], 4)
|
48
|
-
|
49
|
-
prop = tree[2]
|
50
|
-
assert_equal(:property, prop[:node])
|
51
|
-
assert_equal("c", prop[:name])
|
52
|
-
assert_equal("d 42", prop[:value])
|
53
|
-
assert_equal(true, prop[:important])
|
54
|
-
assert_tokens("c:d 42!important;", prop[:tokens], 5)
|
55
|
-
|
56
|
-
assert_equal([
|
57
|
-
{:node=>:ident, :pos=>7, :raw=>"d", :value=>"d"},
|
58
|
-
{:node=>:whitespace, :pos=>8, :raw=>" "},
|
59
|
-
{:node=>:number,
|
60
|
-
:pos=>9,
|
61
|
-
:raw=>"42",
|
62
|
-
:repr=>"42",
|
63
|
-
:type=>:integer,
|
64
|
-
:value=>42}
|
65
|
-
], prop[:children])
|
66
|
-
|
67
|
-
assert_tokens("\n", tree[3], 22)
|
68
|
-
end
|
69
|
-
|
70
32
|
it 'should parse at-rules even though they may be invalid in the given context' do
|
71
33
|
tree = parse("@import 'foo.css'; a:b; @import 'bar.css'")
|
72
|
-
assert_equal(5, tree.size)
|
73
|
-
|
74
|
-
rule = tree[0]
|
75
|
-
assert_equal(:at_rule, rule[:node])
|
76
|
-
assert_equal("import", rule[:name])
|
77
|
-
assert_tokens(" 'foo.css'", rule[:prelude], 7)
|
78
|
-
assert_tokens("@import 'foo.css';", rule[:tokens])
|
79
|
-
|
80
|
-
assert_tokens(" ", tree[1], 18)
|
81
|
-
|
82
|
-
prop = tree[2]
|
83
|
-
assert_equal(:property, prop[:node])
|
84
|
-
assert_equal("a", prop[:name])
|
85
|
-
assert_equal("b", prop[:value])
|
86
|
-
assert_equal(false, prop[:important])
|
87
|
-
assert_tokens("a:b;", prop[:tokens], 19)
|
88
34
|
|
89
35
|
assert_equal([
|
90
|
-
{:node=>:
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
block = value[2]
|
123
|
-
assert_equal(:simple_block, block[:node])
|
124
|
-
assert_equal("{", block[:start])
|
125
|
-
assert_equal("}", block[:end])
|
126
|
-
assert_tokens(";", block[:value], 20)
|
127
|
-
assert_tokens("{;}", block[:tokens], 19)
|
128
|
-
|
129
|
-
assert_tokens(" ", tree[1], 23)
|
130
|
-
|
131
|
-
prop = tree[2]
|
132
|
-
assert_equal(:property, prop[:node])
|
133
|
-
assert_equal("a", prop[:name])
|
134
|
-
assert_equal("b", prop[:value])
|
135
|
-
assert_equal(false, prop[:important])
|
136
|
-
assert_tokens("a:b;", prop[:tokens], 24)
|
137
|
-
assert_equal([
|
138
|
-
{:node=>:ident, :pos=>26, :raw=>"b", :value=>"b"}
|
139
|
-
], prop[:children])
|
140
|
-
|
141
|
-
assert_tokens("; ", tree[3..4], 28)
|
142
|
-
|
143
|
-
rule = tree[5]
|
144
|
-
assert_equal(:at_rule, rule[:node])
|
145
|
-
assert_equal("media", rule[:name])
|
146
|
-
assert_tokens(" print", rule[:prelude], 36)
|
147
|
-
assert_tokens("@media print{div{", rule[:tokens], 30)
|
148
|
-
|
149
|
-
block = rule[:block]
|
150
|
-
assert_equal(:simple_block, block[:node])
|
151
|
-
assert_equal("{", block[:start])
|
152
|
-
assert_equal("}", block[:end])
|
153
|
-
assert_tokens("{div{", block[:tokens], 42)
|
154
|
-
|
155
|
-
value = block[:value]
|
156
|
-
assert_equal(2, value.size)
|
157
|
-
assert_tokens("div", value[0], 43)
|
158
|
-
|
159
|
-
block = value[1]
|
160
|
-
assert_equal(:simple_block, block[:node])
|
161
|
-
assert_equal("{", block[:start])
|
162
|
-
assert_equal("}", block[:end])
|
163
|
-
assert_equal([], block[:value])
|
164
|
-
assert_tokens("{", block[:tokens], 46)
|
165
|
-
end
|
166
|
-
|
167
|
-
it 'should discard invalid nodes' do
|
168
|
-
tree = parse("@ media screen { div{;}} a:b;; @media print{div{")
|
169
|
-
assert_equal(3, tree.size)
|
170
|
-
|
171
|
-
assert_tokens("; ", tree[0..1], 29)
|
172
|
-
|
173
|
-
rule = tree[2]
|
174
|
-
assert_equal(:at_rule, rule[:node])
|
175
|
-
assert_equal("media", rule[:name])
|
176
|
-
assert_tokens(" print", rule[:prelude], 37)
|
177
|
-
assert_tokens("@media print{div{", rule[:tokens], 31)
|
178
|
-
|
179
|
-
block = rule[:block]
|
180
|
-
assert_equal(:simple_block, block[:node])
|
181
|
-
assert_equal("{", block[:start])
|
182
|
-
assert_equal("}", block[:end])
|
183
|
-
assert_tokens("{div{", block[:tokens], 43)
|
184
|
-
|
185
|
-
value = block[:value]
|
186
|
-
assert_equal(2, value.size)
|
187
|
-
assert_tokens("div", value[0], 44)
|
188
|
-
|
189
|
-
block = value[1]
|
190
|
-
assert_equal(:simple_block, block[:node])
|
191
|
-
assert_equal("{", block[:start])
|
192
|
-
assert_equal("}", block[:end])
|
193
|
-
assert_equal([], block[:value])
|
194
|
-
assert_tokens("{", block[:tokens], 47)
|
36
|
+
{:node=>:at_rule,
|
37
|
+
:name=>"import",
|
38
|
+
:prelude=>
|
39
|
+
[{:node=>:whitespace, :pos=>7, :raw=>" "},
|
40
|
+
{:node=>:string, :pos=>8, :raw=>"'foo.css'", :value=>"foo.css"}],
|
41
|
+
:tokens=>
|
42
|
+
[{:node=>:at_keyword, :pos=>0, :raw=>"@import", :value=>"import"},
|
43
|
+
{:node=>:whitespace, :pos=>7, :raw=>" "},
|
44
|
+
{:node=>:string, :pos=>8, :raw=>"'foo.css'", :value=>"foo.css"},
|
45
|
+
{:node=>:semicolon, :pos=>17, :raw=>";"}]},
|
46
|
+
{:node=>:whitespace, :pos=>18, :raw=>" "},
|
47
|
+
{:node=>:property,
|
48
|
+
:name=>"a",
|
49
|
+
:value=>"b",
|
50
|
+
:children=>[{:node=>:ident, :pos=>21, :raw=>"b", :value=>"b"}],
|
51
|
+
:important=>false,
|
52
|
+
:tokens=>
|
53
|
+
[{:node=>:ident, :pos=>19, :raw=>"a", :value=>"a"},
|
54
|
+
{:node=>:colon, :pos=>20, :raw=>":"},
|
55
|
+
{:node=>:ident, :pos=>21, :raw=>"b", :value=>"b"}]},
|
56
|
+
{:node=>:semicolon, :pos=>22, :raw=>";"},
|
57
|
+
{:node=>:whitespace, :pos=>23, :raw=>" "},
|
58
|
+
{:node=>:at_rule,
|
59
|
+
:name=>"import",
|
60
|
+
:prelude=>
|
61
|
+
[{:node=>:whitespace, :pos=>31, :raw=>" "},
|
62
|
+
{:node=>:string, :pos=>32, :raw=>"'bar.css'", :value=>"bar.css"}],
|
63
|
+
:tokens=>
|
64
|
+
[{:node=>:at_keyword, :pos=>24, :raw=>"@import", :value=>"import"},
|
65
|
+
{:node=>:whitespace, :pos=>31, :raw=>" "},
|
66
|
+
{:node=>:string, :pos=>32, :raw=>"'bar.css'", :value=>"bar.css"}]}
|
67
|
+
], tree)
|
195
68
|
end
|
196
69
|
|
197
70
|
it 'should parse values containing functions' do
|
@@ -199,27 +72,35 @@ describe 'Crass::Parser' do
|
|
199
72
|
|
200
73
|
assert_equal([
|
201
74
|
{:node=>:property,
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
{:node=>:ident, :pos=>
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
75
|
+
:name=>"content",
|
76
|
+
:value=>"attr(data-foo) \" \"",
|
77
|
+
:children=>
|
78
|
+
[{:node=>:whitespace, :pos=>8, :raw=>" "},
|
79
|
+
{:node=>:function,
|
80
|
+
:name=>"attr",
|
81
|
+
:value=>[{:node=>:ident, :pos=>14, :raw=>"data-foo", :value=>"data-foo"}],
|
82
|
+
:tokens=>
|
83
|
+
[{:node=>:function, :pos=>9, :raw=>"attr(", :value=>"attr"},
|
84
|
+
{:node=>:ident, :pos=>14, :raw=>"data-foo", :value=>"data-foo"},
|
85
|
+
{:node=>:")", :pos=>22, :raw=>")"}]},
|
86
|
+
{:node=>:whitespace, :pos=>23, :raw=>" "},
|
87
|
+
{:node=>:string, :pos=>24, :raw=>"\" \"", :value=>" "}],
|
88
|
+
:important=>false,
|
89
|
+
:tokens=>
|
90
|
+
[{:node=>:ident, :pos=>0, :raw=>"content", :value=>"content"},
|
91
|
+
{:node=>:colon, :pos=>7, :raw=>":"},
|
92
|
+
{:node=>:whitespace, :pos=>8, :raw=>" "},
|
93
|
+
{:node=>:function,
|
94
|
+
:name=>"attr",
|
95
|
+
:value=>[{:node=>:ident, :pos=>14, :raw=>"data-foo", :value=>"data-foo"}],
|
96
|
+
:tokens=>
|
97
|
+
[{:node=>:function, :pos=>9, :raw=>"attr(", :value=>"attr"},
|
98
|
+
{:node=>:ident, :pos=>14, :raw=>"data-foo", :value=>"data-foo"},
|
99
|
+
{:node=>:")", :pos=>22, :raw=>")"}]},
|
100
|
+
{:node=>:whitespace, :pos=>23, :raw=>" "},
|
101
|
+
{:node=>:string, :pos=>24, :raw=>"\" \"", :value=>" "}]},
|
102
|
+
{:node=>:semicolon, :pos=>27, :raw=>";"}
|
103
|
+
], tree)
|
223
104
|
end
|
224
105
|
|
225
106
|
it 'should parse values containing nested functions' do
|
@@ -227,37 +108,81 @@ describe 'Crass::Parser' do
|
|
227
108
|
|
228
109
|
assert_equal([
|
229
110
|
{:node=>:property,
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
111
|
+
:name=>"width",
|
112
|
+
:value=>"expression(alert(1))",
|
113
|
+
:children=>
|
114
|
+
[{:node=>:whitespace, :pos=>6, :raw=>" "},
|
115
|
+
{:node=>:function,
|
116
|
+
:name=>"expression",
|
117
|
+
:value=>
|
118
|
+
[{:node=>:function,
|
119
|
+
:name=>"alert",
|
120
|
+
:value=>
|
121
|
+
[{:node=>:number,
|
122
|
+
:pos=>24,
|
123
|
+
:raw=>"1",
|
124
|
+
:repr=>"1",
|
125
|
+
:type=>:integer,
|
126
|
+
:value=>1}],
|
127
|
+
:tokens=>
|
128
|
+
[{:node=>:function, :pos=>18, :raw=>"alert(", :value=>"alert"},
|
129
|
+
{:node=>:number,
|
130
|
+
:pos=>24,
|
131
|
+
:raw=>"1",
|
132
|
+
:repr=>"1",
|
133
|
+
:type=>:integer,
|
134
|
+
:value=>1},
|
135
|
+
{:node=>:")", :pos=>25, :raw=>")"}]}],
|
136
|
+
:tokens=>
|
137
|
+
[{:node=>:function, :pos=>7, :raw=>"expression(", :value=>"expression"},
|
138
|
+
{:node=>:function, :pos=>18, :raw=>"alert(", :value=>"alert"},
|
139
|
+
{:node=>:number,
|
140
|
+
:pos=>24,
|
141
|
+
:raw=>"1",
|
142
|
+
:repr=>"1",
|
143
|
+
:type=>:integer,
|
144
|
+
:value=>1},
|
145
|
+
{:node=>:")", :pos=>25, :raw=>")"},
|
146
|
+
{:node=>:")", :pos=>26, :raw=>")"}]}],
|
147
|
+
:important=>false,
|
148
|
+
:tokens=>
|
149
|
+
[{:node=>:ident, :pos=>0, :raw=>"width", :value=>"width"},
|
150
|
+
{:node=>:colon, :pos=>5, :raw=>":"},
|
151
|
+
{:node=>:whitespace, :pos=>6, :raw=>" "},
|
152
|
+
{:node=>:function,
|
153
|
+
:name=>"expression",
|
154
|
+
:value=>
|
155
|
+
[{:node=>:function,
|
156
|
+
:name=>"alert",
|
157
|
+
:value=>
|
158
|
+
[{:node=>:number,
|
159
|
+
:pos=>24,
|
160
|
+
:raw=>"1",
|
161
|
+
:repr=>"1",
|
162
|
+
:type=>:integer,
|
163
|
+
:value=>1}],
|
164
|
+
:tokens=>
|
165
|
+
[{:node=>:function, :pos=>18, :raw=>"alert(", :value=>"alert"},
|
166
|
+
{:node=>:number,
|
167
|
+
:pos=>24,
|
168
|
+
:raw=>"1",
|
169
|
+
:repr=>"1",
|
170
|
+
:type=>:integer,
|
171
|
+
:value=>1},
|
172
|
+
{:node=>:")", :pos=>25, :raw=>")"}]}],
|
173
|
+
:tokens=>
|
174
|
+
[{:node=>:function, :pos=>7, :raw=>"expression(", :value=>"expression"},
|
175
|
+
{:node=>:function, :pos=>18, :raw=>"alert(", :value=>"alert"},
|
176
|
+
{:node=>:number,
|
177
|
+
:pos=>24,
|
178
|
+
:raw=>"1",
|
179
|
+
:repr=>"1",
|
180
|
+
:type=>:integer,
|
181
|
+
:value=>1},
|
182
|
+
{:node=>:")", :pos=>25, :raw=>")"},
|
183
|
+
{:node=>:")", :pos=>26, :raw=>")"}]}]},
|
184
|
+
{:node=>:semicolon, :pos=>27, :raw=>";"}
|
185
|
+
], tree)
|
261
186
|
end
|
262
187
|
|
263
188
|
it 'should not choke on a missing property value' do
|