crass 0.2.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -0
  3. data/HISTORY.md +22 -1
  4. data/LICENSE +1 -1
  5. data/README.md +64 -72
  6. data/Rakefile +4 -0
  7. data/crass.gemspec +2 -2
  8. data/lib/crass.rb +1 -1
  9. data/lib/crass/parser.rb +231 -96
  10. data/lib/crass/scanner.rb +21 -21
  11. data/lib/crass/token-scanner.rb +8 -1
  12. data/lib/crass/tokenizer.rb +133 -131
  13. data/lib/crass/version.rb +1 -1
  14. data/test/css-parsing-tests/An+B.json +156 -0
  15. data/test/css-parsing-tests/LICENSE +8 -0
  16. data/test/css-parsing-tests/README.rst +301 -0
  17. data/test/css-parsing-tests/color3.json +142 -0
  18. data/test/css-parsing-tests/color3_hsl.json +3890 -0
  19. data/test/css-parsing-tests/color3_keywords.json +803 -0
  20. data/test/css-parsing-tests/component_value_list.json +432 -0
  21. data/test/css-parsing-tests/declaration_list.json +44 -0
  22. data/test/css-parsing-tests/make_color3_hsl.py +17 -0
  23. data/test/css-parsing-tests/make_color3_keywords.py +191 -0
  24. data/test/css-parsing-tests/one_component_value.json +27 -0
  25. data/test/css-parsing-tests/one_declaration.json +46 -0
  26. data/test/css-parsing-tests/one_rule.json +36 -0
  27. data/test/css-parsing-tests/rule_list.json +48 -0
  28. data/test/css-parsing-tests/stylesheet.json +44 -0
  29. data/test/css-parsing-tests/stylesheet_bytes.json +146 -0
  30. data/test/shared/parse_rules.rb +377 -434
  31. data/test/support/common.rb +124 -0
  32. data/test/support/serialization/animate.css +3158 -0
  33. data/test/support/serialization/html5-boilerplate.css +268 -0
  34. data/test/support/serialization/misc.css +9 -0
  35. data/test/test_css_parsing_tests.rb +150 -0
  36. data/test/test_parse_properties.rb +136 -211
  37. data/test/test_parse_rules.rb +0 -52
  38. data/test/test_parse_stylesheet.rb +0 -39
  39. data/test/test_serialization.rb +13 -4
  40. metadata +44 -7
  41. 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,9 @@
1
+ /* mangled !important */
2
+ .foo {
3
+ display: none /**/ ! IMPORTANT /* */ ;
4
+ }
5
+
6
+ /* An+B */
7
+ li:nth-child(even of li:not(.filtered)) {
8
+ background-color: black;
9
+ }
@@ -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=>:ident, :pos=>21, :raw=>"b", :value=>"b"}
91
- ], prop[:children])
92
-
93
- assert_tokens(" ", tree[3], 23)
94
-
95
- rule = tree[4]
96
- assert_equal(:at_rule, rule[:node])
97
- assert_equal("import", rule[:name])
98
- assert_tokens(" 'bar.css'", rule[:prelude], 31)
99
- assert_tokens("@import 'bar.css'", rule[:tokens], 24)
100
- end
101
-
102
- it 'should not be fazed by extra semicolons or unclosed blocks' do
103
- tree = parse("@media screen { div{;}} a:b;; @media print{div{")
104
- assert_equal(6, tree.size)
105
-
106
- rule = tree[0]
107
- assert_equal(:at_rule, rule[:node])
108
- assert_equal("media", rule[:name])
109
- assert_tokens(" screen ", rule[:prelude], 6)
110
- assert_tokens("@media screen { div{;}}", rule[:tokens])
111
-
112
- block = rule[:block]
113
- assert_equal(:simple_block, block[:node])
114
- assert_equal("{", block[:start])
115
- assert_equal("}", block[:end])
116
- assert_tokens("{ div{;}}", block[:tokens], 14)
117
-
118
- value = block[:value]
119
- assert_equal(3, value.size)
120
- assert_tokens(" div", value[0..1], 15)
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
- :name=>"content",
203
- :value=>"attr(data-foo) \" \"",
204
- :important=>false,
205
- :children=>
206
- [{:node=>:whitespace, :pos=>8, :raw=>" "},
207
- {:node=>:function, :pos=>9, :raw=>"attr(", :value=>"attr"},
208
- {:node=>:ident, :pos=>14, :raw=>"data-foo", :value=>"data-foo"},
209
- {:node=>:")", :pos=>22, :raw=>")"},
210
- {:node=>:whitespace, :pos=>23, :raw=>" "},
211
- {:node=>:string, :pos=>24, :raw=>"\" \"", :value=>" "}],
212
- :tokens=>
213
- [{:node=>:ident, :pos=>0, :raw=>"content", :value=>"content"},
214
- {:node=>:colon, :pos=>7, :raw=>":"},
215
- {:node=>:whitespace, :pos=>8, :raw=>" "},
216
- {:node=>:function, :pos=>9, :raw=>"attr(", :value=>"attr"},
217
- {:node=>:ident, :pos=>14, :raw=>"data-foo", :value=>"data-foo"},
218
- {:node=>:")", :pos=>22, :raw=>")"},
219
- {:node=>:whitespace, :pos=>23, :raw=>" "},
220
- {:node=>:string, :pos=>24, :raw=>"\" \"", :value=>" "},
221
- {:node=>:semicolon, :pos=>27, :raw=>";"}]}
222
- ], tree)
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
- :name=>"width",
231
- :value=>"expression(alert(1))",
232
- :important=>false,
233
- :children=>
234
- [{:node=>:whitespace, :pos=>6, :raw=>" "},
235
- {:node=>:function, :pos=>7, :raw=>"expression(", :value=>"expression"},
236
- {:node=>:function, :pos=>18, :raw=>"alert(", :value=>"alert"},
237
- {:node=>:number,
238
- :pos=>24,
239
- :raw=>"1",
240
- :repr=>"1",
241
- :type=>:integer,
242
- :value=>1},
243
- {:node=>:")", :pos=>25, :raw=>")"},
244
- {:node=>:")", :pos=>26, :raw=>")"}],
245
- :tokens=>
246
- [{:node=>:ident, :pos=>0, :raw=>"width", :value=>"width"},
247
- {:node=>:colon, :pos=>5, :raw=>":"},
248
- {:node=>:whitespace, :pos=>6, :raw=>" "},
249
- {:node=>:function, :pos=>7, :raw=>"expression(", :value=>"expression"},
250
- {:node=>:function, :pos=>18, :raw=>"alert(", :value=>"alert"},
251
- {:node=>:number,
252
- :pos=>24,
253
- :raw=>"1",
254
- :repr=>"1",
255
- :type=>:integer,
256
- :value=>1},
257
- {:node=>:")", :pos=>25, :raw=>")"},
258
- {:node=>:")", :pos=>26, :raw=>")"},
259
- {:node=>:semicolon, :pos=>27, :raw=>";"}]}
260
- ], tree)
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