csspool 3.0.2 → 4.0.0.pre
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.
- data/Gemfile.lock +4 -4
- data/Manifest.txt +13 -0
- data/README.rdoc +1 -1
- data/lib/csspool/css/document.rb +6 -0
- data/lib/csspool/css/document_handler.rb +41 -7
- data/lib/csspool/css/document_query.rb +14 -0
- data/lib/csspool/css/import_rule.rb +8 -4
- data/lib/csspool/css/keyframes_block.rb +14 -0
- data/lib/csspool/css/keyframes_rule.rb +14 -0
- data/lib/csspool/css/media.rb +5 -3
- data/lib/csspool/css/namespace_rule.rb +13 -0
- data/lib/csspool/css/parser.rb +970 -425
- data/lib/csspool/css/parser.y +227 -30
- data/lib/csspool/css/tokenizer.rb +31 -7
- data/lib/csspool/css/tokenizer.rex +36 -7
- data/lib/csspool/css.rb +4 -0
- data/lib/csspool/selector.rb +5 -1
- data/lib/csspool/selectors/attribute.rb +6 -1
- data/lib/csspool/selectors/pseudo.rb +17 -0
- data/lib/csspool/selectors/pseudo_element.rb +13 -0
- data/lib/csspool/selectors/simple.rb +0 -4
- data/lib/csspool/selectors/type.rb +7 -0
- data/lib/csspool/selectors/universal.rb +7 -0
- data/lib/csspool/selectors.rb +1 -1
- data/lib/csspool/terms/math.rb +18 -0
- data/lib/csspool/terms.rb +1 -0
- data/lib/csspool/visitors/children.rb +1 -1
- data/lib/csspool/visitors/comparable.rb +8 -2
- data/lib/csspool/visitors/iterator.rb +1 -1
- data/lib/csspool/visitors/to_css.rb +41 -21
- data/lib/csspool.rb +1 -1
- data/test/_local_helper.rb +2 -0
- data/test/css/test_document_query.rb +76 -0
- data/test/css/test_import_rule.rb +1 -1
- data/test/css/test_keyframes_rule.rb +93 -0
- data/test/css/test_namespace_rule.rb +54 -0
- data/test/css/test_parser.rb +46 -1
- data/test/css/test_tokenizer.rb +0 -45
- data/test/helper.rb +6 -1
- data/test/sac/test_parser.rb +16 -3
- data/test/test_declaration.rb +39 -0
- data/test/test_parser.rb +26 -13
- data/test/test_selector.rb +205 -5
- data/test/test_term.rb +23 -0
- data/test/visitors/test_to_css.rb +34 -23
- metadata +136 -118
data/lib/csspool/css/parser.y
CHANGED
@@ -3,7 +3,15 @@ class CSSPool::CSS::Parser
|
|
3
3
|
token CHARSET_SYM IMPORT_SYM STRING SEMI IDENT S COMMA LBRACE RBRACE STAR HASH
|
4
4
|
token LSQUARE RSQUARE EQUAL INCLUDES DASHMATCH RPAREN FUNCTION GREATER PLUS
|
5
5
|
token SLASH NUMBER MINUS LENGTH PERCENTAGE EMS EXS ANGLE TIME FREQ URI
|
6
|
+
token IMPORTANT_SYM MEDIA_SYM NTH_PSEUDO_CLASS
|
7
|
+
token IMPORTANT_SYM MEDIA_SYM DOCUMENT_QUERY_SYM FUNCTION_NO_QUOTE
|
6
8
|
token IMPORTANT_SYM MEDIA_SYM
|
9
|
+
token NAMESPACE_SYM TILDE
|
10
|
+
token NAMESPACE_SYM PREFIXMATCH SUFFIXMATCH SUBSTRINGMATCH
|
11
|
+
token NAMESPACE_SYM NOT_PSEUDO_CLASS
|
12
|
+
token NAMESPACE_SYM KEYFRAMES_SYM
|
13
|
+
token NAMESPACE_SYM MATCHES_PSEUDO_CLASS
|
14
|
+
token NAMESPACE_SYM MATH
|
7
15
|
|
8
16
|
rule
|
9
17
|
document
|
@@ -14,8 +22,10 @@ rule
|
|
14
22
|
stylesheet
|
15
23
|
: charset stylesheet
|
16
24
|
| import stylesheet
|
25
|
+
| namespace stylesheet
|
17
26
|
| charset
|
18
27
|
| import
|
28
|
+
| namespace
|
19
29
|
| body
|
20
30
|
;
|
21
31
|
charset
|
@@ -34,6 +44,14 @@ rule
|
|
34
44
|
| STRING { result = Terms::String.new interpret_string val.first }
|
35
45
|
| URI { result = Terms::URI.new interpret_uri val.first }
|
36
46
|
;
|
47
|
+
namespace
|
48
|
+
: NAMESPACE_SYM ident import_location SEMI {
|
49
|
+
@handler.namespace val[1], val[2]
|
50
|
+
}
|
51
|
+
| NAMESPACE_SYM import_location SEMI {
|
52
|
+
@handler.namespace nil, val[1]
|
53
|
+
}
|
54
|
+
;
|
37
55
|
medium
|
38
56
|
: medium COMMA IDENT {
|
39
57
|
result = [val.first, Terms::Ident.new(interpret_identifier val.last)]
|
@@ -44,9 +62,15 @@ rule
|
|
44
62
|
;
|
45
63
|
body
|
46
64
|
: ruleset body
|
47
|
-
|
|
65
|
+
| conditional_rule body
|
66
|
+
| keyframes_rule body
|
48
67
|
| ruleset
|
49
|
-
|
|
68
|
+
| conditional_rule
|
69
|
+
| keyframes_rule
|
70
|
+
;
|
71
|
+
conditional_rule
|
72
|
+
: media
|
73
|
+
| document_query
|
50
74
|
;
|
51
75
|
media
|
52
76
|
: start_media body RBRACE { @handler.end_media val.first }
|
@@ -58,6 +82,60 @@ rule
|
|
58
82
|
}
|
59
83
|
| MEDIA_SYM LBRACE { result = [] }
|
60
84
|
;
|
85
|
+
document_query
|
86
|
+
: start_document_query body RBRACE { @handler.end_document_query }
|
87
|
+
| start_document_query RBRACE { @handler.end_document_query }
|
88
|
+
;
|
89
|
+
start_document_query
|
90
|
+
: DOCUMENT_QUERY_SYM url_match_fns LBRACE {
|
91
|
+
@handler.start_document_query val[1]
|
92
|
+
}
|
93
|
+
;
|
94
|
+
url_match_fns
|
95
|
+
: url_match_fn COMMA url_match_fns {
|
96
|
+
result = [val[0], val[2]].flatten
|
97
|
+
}
|
98
|
+
| url_match_fn {
|
99
|
+
result = val
|
100
|
+
}
|
101
|
+
;
|
102
|
+
url_match_fn
|
103
|
+
: function_no_quote
|
104
|
+
| function
|
105
|
+
| uri
|
106
|
+
;
|
107
|
+
keyframes_rule
|
108
|
+
: start_keyframes_rule keyframes_blocks RBRACE
|
109
|
+
| start_keyframes_rule RBRACE
|
110
|
+
;
|
111
|
+
start_keyframes_rule
|
112
|
+
: KEYFRAMES_SYM IDENT LBRACE {
|
113
|
+
@handler.start_keyframes_rule val[1]
|
114
|
+
}
|
115
|
+
;
|
116
|
+
keyframes_blocks
|
117
|
+
: keyframes_block keyframes_blocks
|
118
|
+
| keyframes_block
|
119
|
+
;
|
120
|
+
keyframes_block
|
121
|
+
: start_keyframes_block declarations RBRACE { @handler.end_keyframes_block }
|
122
|
+
| start_keyframes_block RBRACE { @handler.end_keyframes_block }
|
123
|
+
;
|
124
|
+
start_keyframes_block
|
125
|
+
: keyframes_selectors LBRACE {
|
126
|
+
@handler.start_keyframes_block val[0]
|
127
|
+
}
|
128
|
+
;
|
129
|
+
keyframes_selectors
|
130
|
+
| keyframes_selector COMMA keyframes_selectors {
|
131
|
+
result = val[0] + ', ' + val[2]
|
132
|
+
}
|
133
|
+
| keyframes_selector
|
134
|
+
;
|
135
|
+
keyframes_selector
|
136
|
+
: IDENT
|
137
|
+
| PERCENTAGE { result = val[0].strip }
|
138
|
+
;
|
61
139
|
ruleset
|
62
140
|
: start_selector declarations RBRACE {
|
63
141
|
@handler.end_selector val.first
|
@@ -97,6 +175,7 @@ rule
|
|
97
175
|
: S { result = :s }
|
98
176
|
| GREATER { result = :> }
|
99
177
|
| PLUS { result = :+ }
|
178
|
+
| TILDE { result = :~ }
|
100
179
|
;
|
101
180
|
simple_selector
|
102
181
|
: element_name hcap {
|
@@ -112,9 +191,22 @@ rule
|
|
112
191
|
result = [ss]
|
113
192
|
}
|
114
193
|
;
|
194
|
+
simple_selectors
|
195
|
+
: simple_selector COMMA simple_selectors { result = [val[0], val[2]].flatten }
|
196
|
+
| simple_selector
|
197
|
+
;
|
198
|
+
ident_with_namespace
|
199
|
+
: IDENT { result = [interpret_identifier(val[0]), nil] }
|
200
|
+
| IDENT '|' IDENT { result = [interpret_identifier(val[2]), interpret_identifier(val[0])] }
|
201
|
+
| '|' IDENT { result = [interpret_identifier(val[1]), nil] }
|
202
|
+
| STAR '|' IDENT { result = [interpret_identifier(val[2]), '*'] }
|
203
|
+
;
|
115
204
|
element_name
|
116
|
-
:
|
117
|
-
| STAR
|
205
|
+
: ident_with_namespace { result = Selectors::Type.new val.first[0], nil, val.first[1] }
|
206
|
+
| STAR { result = Selectors::Universal.new val.first }
|
207
|
+
| '|' STAR { result = Selectors::Universal.new val[1] }
|
208
|
+
| STAR '|' STAR { result = Selectors::Universal.new val[2], nil, val[0] }
|
209
|
+
| IDENT '|' STAR { result = Selectors::Universal.new val[2], nil, interpret_identifier(val[0]) }
|
118
210
|
;
|
119
211
|
hcap
|
120
212
|
: hash { result = val }
|
@@ -136,60 +228,118 @@ rule
|
|
136
228
|
}
|
137
229
|
;
|
138
230
|
attrib
|
139
|
-
: LSQUARE
|
231
|
+
: LSQUARE ident_with_namespace EQUAL IDENT RSQUARE {
|
140
232
|
result = Selectors::Attribute.new(
|
141
|
-
|
233
|
+
val[1][0],
|
142
234
|
interpret_identifier(val[3]),
|
143
|
-
Selectors::Attribute::EQUALS
|
235
|
+
Selectors::Attribute::EQUALS,
|
236
|
+
val[1][1]
|
144
237
|
)
|
145
238
|
}
|
146
|
-
| LSQUARE
|
239
|
+
| LSQUARE ident_with_namespace EQUAL STRING RSQUARE {
|
147
240
|
result = Selectors::Attribute.new(
|
148
|
-
|
241
|
+
val[1][0],
|
149
242
|
interpret_string(val[3]),
|
150
|
-
Selectors::Attribute::EQUALS
|
243
|
+
Selectors::Attribute::EQUALS,
|
244
|
+
val[1][1]
|
151
245
|
)
|
152
246
|
}
|
153
|
-
| LSQUARE
|
247
|
+
| LSQUARE ident_with_namespace INCLUDES STRING RSQUARE {
|
154
248
|
result = Selectors::Attribute.new(
|
155
|
-
|
249
|
+
val[1][0],
|
156
250
|
interpret_string(val[3]),
|
157
|
-
Selectors::Attribute::INCLUDES
|
251
|
+
Selectors::Attribute::INCLUDES,
|
252
|
+
val[1][1]
|
158
253
|
)
|
159
254
|
}
|
160
|
-
| LSQUARE
|
255
|
+
| LSQUARE ident_with_namespace INCLUDES IDENT RSQUARE {
|
161
256
|
result = Selectors::Attribute.new(
|
162
|
-
|
257
|
+
val[1][0],
|
163
258
|
interpret_identifier(val[3]),
|
164
|
-
Selectors::Attribute::INCLUDES
|
259
|
+
Selectors::Attribute::INCLUDES,
|
260
|
+
val[1][1]
|
165
261
|
)
|
166
262
|
}
|
167
|
-
| LSQUARE
|
263
|
+
| LSQUARE ident_with_namespace DASHMATCH IDENT RSQUARE {
|
168
264
|
result = Selectors::Attribute.new(
|
169
|
-
|
265
|
+
val[1][0],
|
170
266
|
interpret_identifier(val[3]),
|
171
|
-
Selectors::Attribute::DASHMATCH
|
267
|
+
Selectors::Attribute::DASHMATCH,
|
268
|
+
val[1][1]
|
172
269
|
)
|
173
270
|
}
|
174
|
-
| LSQUARE
|
271
|
+
| LSQUARE ident_with_namespace DASHMATCH STRING RSQUARE {
|
175
272
|
result = Selectors::Attribute.new(
|
176
|
-
|
273
|
+
val[1][0],
|
177
274
|
interpret_string(val[3]),
|
178
|
-
Selectors::Attribute::DASHMATCH
|
275
|
+
Selectors::Attribute::DASHMATCH,
|
276
|
+
val[1][1]
|
179
277
|
)
|
180
278
|
}
|
181
|
-
| LSQUARE IDENT RSQUARE {
|
279
|
+
| LSQUARE ident_with_namespace PREFIXMATCH IDENT RSQUARE {
|
182
280
|
result = Selectors::Attribute.new(
|
183
|
-
|
281
|
+
val[1][0],
|
282
|
+
interpret_identifier(val[3]),
|
283
|
+
Selectors::Attribute::PREFIXMATCH,
|
284
|
+
val[1][1]
|
285
|
+
)
|
286
|
+
}
|
287
|
+
| LSQUARE ident_with_namespace PREFIXMATCH STRING RSQUARE {
|
288
|
+
result = Selectors::Attribute.new(
|
289
|
+
val[1][0],
|
290
|
+
interpret_string(val[3]),
|
291
|
+
Selectors::Attribute::PREFIXMATCH,
|
292
|
+
val[1][1]
|
293
|
+
)
|
294
|
+
}
|
295
|
+
| LSQUARE ident_with_namespace SUFFIXMATCH IDENT RSQUARE {
|
296
|
+
result = Selectors::Attribute.new(
|
297
|
+
val[1][0],
|
298
|
+
interpret_identifier(val[3]),
|
299
|
+
Selectors::Attribute::SUFFIXMATCH,
|
300
|
+
val[1][1]
|
301
|
+
)
|
302
|
+
}
|
303
|
+
| LSQUARE ident_with_namespace SUFFIXMATCH STRING RSQUARE {
|
304
|
+
result = Selectors::Attribute.new(
|
305
|
+
val[1][0],
|
306
|
+
interpret_string(val[3]),
|
307
|
+
Selectors::Attribute::SUFFIXMATCH,
|
308
|
+
val[1][1]
|
309
|
+
)
|
310
|
+
}
|
311
|
+
| LSQUARE ident_with_namespace SUBSTRINGMATCH IDENT RSQUARE {
|
312
|
+
result = Selectors::Attribute.new(
|
313
|
+
val[1][0],
|
314
|
+
interpret_identifier(val[3]),
|
315
|
+
Selectors::Attribute::SUBSTRINGMATCH,
|
316
|
+
val[1][1]
|
317
|
+
)
|
318
|
+
}
|
319
|
+
| LSQUARE ident_with_namespace SUBSTRINGMATCH STRING RSQUARE {
|
320
|
+
result = Selectors::Attribute.new(
|
321
|
+
val[1][0],
|
322
|
+
interpret_string(val[3]),
|
323
|
+
Selectors::Attribute::SUBSTRINGMATCH,
|
324
|
+
val[1][1]
|
325
|
+
)
|
326
|
+
}
|
327
|
+
| LSQUARE ident_with_namespace RSQUARE {
|
328
|
+
result = Selectors::Attribute.new(
|
329
|
+
val[1][0],
|
184
330
|
nil,
|
185
|
-
Selectors::Attribute::SET
|
331
|
+
Selectors::Attribute::SET,
|
332
|
+
val[1][1]
|
186
333
|
)
|
187
334
|
}
|
188
335
|
;
|
189
336
|
pseudo
|
190
337
|
: ':' IDENT {
|
191
|
-
result = Selectors::
|
192
|
-
|
338
|
+
result = Selectors::pseudo interpret_identifier(val[1])
|
339
|
+
}
|
340
|
+
| ':' ':' IDENT {
|
341
|
+
result = Selectors::PseudoElement.new(
|
342
|
+
interpret_identifier(val[2])
|
193
343
|
)
|
194
344
|
}
|
195
345
|
| ':' FUNCTION RPAREN {
|
@@ -204,11 +354,36 @@ rule
|
|
204
354
|
interpret_identifier(val[2])
|
205
355
|
)
|
206
356
|
}
|
357
|
+
| ':' NOT_PSEUDO_CLASS simple_selector RPAREN {
|
358
|
+
result = Selectors::PseudoClass.new(
|
359
|
+
'not',
|
360
|
+
val[2].first.to_s
|
361
|
+
)
|
362
|
+
}
|
363
|
+
| ':' NTH_PSEUDO_CLASS {
|
364
|
+
result = Selectors::PseudoClass.new(
|
365
|
+
interpret_identifier(val[1].sub(/\(.*/, '')),
|
366
|
+
interpret_identifier(val[1].sub(/.*\(/, '').sub(/\).*/, ''))
|
367
|
+
)
|
368
|
+
}
|
369
|
+
| ':' MATCHES_PSEUDO_CLASS simple_selectors RPAREN {
|
370
|
+
result = Selectors::PseudoClass.new(
|
371
|
+
val[1].split('(').first.strip,
|
372
|
+
val[2].join(', ')
|
373
|
+
)
|
374
|
+
}
|
375
|
+
;
|
376
|
+
# declarations can be separated by one *or more* semicolons. semi-colons at the start or end of a ruleset are also allowed
|
377
|
+
one_or_more_semis
|
378
|
+
: SEMI
|
379
|
+
| SEMI one_or_more_semis
|
207
380
|
;
|
208
381
|
declarations
|
209
|
-
: declaration
|
210
|
-
|
|
382
|
+
: declaration one_or_more_semis declarations
|
383
|
+
| one_or_more_semis declarations
|
384
|
+
| declaration one_or_more_semis
|
211
385
|
| declaration
|
386
|
+
| one_or_more_semis
|
212
387
|
;
|
213
388
|
declaration
|
214
389
|
: property ':' expr prio
|
@@ -247,6 +422,7 @@ rule
|
|
247
422
|
| string
|
248
423
|
| uri
|
249
424
|
| hexcolor
|
425
|
+
| math
|
250
426
|
| function
|
251
427
|
;
|
252
428
|
function
|
@@ -260,6 +436,22 @@ rule
|
|
260
436
|
end
|
261
437
|
}
|
262
438
|
;
|
439
|
+
function_no_quote
|
440
|
+
: function_no_quote S { result = val.first }
|
441
|
+
| FUNCTION_NO_QUOTE {
|
442
|
+
parts = val.first.split('(')
|
443
|
+
name = interpret_identifier parts.first
|
444
|
+
result = Terms::Function.new(name, [Terms::String.new(interpret_string_no_quote(parts.last))])
|
445
|
+
}
|
446
|
+
;
|
447
|
+
math
|
448
|
+
: MATH {
|
449
|
+
parts = val.first.split('(', 2)
|
450
|
+
name = parts[0].strip
|
451
|
+
expression = parts[1][0..parts[1].rindex(')')-1].strip
|
452
|
+
result = Terms::Math.new(name, expression)
|
453
|
+
}
|
454
|
+
;
|
263
455
|
hexcolor
|
264
456
|
: hexcolor S { result = val.first }
|
265
457
|
| HASH { result = Terms::Hash.new val.first.sub(/^#/, '') }
|
@@ -267,6 +459,7 @@ rule
|
|
267
459
|
uri
|
268
460
|
: uri S { result = val.first }
|
269
461
|
| URI { result = Terms::URI.new interpret_uri val.first }
|
462
|
+
;
|
270
463
|
string
|
271
464
|
: string S { result = val.first }
|
272
465
|
| STRING { result = Terms::String.new interpret_string val.first }
|
@@ -326,7 +519,11 @@ def interpret_identifier s
|
|
326
519
|
end
|
327
520
|
|
328
521
|
def interpret_uri s
|
329
|
-
interpret_escapes s.match(/^url\((.*)\)$/
|
522
|
+
interpret_escapes s.match(/^url\((.*)\)$/mui)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
|
523
|
+
end
|
524
|
+
|
525
|
+
def interpret_string_no_quote s
|
526
|
+
interpret_escapes s.match(/^(.*)\)$/mu)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
|
330
527
|
end
|
331
528
|
|
332
529
|
def interpret_string s
|
@@ -58,7 +58,7 @@ class Tokenizer < Parser
|
|
58
58
|
token = case @state
|
59
59
|
when nil
|
60
60
|
case
|
61
|
-
when (text = @ss.scan(/url\([\s]*("([^\n\r\f\\"]
|
61
|
+
when (text = @ss.scan(/url\([\s]*("([^\n\r\f\\"]|\\(\n|\r\n|\r|\f)|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*"|'([^\n\r\f\\']|\\(\n|\r\n|\r|\f)|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*')[\s]*\)/i))
|
62
62
|
action { [:URI, st(text)] }
|
63
63
|
|
64
64
|
when (text = @ss.scan(/url\([\s]*([!#\$%&*-~]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*[\s]*\)/i))
|
@@ -70,6 +70,24 @@ class Tokenizer < Parser
|
|
70
70
|
when (text = @ss.scan(/[\s]*\/\*(.|[\s]*)*?\*\/[\s]*/i))
|
71
71
|
action { next_token }
|
72
72
|
|
73
|
+
when (text = @ss.scan(/not\(\s*/i))
|
74
|
+
action { [:NOT_PSEUDO_CLASS, st(text)] }
|
75
|
+
|
76
|
+
when (text = @ss.scan(/(nth\-child|nth\-last\-child|nth\-of\-type|nth\-last\-of\-type)\([\s]*([\+\-]?[0-9]*n([\s]*[\+\-][\s]*[0-9]+)?|[\+\-]?[0-9]+|odd|even)[\s]*\)/i))
|
77
|
+
action { [:NTH_PSEUDO_CLASS, st(text)] }
|
78
|
+
|
79
|
+
when (text = @ss.scan(/(\-[A-Za-z]+\-)?(matches|any)\(\s*/i))
|
80
|
+
action { [:MATCHES_PSEUDO_CLASS, st(text)] }
|
81
|
+
|
82
|
+
when (text = @ss.scan(/(domain|url\-prefix)\([\s]*("([^\n\r\f\\"]|\\(\n|\r\n|\r|\f)|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*"|'([^\n\r\f\\']|\\(\n|\r\n|\r|\f)|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*')[\s]*\)/i))
|
83
|
+
action { [:FUNCTION_NO_QUOTE, st(text)] }
|
84
|
+
|
85
|
+
when (text = @ss.scan(/(domain|url\-prefix)\([\s]*([!#\$%&*-~]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*[\s]*\)/i))
|
86
|
+
action { [:FUNCTION_NO_QUOTE, st(text)] }
|
87
|
+
|
88
|
+
when (text = @ss.scan(/[\s]*(\-[A-Za-z]+\-)?calc\([\s]*(([0-9]*\.[0-9]+|[0-9]+)|([0-9]*\.[0-9]+|[0-9]+)(px|cm|mm|in|pt|pc)|([0-9]*\.[0-9]+|[0-9]+)%|([0-9]*\.[0-9]+|[0-9]+)em|([0-9]*\.[0-9]+|[0-9]+)ex)([\s]*(\*[\s]*(([0-9]*\.[0-9]+|[0-9]+)|([0-9]*\.[0-9]+|[0-9]+)(px|cm|mm|in|pt|pc)|([0-9]*\.[0-9]+|[0-9]+)%|([0-9]*\.[0-9]+|[0-9]+)em|([0-9]*\.[0-9]+|[0-9]+)ex)|\/[\s]*([0-9]*\.[0-9]+|[0-9]+)))*([\s]+[\+\-][\s]+(([0-9]*\.[0-9]+|[0-9]+)|([0-9]*\.[0-9]+|[0-9]+)(px|cm|mm|in|pt|pc)|([0-9]*\.[0-9]+|[0-9]+)%|([0-9]*\.[0-9]+|[0-9]+)em|([0-9]*\.[0-9]+|[0-9]+)ex)([\s]*(\*[\s]*(([0-9]*\.[0-9]+|[0-9]+)|([0-9]*\.[0-9]+|[0-9]+)(px|cm|mm|in|pt|pc)|([0-9]*\.[0-9]+|[0-9]+)%|([0-9]*\.[0-9]+|[0-9]+)em|([0-9]*\.[0-9]+|[0-9]+)ex)|\/[\s]*([0-9]*\.[0-9]+|[0-9]+)))*)*[\s]*\)[\s]*[\s]*/i))
|
89
|
+
action { [:MATH, st(text)] }
|
90
|
+
|
73
91
|
when (text = @ss.scan(/[-@]?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f]|[.])*\(\s*/i))
|
74
92
|
action { [:FUNCTION, st(text)] }
|
75
93
|
|
@@ -85,6 +103,15 @@ class Tokenizer < Parser
|
|
85
103
|
when (text = @ss.scan(/[\s]*@media[\s]*/i))
|
86
104
|
action { [:MEDIA_SYM, st(text)] }
|
87
105
|
|
106
|
+
when (text = @ss.scan(/[\s]*@(\-[A-Za-z]+\-)?document[\s]*/i))
|
107
|
+
action { [:DOCUMENT_QUERY_SYM, st(text)] }
|
108
|
+
|
109
|
+
when (text = @ss.scan(/[\s]*@namespace[\s]*/i))
|
110
|
+
action { [:NAMESPACE_SYM, st(text)] }
|
111
|
+
|
112
|
+
when (text = @ss.scan(/[\s]*@(\-[A-Za-z]+\-)?keyframes[\s]*/i))
|
113
|
+
action { [:KEYFRAMES_SYM, st(text)] }
|
114
|
+
|
88
115
|
when (text = @ss.scan(/[\s]*!([\s]*|[\s]*\/\*(.|[\s]*)*?\*\/[\s]*)important[\s]*/i))
|
89
116
|
action { [:IMPORTANT_SYM, st(text)] }
|
90
117
|
|
@@ -118,7 +145,7 @@ class Tokenizer < Parser
|
|
118
145
|
when (text = @ss.scan(/[\s]*\)/i))
|
119
146
|
action { [:RPAREN, st(text)] }
|
120
147
|
|
121
|
-
when (text = @ss.scan(
|
148
|
+
when (text = @ss.scan(/\[[\s]*/i))
|
122
149
|
action { [:LSQUARE, st(text)] }
|
123
150
|
|
124
151
|
when (text = @ss.scan(/[\s]*\]/i))
|
@@ -148,9 +175,6 @@ class Tokenizer < Parser
|
|
148
175
|
when (text = @ss.scan(/[\s]*~[\s]*/i))
|
149
176
|
action { [:TILDE, st(text)] }
|
150
177
|
|
151
|
-
when (text = @ss.scan(/\:not\([\s]*/i))
|
152
|
-
action { [:NOT, st(text)] }
|
153
|
-
|
154
178
|
when (text = @ss.scan(/[\s]*([0-9]*\.[0-9]+|[0-9]+)em[\s]*/i))
|
155
179
|
action { [:EMS, st(text)] }
|
156
180
|
|
@@ -196,10 +220,10 @@ class Tokenizer < Parser
|
|
196
220
|
when (text = @ss.scan(/[\s]+/i))
|
197
221
|
action { [:S, st(text)] }
|
198
222
|
|
199
|
-
when (text = @ss.scan(/("([^\n\r\f\\"]
|
223
|
+
when (text = @ss.scan(/("([^\n\r\f\\"]|\\(\n|\r\n|\r|\f)|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*"|'([^\n\r\f\\']|\\(\n|\r\n|\r|\f)|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*')/i))
|
200
224
|
action { [:STRING, st(text)] }
|
201
225
|
|
202
|
-
when (text = @ss.scan(/("([^\n\r\f\\"]
|
226
|
+
when (text = @ss.scan(/("([^\n\r\f\\"]|\\(\n|\r\n|\r|\f)|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*|'([^\n\r\f\\']|\\(\n|\r\n|\r|\f)|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*)/i))
|
203
227
|
action { [:INVALID, st(text)] }
|
204
228
|
|
205
229
|
when (text = @ss.scan(/./i))
|
@@ -3,11 +3,17 @@ module CSS
|
|
3
3
|
class Tokenizer < Parser
|
4
4
|
|
5
5
|
macro
|
6
|
-
nl \n|\r\n|\r|\f
|
6
|
+
nl (\n|\r\n|\r|\f)
|
7
7
|
w [\s]*
|
8
8
|
nonascii [^\0-\177]
|
9
9
|
num ([0-9]*\.[0-9]+|[0-9]+)
|
10
|
+
length {num}(px|cm|mm|in|pt|pc)
|
11
|
+
percentage {num}%
|
12
|
+
ems {num}em
|
13
|
+
exs {num}ex
|
10
14
|
unicode \\[0-9A-Fa-f]{1,6}(\r\n|[\s])?
|
15
|
+
nth ([\+\-]?[0-9]*n({w}[\+\-]{w}[0-9]+)?|[\+\-]?[0-9]+|odd|even)
|
16
|
+
vendorprefix \-[A-Za-z]+\-
|
11
17
|
|
12
18
|
escape {unicode}|\\[^\n\r\f0-9A-Fa-f]
|
13
19
|
nmchar [_A-Za-z0-9-]|{nonascii}|{escape}
|
@@ -23,6 +29,12 @@ macro
|
|
23
29
|
invalid ({invalid1}|{invalid2})
|
24
30
|
comment \/\*(.|{w})*?\*\/
|
25
31
|
|
32
|
+
unit ({num}|{length}|{percentage}|{ems}|{exs})
|
33
|
+
product {unit}({w}(\*{w}{unit}|\/{w}{num}))*
|
34
|
+
sum {product}([\s]+[\+\-][\s]+{product})*
|
35
|
+
calc ({vendorprefix})?calc\({w}{sum}{w}\)
|
36
|
+
math {calc}{w}
|
37
|
+
|
26
38
|
rule
|
27
39
|
|
28
40
|
# [:state] pattern [actions]
|
@@ -32,11 +44,29 @@ rule
|
|
32
44
|
U\+[0-9a-fA-F?]{1,6}(-[0-9a-fA-F]{1,6})? {[:UNICODE_RANGE, st(text)] }
|
33
45
|
{w}{comment}{w} { next_token }
|
34
46
|
|
47
|
+
# this one takes a selector as a parameter
|
48
|
+
not\(\s* { [:NOT_PSEUDO_CLASS, st(text)] }
|
49
|
+
|
50
|
+
# this one takes an "nth" value
|
51
|
+
(nth\-child|nth\-last\-child|nth\-of\-type|nth\-last\-of\-type)\({w}{nth}{w}\) { [:NTH_PSEUDO_CLASS, st(text)] }
|
52
|
+
|
53
|
+
# this one takes a comma-separated list of simple selectors as a parameter
|
54
|
+
({vendorprefix})?(matches|any)\(\s* { [:MATCHES_PSEUDO_CLASS, st(text)] }
|
55
|
+
|
56
|
+
# functions that can take an unquoted string parameter
|
57
|
+
(domain|url\-prefix)\({w}{string}{w}\) { [:FUNCTION_NO_QUOTE, st(text)] }
|
58
|
+
(domain|url\-prefix)\({w}([!#\$%&*-~]|{nonascii}|{escape})*{w}\) { [:FUNCTION_NO_QUOTE, st(text)] }
|
59
|
+
|
60
|
+
{w}{math}{w} { [:MATH, st(text)] }
|
61
|
+
|
35
62
|
{func}\(\s* { [:FUNCTION, st(text)] }
|
36
63
|
{w}@import{w} { [:IMPORT_SYM, st(text)] }
|
37
64
|
{w}@page{w} { [:PAGE_SYM, st(text)] }
|
38
65
|
{w}@charset{w} { [:CHARSET_SYM, st(text)] }
|
39
66
|
{w}@media{w} { [:MEDIA_SYM, st(text)] }
|
67
|
+
{w}@({vendorprefix})?document{w} { [:DOCUMENT_QUERY_SYM, st(text)] }
|
68
|
+
{w}@namespace{w} { [:NAMESPACE_SYM, st(text)] }
|
69
|
+
{w}@({vendorprefix})?keyframes{w} { [:KEYFRAMES_SYM, st(text)] }
|
40
70
|
{w}!({w}|{w}{comment}{w})important{w} { [:IMPORTANT_SYM, st(text)] }
|
41
71
|
{ident} { [:IDENT, st(text)] }
|
42
72
|
\#{name} { [:HASH, st(text)] }
|
@@ -48,7 +78,7 @@ rule
|
|
48
78
|
{w}!={w} { [:NOT_EQUAL, st(text)] }
|
49
79
|
{w}={w} { [:EQUAL, st(text)] }
|
50
80
|
{w}\) { [:RPAREN, st(text)] }
|
51
|
-
|
81
|
+
\[{w} { [:LSQUARE, st(text)] }
|
52
82
|
{w}\] { [:RSQUARE, st(text)] }
|
53
83
|
{w}\+{w} { [:PLUS, st(text)] }
|
54
84
|
{w}\{{w} { [:LBRACE, st(text)] }
|
@@ -58,16 +88,15 @@ rule
|
|
58
88
|
{w};{w} { [:SEMI, st(';')] }
|
59
89
|
\* { [:STAR, st(text)] }
|
60
90
|
{w}~{w} { [:TILDE, st(text)] }
|
61
|
-
|
62
|
-
{w}{
|
63
|
-
{w}{num}ex{w} { [:EXS, st(text)] }
|
91
|
+
{w}{ems}{w} { [:EMS, st(text)] }
|
92
|
+
{w}{exs}{w} { [:EXS, st(text)] }
|
64
93
|
|
65
|
-
{w}{
|
94
|
+
{w}{length}{w} { [:LENGTH, st(text)] }
|
66
95
|
{w}{num}(deg|rad|grad){w} { [:ANGLE, st(text)] }
|
67
96
|
{w}{num}(ms|s){w} { [:TIME, st(text)] }
|
68
97
|
{w}{num}[k]?hz{w} { [:FREQ, st(text)] }
|
69
98
|
|
70
|
-
{w}{
|
99
|
+
{w}{percentage}{w} { [:PERCENTAGE, st(text)] }
|
71
100
|
{w}{num}{w} { [:NUMBER, st(text)] }
|
72
101
|
{w}\/\/{w} { [:DOUBLESLASH, st(text)] }
|
73
102
|
{w}\/{w} { [:SLASH, st('/')] }
|
data/lib/csspool/css.rb
CHANGED
@@ -6,3 +6,7 @@ require 'csspool/css/rule_set'
|
|
6
6
|
require 'csspool/css/declaration'
|
7
7
|
require 'csspool/css/document'
|
8
8
|
require 'csspool/css/document_handler'
|
9
|
+
require 'csspool/css/document_query'
|
10
|
+
require 'csspool/css/namespace_rule'
|
11
|
+
require 'csspool/css/keyframes_rule'
|
12
|
+
require 'csspool/css/keyframes_block'
|
data/lib/csspool/selector.rb
CHANGED
@@ -17,10 +17,14 @@ module CSSPool
|
|
17
17
|
def specificity
|
18
18
|
a = b = c = 0
|
19
19
|
simple_selectors.each do |s|
|
20
|
-
|
20
|
+
if !s.name.nil?
|
21
|
+
c += 1
|
22
|
+
end
|
21
23
|
s.additional_selectors.each do |additional_selector|
|
22
24
|
if Selectors::Id === additional_selector
|
23
25
|
a += 1
|
26
|
+
elsif Selectors::PseudoElement === additional_selector
|
27
|
+
c += 1
|
24
28
|
else
|
25
29
|
b += 1
|
26
30
|
end
|
@@ -4,17 +4,22 @@ module CSSPool
|
|
4
4
|
attr_accessor :name
|
5
5
|
attr_accessor :value
|
6
6
|
attr_accessor :match_way
|
7
|
+
attr_accessor :namespace
|
7
8
|
|
8
9
|
NO_MATCH = 0
|
9
10
|
SET = 1
|
10
11
|
EQUALS = 2
|
11
12
|
INCLUDES = 3
|
12
13
|
DASHMATCH = 4
|
14
|
+
PREFIXMATCH = 5
|
15
|
+
SUFFIXMATCH = 6
|
16
|
+
SUBSTRINGMATCH = 7
|
13
17
|
|
14
|
-
def initialize name, value, match_way
|
18
|
+
def initialize name, value, match_way, namespace = nil
|
15
19
|
@name = name
|
16
20
|
@value = value
|
17
21
|
@match_way = match_way
|
22
|
+
@namespace = namespace
|
18
23
|
end
|
19
24
|
end
|
20
25
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'csspool/selectors/pseudo_class'
|
2
|
+
require 'csspool/selectors/pseudo_element'
|
3
|
+
|
4
|
+
module CSSPool
|
5
|
+
module Selectors
|
6
|
+
def Selectors.pseudo name
|
7
|
+
# FIXME: This is a bit of an ugly solution. Should be able to handle it
|
8
|
+
# more elegantly, and without calling out css2
|
9
|
+
css2_pseudo_elements =
|
10
|
+
if %w{after before first-letter first-line}.include? name
|
11
|
+
PseudoElement.new name, true
|
12
|
+
else
|
13
|
+
PseudoClass.new name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/csspool/selectors.rb
CHANGED