csspool-st 3.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.
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