crass 0.0.2 → 0.1.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 +8 -8
- data/.gitignore +1 -0
- data/HISTORY.md +28 -1
- data/README.md +30 -9
- data/crass.gemspec +2 -2
- data/lib/crass.rb +8 -0
- data/lib/crass/parser.rb +125 -60
- data/lib/crass/scanner.rb +32 -34
- data/lib/crass/tokenizer.rb +159 -140
- data/lib/crass/version.rb +1 -1
- data/test/shared/parse_rules.rb +345 -0
- data/test/support/common.rb +20 -0
- data/test/test_crass.rb +17 -2
- data/test/test_parse_properties.rb +175 -0
- data/test/test_parse_rules.rb +69 -0
- data/test/test_parse_stylesheet.rb +27 -351
- data/test/test_serialization.rb +3 -1
- data/test/test_tokenizer.rb +1562 -0
- metadata +12 -5
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OGNiZTc1MDI4ZWMwYmQxZGI4ODE4Yzc1YzNkNTU2MDJjODc1NTI0Mg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NTdlYmJkNTZlYjI3ZjE3Njc2ZDRiMzkxYWZjOTQ0OWE3ZTcxYWRiOQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZjA0NTZkOTAwYTYwZmNjMDgyMzE1N2NkZDQ5ZTlkNDg0NzZmYTk4OGNiMjli
|
10
|
+
MDdlNzQwNTAwZWZmMjkzY2U3N2NkMjEyOWU1ZDMyZWZlYjU1NGRjNzg1Y2Ez
|
11
|
+
ZWZiYjg5ZTk2YmFhNWQ4NDk1ZDEzNmUwYjg2NDE0NjkyNzFmMjY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NTE5NzI2MDViOWU5YzE4NzFmOWNlZmU0MWM5NTJhNGIyZTMyMTBkOGVjNWQ5
|
14
|
+
MmQ5NDZhYWY5YjIzMTgyNzNjNzZmY2I1NDE3YzFmMzM2Njk1NGQzMzQ3OTkw
|
15
|
+
MjFmODllYmM5NDQ5MTk3YjAwN2I3M2NmNzU4NDZkYzQwYzE1NWE=
|
data/.gitignore
CHANGED
data/HISTORY.md
CHANGED
@@ -1,6 +1,34 @@
|
|
1
1
|
Crass Change History
|
2
2
|
====================
|
3
3
|
|
4
|
+
0.1.0 (2013-10-04)
|
5
|
+
------------------
|
6
|
+
|
7
|
+
* Tokenization is a little over 50% faster.
|
8
|
+
|
9
|
+
* Added tons of unit tests.
|
10
|
+
|
11
|
+
* Added `Crass.parse_properties` and `Crass::Parser.parse_properties`, which can
|
12
|
+
be used to parse the contents of an HTML element's `style` attribute.
|
13
|
+
|
14
|
+
* Added `Crass::Parser.parse_rules`, which can be used to parse the contents of
|
15
|
+
an `:at_rule` block like `@media` that may contain style rules.
|
16
|
+
|
17
|
+
* Fixed: `Crass::Parser#consume_at_rule` and `#consume_qualified_rule` didn't
|
18
|
+
properly handle already-parsed `:simple_block` nodes in the input, which
|
19
|
+
occurs when parsing rules in the value of an `:at_rule` block.
|
20
|
+
|
21
|
+
* Fixed: On `:property` nodes, `:important` is now set to `true` when the
|
22
|
+
property is followed by an "!important" declaration.
|
23
|
+
|
24
|
+
* Fixed: "!important" is no longer included in the value of a `:property` node.
|
25
|
+
|
26
|
+
* Fixed: A variety of tokenization bugs uncovered by tests.
|
27
|
+
|
28
|
+
* Fixed: Added a workaround for a possible spec bug when an `:at_keyword` is
|
29
|
+
encountered while consuming declarations.
|
30
|
+
|
31
|
+
|
4
32
|
0.0.2 (2013-09-30)
|
5
33
|
------------------
|
6
34
|
|
@@ -11,4 +39,3 @@ Crass Change History
|
|
11
39
|
------------------
|
12
40
|
|
13
41
|
* Initial release.
|
14
|
-
|
data/README.md
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
Crass
|
2
2
|
=====
|
3
3
|
|
4
|
-
Crass is a Ruby CSS parser based on the [CSS Syntax
|
4
|
+
Crass is a Ruby CSS parser based on the [CSS Syntax Level 3][css] draft
|
5
|
+
specification.
|
5
6
|
|
6
7
|
* [Home](https://github.com/rgrove/crass/)
|
7
8
|
* [API Docs](http://rubydoc.info/github/rgrove/crass/master)
|
8
9
|
|
9
10
|
[](https://travis-ci.org/rgrove/crass?branch=master)
|
11
|
+
[](http://badge.fury.io/rb/crass)
|
10
12
|
|
11
13
|
Features
|
12
14
|
--------
|
13
15
|
|
14
16
|
* Pure Ruby, with no runtime dependencies other than Ruby 1.9.x or higher.
|
15
17
|
|
16
|
-
* Tokenizes and parses CSS according to the rules defined in the
|
17
|
-
[CSS Syntax
|
18
|
+
* Tokenizes and parses CSS according to the rules defined in the 2013 draft of
|
19
|
+
the [CSS Syntax Level 3][css] specification.
|
18
20
|
|
19
21
|
* Extremely tolerant of broken or invalid CSS. If a browser can handle it, Crass
|
20
22
|
should be able to handle it too.
|
@@ -32,7 +34,9 @@ Features
|
|
32
34
|
Problems
|
33
35
|
--------
|
34
36
|
|
35
|
-
*
|
37
|
+
* Crass isn't terribly fast. I mean, it's Ruby, and it's not really slow by Ruby
|
38
|
+
standards. But compared to the CSS parser in your average browser? Yeah, it's
|
39
|
+
slow.
|
36
40
|
|
37
41
|
* Crass only parses the CSS syntax; it doesn't understand what any of it means,
|
38
42
|
doesn't coalesce selectors, etc. You can do this yourself by consuming the
|
@@ -43,9 +47,10 @@ Problems
|
|
43
47
|
(except for wholesale removal of nodes) are not reflected in the serialized
|
44
48
|
output.
|
45
49
|
|
46
|
-
*
|
50
|
+
* At the moment, Crass only supports UTF-8 input and doesn't respect `@charset`
|
51
|
+
rules. Input in any other encoding will be converted to UTF-8.
|
47
52
|
|
48
|
-
* Probably
|
53
|
+
* Probably other things. Did I mention Crass is pretty new?
|
49
54
|
|
50
55
|
Installing
|
51
56
|
----------
|
@@ -54,9 +59,6 @@ Installing
|
|
54
59
|
gem install crass
|
55
60
|
```
|
56
61
|
|
57
|
-
...but only if you're brave. Seriously, this thing will almost certainly kill
|
58
|
-
your family and poop on your pets.
|
59
|
-
|
60
62
|
Examples
|
61
63
|
--------
|
62
64
|
|
@@ -95,6 +97,7 @@ This returns a big fat ugly parse tree, which looks like this:
|
|
95
97
|
{:node=>:property,
|
96
98
|
:name=>"color",
|
97
99
|
:value=>"#0d8bfa",
|
100
|
+
:important=>false,
|
98
101
|
:tokens=>
|
99
102
|
[{:node=>:ident, :pos=>27, :raw=>"color", :value=>"color"},
|
100
103
|
{:node=>:colon, :pos=>32, :raw=>":"},
|
@@ -109,6 +112,7 @@ This returns a big fat ugly parse tree, which looks like this:
|
|
109
112
|
{:node=>:property,
|
110
113
|
:name=>"text-decoration",
|
111
114
|
:value=>"underline",
|
115
|
+
:important=>false,
|
112
116
|
:tokens=>
|
113
117
|
[{:node=>:ident,
|
114
118
|
:pos=>45,
|
@@ -168,6 +172,23 @@ hate to have to turn down a pull request you spent a lot of time on.
|
|
168
172
|
|
169
173
|
[issue]: https://github.com/rgrove/crass/issues/new
|
170
174
|
|
175
|
+
Acknowledgments
|
176
|
+
---------------
|
177
|
+
|
178
|
+
I'm deeply, deeply grateful to [Simon Sapin][simon] for his wonderfully
|
179
|
+
comprehensive [CSS parsing tests][css-tests], which I adapted to create many of
|
180
|
+
Crass's tests. They've been invaluable in helping me fix bugs and handle weird
|
181
|
+
edge cases, and Crass would be much crappier without them.
|
182
|
+
|
183
|
+
I'm also grateful to [Tab Atkins Jr.][tab] and Simon Sapin (again!) for their
|
184
|
+
work on the [CSS Syntax Level 3][spec] specification, which defines the
|
185
|
+
tokenizing and parsing rules that Crass implements.
|
186
|
+
|
187
|
+
[css-tests]:https://github.com/SimonSapin/css-parsing-tests/
|
188
|
+
[simon]:http://exyr.org/about/
|
189
|
+
[spec]:http://www.w3.org/TR/css-syntax-3/
|
190
|
+
[tab]:http://www.xanthir.com/contact/
|
191
|
+
|
171
192
|
License
|
172
193
|
-------
|
173
194
|
|
data/crass.gemspec
CHANGED
@@ -3,8 +3,8 @@ require './lib/crass/version'
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = 'crass'
|
6
|
-
s.summary = 'CSS parser based on the CSS Syntax
|
7
|
-
s.description = 'Crass is a pure Ruby CSS parser based on the CSS Syntax
|
6
|
+
s.summary = 'CSS parser based on the CSS Syntax Level 3 draft.'
|
7
|
+
s.description = 'Crass is a pure Ruby CSS parser based on the CSS Syntax Level 3 draft.'
|
8
8
|
s.version = Crass::VERSION
|
9
9
|
s.authors = ['Ryan Grove']
|
10
10
|
s.email = ['ryan@wonko.com']
|
data/lib/crass.rb
CHANGED
@@ -11,4 +11,12 @@ module Crass
|
|
11
11
|
Parser.parse_stylesheet(input, options)
|
12
12
|
end
|
13
13
|
|
14
|
+
# Parses _input_ as a string of CSS properties (such as the contents of an
|
15
|
+
# HTML element's `style` attribute) and returns a parse tree.
|
16
|
+
#
|
17
|
+
# See {Tokenizer#initialize} for _options_.
|
18
|
+
def self.parse_properties(input, options = {})
|
19
|
+
Parser.parse_properties(input, options)
|
20
|
+
end
|
21
|
+
|
14
22
|
end
|
data/lib/crass/parser.rb
CHANGED
@@ -16,6 +16,36 @@ module Crass
|
|
16
16
|
|
17
17
|
# -- Class Methods ---------------------------------------------------------
|
18
18
|
|
19
|
+
# Parses CSS properties (such as the contents of an HTML element's `style`
|
20
|
+
# attribute) and returns a parse tree.
|
21
|
+
#
|
22
|
+
# See {Tokenizer#initialize} for _options_.
|
23
|
+
#
|
24
|
+
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#parse-a-list-of-declarations
|
25
|
+
def self.parse_properties(input, options = {})
|
26
|
+
Parser.new(input, options).parse_properties
|
27
|
+
end
|
28
|
+
|
29
|
+
# Parses a CSS rules (such as the content of a `@media` block) and returns a
|
30
|
+
# parse tree. The only difference from {#parse_stylesheet} is that CDO/CDC
|
31
|
+
# nodes (`<!--` and `-->`) aren't ignored.
|
32
|
+
#
|
33
|
+
# See {Tokenizer#initialize} for _options_.
|
34
|
+
#
|
35
|
+
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#parse-a-list-of-rules
|
36
|
+
def self.parse_rules(input, options = {})
|
37
|
+
parser = Parser.new(input, options)
|
38
|
+
rules = parser.consume_rules
|
39
|
+
|
40
|
+
rules.map do |rule|
|
41
|
+
if rule[:node] == :qualified_rule
|
42
|
+
parser.create_style_rule(rule)
|
43
|
+
else
|
44
|
+
rule
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
19
49
|
# Parses a CSS stylesheet and returns a parse tree.
|
20
50
|
#
|
21
51
|
# See {Tokenizer#initialize} for _options_.
|
@@ -26,10 +56,10 @@ module Crass
|
|
26
56
|
rules = parser.consume_rules(:top_level => true)
|
27
57
|
|
28
58
|
rules.map do |rule|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
59
|
+
if rule[:node] == :qualified_rule
|
60
|
+
parser.create_style_rule(rule)
|
61
|
+
else
|
62
|
+
rule
|
33
63
|
end
|
34
64
|
end
|
35
65
|
end
|
@@ -46,20 +76,32 @@ module Crass
|
|
46
76
|
string = ''
|
47
77
|
|
48
78
|
nodes.each do |node|
|
79
|
+
next if node.nil?
|
80
|
+
|
49
81
|
case node[:node]
|
82
|
+
when :at_rule
|
83
|
+
string << node[:tokens].first[:raw]
|
84
|
+
string << self.stringify(node[:prelude], options)
|
85
|
+
|
86
|
+
if node[:block]
|
87
|
+
string << self.stringify(node[:block], options)
|
88
|
+
end
|
89
|
+
|
50
90
|
when :comment
|
51
91
|
string << node[:raw] unless options[:exclude_comments]
|
52
92
|
|
53
|
-
when :style_rule
|
54
|
-
string << self.stringify(node[:selector][:tokens], options)
|
55
|
-
string << "{"
|
56
|
-
string << self.stringify(node[:children], options)
|
57
|
-
string << "}"
|
58
|
-
|
59
93
|
when :property
|
60
|
-
string << options[:indent] if options[:indent]
|
61
94
|
string << self.stringify(node[:tokens], options)
|
62
95
|
|
96
|
+
when :simple_block
|
97
|
+
string << node[:start]
|
98
|
+
string << self.stringify(node[:value], options)
|
99
|
+
string << node[:end]
|
100
|
+
|
101
|
+
when :style_rule
|
102
|
+
string << self.stringify(node[:selector][:tokens], options)
|
103
|
+
string << "{#{self.stringify(node[:children], options)}}"
|
104
|
+
|
63
105
|
else
|
64
106
|
if node.key?(:raw)
|
65
107
|
string << node[:raw]
|
@@ -74,7 +116,7 @@ module Crass
|
|
74
116
|
|
75
117
|
# -- Instance Methods ------------------------------------------------------
|
76
118
|
|
77
|
-
#
|
119
|
+
# {TokenScanner} wrapping the tokens generated from this parser's input.
|
78
120
|
attr_reader :tokens
|
79
121
|
|
80
122
|
# Initializes a parser based on the given _input_, which may be a CSS string
|
@@ -96,7 +138,7 @@ module Crass
|
|
96
138
|
rule = {}
|
97
139
|
|
98
140
|
rule[:tokens] = input.collect do
|
99
|
-
rule[:name] =
|
141
|
+
rule[:name] = input.consume[:value]
|
100
142
|
rule[:prelude] = []
|
101
143
|
|
102
144
|
while token = input.consume
|
@@ -108,12 +150,14 @@ module Crass
|
|
108
150
|
rule[:block] = consume_simple_block(input)
|
109
151
|
break
|
110
152
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
153
|
+
when :simple_block
|
154
|
+
if token[:start] == '{'
|
155
|
+
rule[:block] = token
|
156
|
+
break
|
157
|
+
else
|
158
|
+
input.reconsume
|
159
|
+
rule[:prelude] << consume_component_value(input)
|
160
|
+
end
|
117
161
|
|
118
162
|
else
|
119
163
|
input.reconsume
|
@@ -140,34 +184,48 @@ module Crass
|
|
140
184
|
|
141
185
|
# Consumes a declaration and returns it, or `nil` on parse error.
|
142
186
|
#
|
143
|
-
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#consume-a-
|
187
|
+
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#consume-a-declaration
|
144
188
|
def consume_declaration(input = @tokens)
|
145
189
|
declaration = {}
|
190
|
+
value = []
|
146
191
|
|
147
192
|
declaration[:tokens] = input.collect do
|
148
193
|
declaration[:name] = input.consume[:value]
|
149
194
|
|
150
|
-
value = []
|
151
195
|
token = input.consume
|
152
|
-
token = input.consume while token[:node] == :whitespace
|
153
|
-
|
154
|
-
return nil if token[:node] != :colon # TODO: parse error
|
196
|
+
token = input.consume while token && token[:node] == :whitespace
|
155
197
|
|
198
|
+
return nil if !token || token[:node] != :colon # TODO: parse error
|
156
199
|
value << token while token = input.consume
|
157
|
-
|
200
|
+
end
|
158
201
|
|
159
|
-
|
202
|
+
# Look for !important.
|
203
|
+
pos = -1
|
204
|
+
while token = value[pos]
|
205
|
+
type = token[:node]
|
206
|
+
|
207
|
+
if type == :whitespace || type == :comment || type == :semicolon
|
208
|
+
pos -= 1
|
209
|
+
next
|
210
|
+
end
|
160
211
|
|
161
|
-
if
|
162
|
-
|
163
|
-
maybe_important[0][:value] == '!' &&
|
164
|
-
maybe_important[1][:node] == :ident &&
|
165
|
-
maybe_important[1][:value].downcase == 'important'
|
212
|
+
if type == :ident && token[:value].downcase == 'important'
|
213
|
+
prev_token = value[pos - 1]
|
166
214
|
|
167
|
-
|
215
|
+
if prev_token && prev_token[:node] == :delim &&
|
216
|
+
prev_token[:value] == '!'
|
217
|
+
|
218
|
+
declaration[:important] = true
|
219
|
+
value.slice!(pos - 1, 2)
|
220
|
+
else
|
221
|
+
break
|
222
|
+
end
|
223
|
+
else
|
224
|
+
break
|
168
225
|
end
|
169
226
|
end
|
170
227
|
|
228
|
+
declaration[:value] = value
|
171
229
|
create_node(:declaration, declaration)
|
172
230
|
end
|
173
231
|
|
@@ -176,7 +234,7 @@ module Crass
|
|
176
234
|
# NOTE: The returned list may include `:comment`, `:semicolon`, and
|
177
235
|
# `:whitespace` nodes, which is non-standard.
|
178
236
|
#
|
179
|
-
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#consume-a-list-of-
|
237
|
+
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#consume-a-list-of-declarations
|
180
238
|
def consume_declarations(input = @tokens)
|
181
239
|
declarations = []
|
182
240
|
|
@@ -189,8 +247,13 @@ module Crass
|
|
189
247
|
# TODO: this is technically a parse error when parsing a style rule,
|
190
248
|
# but not necessarily at other times.
|
191
249
|
|
192
|
-
#
|
193
|
-
# since
|
250
|
+
# Note: The spec doesn't say we should reconsume here, but it's
|
251
|
+
# necessary since `consume_at_rule` must consume the `:at_keyword` as
|
252
|
+
# the rule's name or it'll end up in the prelude. The spec *does* say
|
253
|
+
# we should reconsume when an `:at_keyword` is encountered in
|
254
|
+
# `consume_rules`, so we either have to reconsume in both places or in
|
255
|
+
# neither place. I've chosen to reconsume in both places.
|
256
|
+
input.reconsume
|
194
257
|
declarations << consume_at_rule(input)
|
195
258
|
|
196
259
|
when :ident
|
@@ -247,7 +310,7 @@ module Crass
|
|
247
310
|
# Consumes a qualified rule and returns it, or `nil` if a parse error
|
248
311
|
# occurs.
|
249
312
|
#
|
250
|
-
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#consume-a-qualified-
|
313
|
+
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#consume-a-qualified-rule
|
251
314
|
def consume_qualified_rule(input = @tokens)
|
252
315
|
rule = {:prelude => []}
|
253
316
|
|
@@ -258,15 +321,9 @@ module Crass
|
|
258
321
|
if token[:node] == :'{'
|
259
322
|
rule[:block] = consume_simple_block(input)
|
260
323
|
break
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
# TODO: At this point, the spec says we should check for a "simple block
|
265
|
-
# with an associated token of <<{-token>>", but isn't that exactly what
|
266
|
-
# we just did above? And the tokenizer only ever produces standalone
|
267
|
-
# <<{-token>>s, so how could the token stream ever contain one that's
|
268
|
-
# already associated with a simple block? What am I missing?
|
269
|
-
|
324
|
+
elsif token[:node] == :simple_block
|
325
|
+
rule[:block] = token
|
326
|
+
break
|
270
327
|
else
|
271
328
|
input.reconsume
|
272
329
|
rule[:prelude] << consume_component_value(input)
|
@@ -279,7 +336,7 @@ module Crass
|
|
279
336
|
|
280
337
|
# Consumes a list of rules and returns them.
|
281
338
|
#
|
282
|
-
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#consume-a-list-of-
|
339
|
+
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#consume-a-list-of-rules
|
283
340
|
def consume_rules(flags = {})
|
284
341
|
rules = []
|
285
342
|
|
@@ -358,33 +415,41 @@ module Crass
|
|
358
415
|
# * http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#style-rules
|
359
416
|
# * http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#consume-a-list-of-declarations0
|
360
417
|
def create_style_rule(rule)
|
361
|
-
|
362
|
-
|
418
|
+
create_node(:style_rule,
|
419
|
+
:selector => create_selector(rule[:prelude]),
|
420
|
+
:children => parse_properties(rule[:block][:value]))
|
421
|
+
end
|
363
422
|
|
364
|
-
|
423
|
+
# Parses a list of declarations and returns an array of `:property` nodes
|
424
|
+
# (and any non-declaration nodes that were in the input). This is useful for
|
425
|
+
# parsing the contents of an HTML element's `style` attribute.
|
426
|
+
#
|
427
|
+
# http://www.w3.org/TR/2013/WD-css-syntax-3-20130919/#parse-a-list-of-declarations
|
428
|
+
def parse_properties(input = @tokens)
|
429
|
+
input = TokenScanner.new(input) unless input.is_a?(TokenScanner)
|
430
|
+
properties = []
|
431
|
+
|
432
|
+
consume_declarations(input).each do |decl|
|
365
433
|
unless decl[:node] == :declaration
|
366
|
-
|
434
|
+
properties << decl
|
367
435
|
next
|
368
436
|
end
|
369
437
|
|
370
|
-
|
371
|
-
:name
|
372
|
-
:value
|
373
|
-
:
|
438
|
+
properties << create_node(:property,
|
439
|
+
:name => decl[:name],
|
440
|
+
:value => parse_value(decl[:value]),
|
441
|
+
:important => decl[:important] == true,
|
442
|
+
:tokens => decl[:tokens])
|
374
443
|
end
|
375
444
|
|
376
|
-
|
377
|
-
:selector => create_selector(rule[:prelude]),
|
378
|
-
:children => children
|
379
|
-
)
|
445
|
+
properties
|
380
446
|
end
|
381
447
|
|
382
448
|
# Returns the unescaped value of a selector name or property declaration.
|
383
449
|
def parse_value(nodes)
|
450
|
+
nodes = [nodes] unless nodes.is_a?(Array)
|
384
451
|
string = ''
|
385
452
|
|
386
|
-
nodes = [nodes] unless nodes.is_a?(Array)
|
387
|
-
|
388
453
|
nodes.each do |node|
|
389
454
|
case node[:node]
|
390
455
|
when :comment, :semicolon then next
|