csspool-st 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.autotest +16 -0
  2. data/.gemtest +0 -0
  3. data/CHANGELOG.rdoc +87 -0
  4. data/Manifest.txt +62 -0
  5. data/README.rdoc +65 -0
  6. data/Rakefile +50 -0
  7. data/lib/csspool/collection.rb +50 -0
  8. data/lib/csspool/css/charset.rb +13 -0
  9. data/lib/csspool/css/declaration.rb +19 -0
  10. data/lib/csspool/css/document.rb +34 -0
  11. data/lib/csspool/css/document_handler.rb +51 -0
  12. data/lib/csspool/css/import_rule.rb +27 -0
  13. data/lib/csspool/css/media.rb +13 -0
  14. data/lib/csspool/css/parser.rb +1298 -0
  15. data/lib/csspool/css/parser.y +398 -0
  16. data/lib/csspool/css/rule_set.rb +17 -0
  17. data/lib/csspool/css/tokenizer.rb +231 -0
  18. data/lib/csspool/css/tokenizer.rex +97 -0
  19. data/lib/csspool/css.rb +8 -0
  20. data/lib/csspool/node.rb +41 -0
  21. data/lib/csspool/sac/document.rb +35 -0
  22. data/lib/csspool/sac/parser.rb +16 -0
  23. data/lib/csspool/sac.rb +2 -0
  24. data/lib/csspool/selector.rb +36 -0
  25. data/lib/csspool/selectors/additional.rb +6 -0
  26. data/lib/csspool/selectors/attribute.rb +21 -0
  27. data/lib/csspool/selectors/class.rb +11 -0
  28. data/lib/csspool/selectors/id.rb +11 -0
  29. data/lib/csspool/selectors/pseudo_class.rb +13 -0
  30. data/lib/csspool/selectors/simple.rb +22 -0
  31. data/lib/csspool/selectors/type.rb +6 -0
  32. data/lib/csspool/selectors/universal.rb +6 -0
  33. data/lib/csspool/selectors.rb +9 -0
  34. data/lib/csspool/terms/function.rb +17 -0
  35. data/lib/csspool/terms/hash.rb +6 -0
  36. data/lib/csspool/terms/ident.rb +15 -0
  37. data/lib/csspool/terms/number.rb +14 -0
  38. data/lib/csspool/terms/rgb.rb +20 -0
  39. data/lib/csspool/terms/string.rb +6 -0
  40. data/lib/csspool/terms/uri.rb +6 -0
  41. data/lib/csspool/terms.rb +7 -0
  42. data/lib/csspool/visitors/children.rb +50 -0
  43. data/lib/csspool/visitors/comparable.rb +84 -0
  44. data/lib/csspool/visitors/iterator.rb +80 -0
  45. data/lib/csspool/visitors/to_css.rb +248 -0
  46. data/lib/csspool/visitors/visitor.rb +17 -0
  47. data/lib/csspool/visitors.rb +5 -0
  48. data/lib/csspool.rb +21 -0
  49. data/test/css/test_document.rb +13 -0
  50. data/test/css/test_import_rule.rb +42 -0
  51. data/test/css/test_parser.rb +462 -0
  52. data/test/css/test_tokenizer.rb +320 -0
  53. data/test/helper.rb +65 -0
  54. data/test/sac/test_parser.rb +123 -0
  55. data/test/sac/test_properties.rb +43 -0
  56. data/test/sac/test_terms.rb +228 -0
  57. data/test/test_collection.rb +81 -0
  58. data/test/test_parser.rb +91 -0
  59. data/test/test_selector.rb +51 -0
  60. data/test/visitors/test_children.rb +20 -0
  61. data/test/visitors/test_comparable.rb +102 -0
  62. data/test/visitors/test_each.rb +19 -0
  63. data/test/visitors/test_to_css.rb +309 -0
  64. metadata +214 -0
