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.
Files changed (46) hide show
  1. data/Gemfile.lock +4 -4
  2. data/Manifest.txt +13 -0
  3. data/README.rdoc +1 -1
  4. data/lib/csspool/css/document.rb +6 -0
  5. data/lib/csspool/css/document_handler.rb +41 -7
  6. data/lib/csspool/css/document_query.rb +14 -0
  7. data/lib/csspool/css/import_rule.rb +8 -4
  8. data/lib/csspool/css/keyframes_block.rb +14 -0
  9. data/lib/csspool/css/keyframes_rule.rb +14 -0
  10. data/lib/csspool/css/media.rb +5 -3
  11. data/lib/csspool/css/namespace_rule.rb +13 -0
  12. data/lib/csspool/css/parser.rb +970 -425
  13. data/lib/csspool/css/parser.y +227 -30
  14. data/lib/csspool/css/tokenizer.rb +31 -7
  15. data/lib/csspool/css/tokenizer.rex +36 -7
  16. data/lib/csspool/css.rb +4 -0
  17. data/lib/csspool/selector.rb +5 -1
  18. data/lib/csspool/selectors/attribute.rb +6 -1
  19. data/lib/csspool/selectors/pseudo.rb +17 -0
  20. data/lib/csspool/selectors/pseudo_element.rb +13 -0
  21. data/lib/csspool/selectors/simple.rb +0 -4
  22. data/lib/csspool/selectors/type.rb +7 -0
  23. data/lib/csspool/selectors/universal.rb +7 -0
  24. data/lib/csspool/selectors.rb +1 -1
  25. data/lib/csspool/terms/math.rb +18 -0
  26. data/lib/csspool/terms.rb +1 -0
  27. data/lib/csspool/visitors/children.rb +1 -1
  28. data/lib/csspool/visitors/comparable.rb +8 -2
  29. data/lib/csspool/visitors/iterator.rb +1 -1
  30. data/lib/csspool/visitors/to_css.rb +41 -21
  31. data/lib/csspool.rb +1 -1
  32. data/test/_local_helper.rb +2 -0
  33. data/test/css/test_document_query.rb +76 -0
  34. data/test/css/test_import_rule.rb +1 -1
  35. data/test/css/test_keyframes_rule.rb +93 -0
  36. data/test/css/test_namespace_rule.rb +54 -0
  37. data/test/css/test_parser.rb +46 -1
  38. data/test/css/test_tokenizer.rb +0 -45
  39. data/test/helper.rb +6 -1
  40. data/test/sac/test_parser.rb +16 -3
  41. data/test/test_declaration.rb +39 -0
  42. data/test/test_parser.rb +26 -13
  43. data/test/test_selector.rb +205 -5
  44. data/test/test_term.rb +23 -0
  45. data/test/visitors/test_to_css.rb +34 -23
  46. metadata +136 -118
@@ -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
- | media body
65
+ | conditional_rule body
66
+ | keyframes_rule body
48
67
  | ruleset
49
- | media
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
- : IDENT { result = Selectors::Type.new interpret_identifier val.first }
117
- | STAR { result = Selectors::Universal.new val.first }
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 IDENT EQUAL IDENT RSQUARE {
231
+ : LSQUARE ident_with_namespace EQUAL IDENT RSQUARE {
140
232
  result = Selectors::Attribute.new(
141
- interpret_identifier(val[1]),
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 IDENT EQUAL STRING RSQUARE {
239
+ | LSQUARE ident_with_namespace EQUAL STRING RSQUARE {
147
240
  result = Selectors::Attribute.new(
148
- interpret_identifier(val[1]),
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 IDENT INCLUDES STRING RSQUARE {
247
+ | LSQUARE ident_with_namespace INCLUDES STRING RSQUARE {
154
248
  result = Selectors::Attribute.new(
155
- interpret_identifier(val[1]),
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 IDENT INCLUDES IDENT RSQUARE {
255
+ | LSQUARE ident_with_namespace INCLUDES IDENT RSQUARE {
161
256
  result = Selectors::Attribute.new(
162
- interpret_identifier(val[1]),
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 IDENT DASHMATCH IDENT RSQUARE {
263
+ | LSQUARE ident_with_namespace DASHMATCH IDENT RSQUARE {
168
264
  result = Selectors::Attribute.new(
169
- interpret_identifier(val[1]),
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 IDENT DASHMATCH STRING RSQUARE {
271
+ | LSQUARE ident_with_namespace DASHMATCH STRING RSQUARE {
175
272
  result = Selectors::Attribute.new(
176
- interpret_identifier(val[1]),
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
- interpret_identifier(val[1]),
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::PseudoClass.new(
192
- interpret_identifier(val[1])
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 SEMI declarations
210
- | declaration SEMI
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\((.*)\)$/mu)[1].strip.match(/^(['"]?)((?:\\.|.)*)\1$/mu)[2]
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\\"]|\\\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))
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(/[\s]*\[[\s]*/i))
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\\"]|\\\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))
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\\"]|\\\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))
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
- {w}\[{w} { [:LSQUARE, st(text)] }
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
- \:not\({w} { [:NOT, st(text)] }
62
- {w}{num}em{w} { [:EMS, st(text)] }
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}{num}(px|cm|mm|in|pt|pc){w} { [:LENGTH, st(text)] }
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}{num}%{w} { [:PERCENTAGE, st(text)] }
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'
@@ -17,10 +17,14 @@ module CSSPool
17
17
  def specificity
18
18
  a = b = c = 0
19
19
  simple_selectors.each do |s|
20
- c += 1
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
@@ -0,0 +1,13 @@
1
+ module CSSPool
2
+ module Selectors
3
+ class PseudoElement < CSSPool::Selectors::Additional
4
+ attr_accessor :name
5
+ attr_accessor :css2
6
+
7
+ def initialize name, css2 = nil
8
+ @name = name
9
+ @css2 = css2
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,10 +1,6 @@
1
1
  module CSSPool
2
2
  module Selectors
3
3
  class Simple < CSSPool::Node
4
- NO_COMBINATOR = 0
5
- DESCENDENT = 1
6
- PRECEDED_BY = 2
7
- CHILD = 3
8
4
 
9
5
  attr_accessor :name
10
6
  attr_accessor :parse_location
@@ -1,6 +1,13 @@
1
1
  module CSSPool
2
2
  module Selectors
3
3
  class Type < CSSPool::Selectors::Simple
4
+
5
+ attr_accessor :namespace
6
+
7
+ def initialize name, combinator = nil, namespace = nil
8
+ super name, combinator
9
+ @namespace = namespace
10
+ end
4
11
  end
5
12
  end
6
13
  end
@@ -1,6 +1,13 @@
1
1
  module CSSPool
2
2
  module Selectors
3
3
  class Universal < CSSPool::Selectors::Simple
4
+
5
+ attr_accessor :namespace
6
+
7
+ def initialize name, combinator = nil, namespace = nil
8
+ super name, combinator
9
+ @namespace = namespace
10
+ end
4
11
  end
5
12
  end
6
13
  end
@@ -4,5 +4,5 @@ require 'csspool/selectors/type'
4
4
  require 'csspool/selectors/additional'
5
5
  require 'csspool/selectors/id'
6
6
  require 'csspool/selectors/class'
7
- require 'csspool/selectors/pseudo_class'
7
+ require 'csspool/selectors/pseudo'
8
8
  require 'csspool/selectors/attribute'