@@ -0,0 +1,320 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require "helper"
4
+
5
+ module CSSPool
6
+ module CSS
7
+ class TestTokenizer < CSSPool::TestCase
8
+ def setup
9
+ super
10
+ @scanner = Class.new(CSSPool::CSS::Tokenizer) {
11
+ def do_parse
12
+ end
13
+ }.new
14
+ end
15
+
16
+ {
17
+ 'em' => :EMS,
18
+ 'ex' => :EXS,
19
+ 'px' => :LENGTH,
20
+ 'cm' => :LENGTH,
21
+ 'mm' => :LENGTH,
22
+ 'in' => :LENGTH,
23
+ 'pt' => :LENGTH,
24
+ 'pc' => :LENGTH,
25
+ 'deg' => :ANGLE,
26
+ 'rad' => :ANGLE,
27
+ 'grad' => :ANGLE,
28
+ 'ms' => :TIME,
29
+ 's' => :TIME,
30
+ 'hz' => :FREQ,
31
+ 'khz' => :FREQ,
32
+ '%' => :PERCENTAGE,
33
+ }.each do |unit,sym|
34
+ define_method :"test_#{unit}" do
35
+ ['10', '0.1'].each do |num|
36
+ num = "#{num}#{unit}"
37
+ [num, " #{num}", "#{num} ", " #{num} "].each do |str|
38
+ @scanner.scan str
39
+ assert_tokens([[sym, str]], @scanner)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def test_num
46
+ ['10', '0.1'].each do |num|
47
+ [num, " #{num}", "#{num} ", " #{num} "].each do |str|
48
+ @scanner.scan str
49
+ assert_tokens([[:NUMBER, str]], @scanner)
50
+ end
51
+ end
52
+ end
53
+
54
+ def test_important
55
+ [
56
+ '!important',
57
+ ' !important',
58
+ '!important ',
59
+ '! important ',
60
+ ' ! important ',
61
+ ].each do |str|
62
+ @scanner.scan str
63
+ assert_tokens([[:IMPORTANT_SYM, str]], @scanner)
64
+ end
65
+ end
66
+
67
+ {
68
+ '@page' => :PAGE_SYM,
69
+ '@import' => :IMPORT_SYM,
70
+ '@media' => :MEDIA_SYM,
71
+ '@charset' => :CHARSET_SYM,
72
+ }.each do |k,v|
73
+ define_method(:"test_#{k.sub(/@/, '')}") do
74
+ [k, " #{k}", "#{k} ", " #{k} "].each do |str|
75
+ @scanner.scan str
76
+ assert_tokens([[v, str]], @scanner)
77
+ end
78
+ end
79
+ end
80
+
81
+ def test_invalid
82
+ str = "'internet"
83
+ @scanner.scan str
84
+ assert_tokens([[:INVALID, str]], @scanner)
85
+
86
+ str = '"internet'
87
+ @scanner.scan str
88
+ assert_tokens([[:INVALID, str]], @scanner)
89
+ end
90
+
91
+ #def test_comment
92
+ # str = "/**** Hello World ***/"
93
+ # @scanner.scan str
94
+ # assert_tokens([[:COMMENT, str]], @scanner)
95
+
96
+ # str = "/* Hello World */"
97
+ # @scanner.scan str
98
+ # assert_tokens([[:COMMENT, str]], @scanner)
99
+ #end
100
+
101
+ def test_rbrace
102
+ str = " } \n "
103
+ @scanner.scan str
104
+ assert_tokens([[:RBRACE, str]], @scanner)
105
+ end
106
+
107
+ def test_lbrace
108
+ str = " { "
109
+ @scanner.scan str
110
+ assert_tokens([[:LBRACE, str]], @scanner)
111
+ end
112
+
113
+ def test_semi
114
+ str = " ; "
115
+ @scanner.scan str
116
+ assert_tokens([[:SEMI, ';']], @scanner)
117
+ end
118
+
119
+ def test_cdc
120
+ @scanner.scan("-->")
121
+ assert_tokens([[:CDC, "-->"]], @scanner)
122
+ end
123
+
124
+ def test_cdo
125
+ @scanner.scan("<!--")
126
+ assert_tokens([[:CDO, "<!--"]], @scanner)
127
+ end
128
+
129
+ def test_unicode_range
130
+ @scanner.scan("U+0-10FFFF")
131
+ assert_tokens([[:UNICODE_RANGE, "U+0-10FFFF"]], @scanner)
132
+ end
133
+
134
+ def test_url_with_string
135
+ @scanner.scan("url('http://tlm.com')")
136
+ assert_tokens([[:URI, "url('http://tlm.com')"]], @scanner)
137
+ end
138
+
139
+ def test_url_with_others
140
+ @scanner.scan("url(http://tlm.com?f&b)")
141
+ assert_tokens([[:URI, "url(http://tlm.com?f&b)"]], @scanner)
142
+ end
143
+
144
+ def test_unicode
145
+ @scanner.scan("a日本語")
146
+ assert_tokens([[:IDENT, 'a日本語']], @scanner)
147
+ end
148
+
149
+ def test_tokenize_bad_percent
150
+ @scanner.scan("%")
151
+ assert_tokens([["%", "%"]], @scanner)
152
+ end
153
+
154
+ def test_not_equal
155
+ @scanner.scan("h1[a!='Tender Lovemaking']")
156
+ assert_tokens([ [:IDENT, 'h1'],
157
+ [:LSQUARE, '['],
158
+ [:IDENT, 'a'],
159
+ [:NOT_EQUAL, '!='],
160
+ [:STRING, "'Tender Lovemaking'"],
161
+ [:RSQUARE, ']'],
162
+ ], @scanner)
163
+ end
164
+
165
+ def test_negation
166
+ @scanner.scan("p:not(.a)")
167
+ assert_tokens([ [:IDENT, 'p'],
168
+ [:NOT, ':not('],
169
+ ['.', '.'],
170
+ [:IDENT, 'a'],
171
+ [:RPAREN, ')'],
172
+ ], @scanner)
173
+ end
174
+
175
+ def test_function
176
+ @scanner.scan("script comment()")
177
+ assert_tokens([ [:IDENT, 'script'],
178
+ [:S, ' '],
179
+ [:FUNCTION, 'comment('],
180
+ [:RPAREN, ')'],
181
+ ], @scanner)
182
+ end
183
+
184
+ def test_preceding_selector
185
+ @scanner.scan("E ~ F")
186
+ assert_tokens([ [:IDENT, 'E'],
187
+ [:TILDE, ' ~ '],
188
+ [:IDENT, 'F'],
189
+ ], @scanner)
190
+ end
191
+
192
+ def test_scan_attribute_string
193
+ @scanner.scan("h1[a='Tender Lovemaking']")
194
+ assert_tokens([ [:IDENT, 'h1'],
195
+ [:LSQUARE, '['],
196
+ [:IDENT, 'a'],
197
+ [:EQUAL, '='],
198
+ [:STRING, "'Tender Lovemaking'"],
199
+ [:RSQUARE, ']'],
200
+ ], @scanner)
201
+ @scanner.scan('h1[a="Tender Lovemaking"]')
202
+ assert_tokens([ [:IDENT, 'h1'],
203
+ [:LSQUARE, '['],
204
+ [:IDENT, 'a'],
205
+ [:EQUAL, '='],
206
+ [:STRING, '"Tender Lovemaking"'],
207
+ [:RSQUARE, ']'],
208
+ ], @scanner)
209
+ end
210
+
211
+ def test_scan_id
212
+ @scanner.scan('#foo')
213
+ assert_tokens([ [:HASH, '#foo'] ], @scanner)
214
+ end
215
+
216
+ def test_scan_pseudo
217
+ @scanner.scan('a:visited')
218
+ assert_tokens([ [:IDENT, 'a'],
219
+ [':', ':'],
220
+ [:IDENT, 'visited']
221
+ ], @scanner)
222
+ end
223
+
224
+ def test_scan_star
225
+ @scanner.scan('*')
226
+ assert_tokens([ [:STAR, '*'], ], @scanner)
227
+ end
228
+
229
+ def test_ident_with_minus
230
+ @scanner.scan('-book')
231
+ assert_tokens([ [:IDENT, '-book'], ], @scanner)
232
+ end
233
+
234
+ def test_scan_class
235
+ @scanner.scan('x.awesome')
236
+ assert_tokens([ [:IDENT, 'x'],
237
+ ['.', '.'],
238
+ [:IDENT, 'awesome'],
239
+ ], @scanner)
240
+ end
241
+
242
+ def test_scan_greater
243
+ @scanner.scan('x > y')
244
+ assert_tokens([ [:IDENT, 'x'],
245
+ [:GREATER, ' > '],
246
+ [:IDENT, 'y']
247
+ ], @scanner)
248
+ end
249
+
250
+ def test_scan_slash
251
+ @scanner.scan('x/y')
252
+ assert_tokens([ [:IDENT, 'x'],
253
+ [:SLASH, '/'],
254
+ [:IDENT, 'y']
255
+ ], @scanner)
256
+ end
257
+
258
+ def test_scan_doubleslash
259
+ @scanner.scan('x//y')
260
+ assert_tokens([ [:IDENT, 'x'],
261
+ [:DOUBLESLASH, '//'],
262
+ [:IDENT, 'y']
263
+ ], @scanner)
264
+ end
265
+
266
+ def test_scan_function_selector
267
+ @scanner.scan('x:eq(0)')
268
+ assert_tokens([ [:IDENT, 'x'],
269
+ [':', ':'],
270
+ [:FUNCTION, 'eq('],
271
+ [:NUMBER, "0"],
272
+ [:RPAREN, ')'],
273
+ ], @scanner)
274
+ end
275
+
276
+ def test_scan_an_plus_b
277
+ @scanner.scan('x:nth-child(5n+3)')
278
+ assert_tokens([ [:IDENT, 'x'],
279
+ [':', ':'],
280
+ [:FUNCTION, 'nth-child('],
281
+ [:NUMBER, '5'],
282
+ [:IDENT, 'n'],
283
+ [:PLUS, '+'],
284
+ [:NUMBER, '3'],
285
+ [:RPAREN, ')'],
286
+ ], @scanner)
287
+
288
+ @scanner.scan('x:nth-child(-1n+3)')
289
+ assert_tokens([ [:IDENT, 'x'],
290
+ [':', ':'],
291
+ [:FUNCTION, 'nth-child('],
292
+ [:MINUS, '-'],
293
+ [:NUMBER, '1'],
294
+ [:IDENT, 'n'],
295
+ [:PLUS, '+'],
296
+ [:NUMBER, '3'],
297
+ [:RPAREN, ')'],
298
+ ], @scanner)
299
+
300
+ @scanner.scan('x:nth-child(-n+3)')
301
+ assert_tokens([ [:IDENT, 'x'],
302
+ [':', ':'],
303
+ [:FUNCTION, 'nth-child('],
304
+ [:IDENT, '-n'],
305
+ [:PLUS, '+'],
306
+ [:NUMBER, '3'],
307
+ [:RPAREN, ')'],
308
+ ], @scanner)
309
+ end
310
+
311
+ def assert_tokens(tokens, scanner)
312
+ toks = []
313
+ while tok = @scanner.next_token
314
+ toks << tok
315
+ end
316
+ assert_equal(tokens, toks)
317
+ end
318
+ end
319
+ end
320
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,65 @@
1
+ require "test/unit"
2
+ require "csspool"
3
+
4
+ module CSSPool
5
+ class TestCase < Test::Unit::TestCase
6
+ unless RUBY_VERSION >= '1.9'
7
+ undef :default_test
8
+ end
9
+
10
+ ASSET_DIR = File.join(File.dirname(__FILE__), 'files')
11
+
12
+ class MyDoc < CSSPool::CSS::DocumentHandler
13
+ attr_accessor :start_documents, :end_documents
14
+ attr_accessor :charsets, :import_styles, :comments, :start_selectors
15
+ attr_accessor :end_selectors, :properties
16
+
17
+ def initialize
18
+ @start_documents = []
19
+ @end_documents = []
20
+ @charsets = []
21
+ @import_styles = []
22
+ @comments = []
23
+ @start_selectors = []
24
+ @end_selectors = []
25
+ @properties = []
26
+ end
27
+
28
+ def property name, expression, important
29
+ @properties << [name, expression]
30
+ end
31
+
32
+ def start_document
33
+ @start_documents << true
34
+ end
35
+
36
+ def end_document
37
+ @end_documents << true
38
+ end
39
+
40
+ def charset name, location
41
+ @charsets << [name, location]
42
+ end
43
+
44
+ def import_style media_list, uri, default_ns = nil, location = {}
45
+ @import_styles << [media_list, uri, default_ns, location]
46
+ end
47
+
48
+ def namespace_declaration prefix, uri, location
49
+ @import_styles << [prefix, uri, location]
50
+ end
51
+
52
+ def comment string
53
+ @comments << string
54
+ end
55
+
56
+ def start_selector selectors
57
+ @start_selectors << selectors
58
+ end
59
+
60
+ def end_selector selectors
61
+ @end_selectors << selectors
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,123 @@
1
+ require 'helper'
2
+
3
+ module CSSPool
4
+ module SAC
5
+ class TestParser < CSSPool::TestCase
6
+ def setup
7
+ super
8
+ @doc = MyDoc.new
9
+ @css = <<-eocss
10
+ @charset "UTF-8";
11
+ @import url("foo.css") screen;
12
+ /* This is a comment */
13
+ div a.foo, #bar, * { background: red; }
14
+ div#a, a.foo, a:hover, a[href][int="10"]{ background: red; }
15
+ ::selection, q:before { background: red; }
16
+ eocss
17
+ @parser = CSSPool::SAC::Parser.new(@doc)
18
+ @parser.parse(@css)
19
+ end
20
+
21
+ def test_start_and_end_called_with_the_same
22
+ assert_equal @doc.start_selectors, @doc.end_selectors
23
+ end
24
+
25
+ def test_parse_no_doc
26
+ parser = CSSPool::SAC::Parser.new
27
+ parser.parse(@css)
28
+ end
29
+
30
+ def test_start_document
31
+ assert_equal [true], @doc.start_documents
32
+ end
33
+
34
+ def test_end_document
35
+ assert_equal [true], @doc.end_documents
36
+ end
37
+
38
+ def test_charset
39
+ assert_equal("UTF-8", @doc.charsets.first.first)
40
+ end
41
+
42
+ def test_import_style
43
+ styles = @doc.import_styles.first
44
+ assert_equal "screen", styles.first.first.first.value
45
+ assert_equal "foo.css", styles[1].value
46
+ assert_nil styles[2]
47
+ end
48
+
49
+ def test_start_selector
50
+ selectors_for_rule = @doc.start_selectors.first
51
+ assert selectors_for_rule
52
+ assert_equal 3, selectors_for_rule.length
53
+ end
54
+
55
+ def test_simple_selector
56
+ selectors_for_rule = @doc.start_selectors.first
57
+ selector = selectors_for_rule.first # => div a.foo
58
+ assert_equal 2, selector.simple_selectors.length
59
+ end
60
+
61
+ def test_additional_selector_list
62
+ selectors_for_rule = @doc.start_selectors.first
63
+ selector = selectors_for_rule.first # => div a.foo
64
+ simple_selector = selector.simple_selectors[1] # => a.foo
65
+ assert additional_selectors = simple_selector.additional_selectors
66
+ assert_equal 1, additional_selectors.length
67
+ end
68
+
69
+ def test_additional_selector_class_name
70
+ selectors_for_rule = @doc.start_selectors.first
71
+ selector = selectors_for_rule.first # => div a.foo
72
+ simple_selector = selector.simple_selectors[1] # => a.foo
73
+ assert additional_selectors = simple_selector.additional_selectors
74
+ foo_class = additional_selectors.first
75
+ assert_equal 'foo', foo_class.name
76
+ end
77
+
78
+ def test_id_additional_selector
79
+ selectors_for_rule = @doc.start_selectors[1]
80
+ selector = selectors_for_rule.first # => div#a
81
+ simple_selector = selector.simple_selectors.first # => div#a
82
+ assert_equal 'a', simple_selector.additional_selectors.first.name
83
+ end
84
+
85
+ def test_pseudo_additional_selector
86
+ selectors_for_rule = @doc.start_selectors[1]
87
+ selector = selectors_for_rule[2] # => 'a:hover'
88
+ simple_selector = selector.simple_selectors.first # => a:hover
89
+ assert_equal 'hover', simple_selector.additional_selectors.first.name
90
+ assert_nil simple_selector.additional_selectors.first.extra
91
+ end
92
+
93
+ def test_attribute_selector
94
+ selectors_for_rule = @doc.start_selectors[1]
95
+ selector = selectors_for_rule[3] # => a[href][int="10"]
96
+ simple_selector = selector.simple_selectors.first
97
+
98
+ assert_equal 'href', simple_selector.additional_selectors.first.name
99
+ assert_nil simple_selector.additional_selectors.first.value
100
+ assert_equal 1, simple_selector.additional_selectors.first.match_way
101
+
102
+ assert_equal 'int', simple_selector.additional_selectors[1].name
103
+ assert_equal '10', simple_selector.additional_selectors[1].value
104
+ assert_equal 2, simple_selector.additional_selectors[1].match_way
105
+ end
106
+
107
+ def test_pseudo_element_additional_selector
108
+ selectors_for_rule = @doc.start_selectors[2]
109
+ selector = selectors_for_rule[0]
110
+
111
+ simple_selector = selector.simple_selectors[0] # ::selection
112
+ assert_equal 'selection', simple_selector.additional_selectors.first.name
113
+ assert_nil simple_selector.additional_selectors.first.css2
114
+
115
+ selector = selectors_for_rule[1]
116
+ simple_selector = selector.simple_selectors[0] # q.before
117
+ assert_equal 'before', simple_selector.additional_selectors.first.name
118
+ assert_equal true, simple_selector.additional_selectors.first.css2
119
+ end
120
+
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,43 @@
1
+ require 'helper'
2
+
3
+ module CSSPool
4
+ module SAC
5
+ class TestProperties < CSSPool::TestCase
6
+ def setup
7
+ super
8
+ @doc = MyDoc.new
9
+ @css = <<-eocss
10
+ @charset "UTF-8";
11
+ @import url("foo.css") screen;
12
+ /* This is a comment */
13
+ div a.foo, #bar, * { background: red; }
14
+ div#a, a.foo, a:hover, a[href][int="10"]{ background: red; }
15
+ eocss
16
+ @parser = CSSPool::SAC::Parser.new(@doc)
17
+ @parser.parse(@css)
18
+ end
19
+
20
+ def test_properties
21
+ assert_equal ['background'], @doc.properties.map { |x| x.first }.uniq
22
+ @doc.properties.each do |property|
23
+ assert_equal 1, property[1].length
24
+ end
25
+ assert_equal ['red'], @doc.properties.map { |x| x[1].first.value }.uniq
26
+ end
27
+
28
+ def test_ident_with_comma
29
+ doc = MyDoc.new
30
+ parser = CSSPool::SAC::Parser.new(doc)
31
+ parser.parse <<-eocss
32
+ h1 { font-family: Verdana, sans-serif, monospace; }
33
+ eocss
34
+ assert_equal 1, doc.properties.length
35
+ values = doc.properties.first[1]
36
+ assert_equal 3, values.length
37
+ assert_equal [nil, ',', ','],
38
+ values.map { |value| value.operator }
39
+ values.each { |value| assert value.parse_location }
40
+ end
41
+ end
42
+ end
43
+ end