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,248 @@
1
+ module CSSPool
2
+ module Visitors
3
+ class ToCSS < Visitor
4
+
5
+ CSS_IDENTIFIER_ILLEGAL_CHARACTERS =
6
+ (0..255).to_a.pack('U*').gsub(/[a-zA-Z0-9_-]/, '')
7
+ CSS_STRING_ESCAPE_MAP = {
8
+ "\\" => "\\\\",
9
+ "\"" => "\\\"",
10
+ "\n" => "\\a ", # CSS2 4.1.3 p3.2
11
+ "\r" => "\\\r",
12
+ "\f" => "\\\f"
13
+ }
14
+
15
+ def initialize
16
+ @indent_level = 0
17
+ @indent_space = indent_space
18
+ end
19
+
20
+ visitor_for CSS::Document do |target|
21
+ # Default media list is []
22
+ current_media_type = []
23
+
24
+ tokens = []
25
+
26
+ target.charsets.each do |char_set|
27
+ tokens << char_set.accept(self)
28
+ end
29
+
30
+ target.import_rules.each do |ir|
31
+ tokens << ir.accept(self)
32
+ end
33
+
34
+ target.rule_sets.each { |rs|
35
+ if rs.media != current_media_type
36
+ media = " " + rs.media.map do |medium|
37
+ medium.name.map{ |i| i.accept(self) }.join(' ')
38
+ end.join(', ')
39
+ tokens << "#{indent}@media#{media} {"
40
+ @indent_level += 1
41
+ end
42
+
43
+ tokens << rs.accept(self)
44
+
45
+ if rs.media != current_media_type
46
+ current_media_type = rs.media
47
+ @indent_level -= 1
48
+ tokens << "#{indent}}"
49
+ end
50
+ }
51
+ tokens.join(line_break)
52
+ end
53
+
54
+ visitor_for CSS::Charset do |target|
55
+ "@charset \"#{escape_css_string target.name}\";"
56
+ end
57
+
58
+ visitor_for CSS::ImportRule do |target|
59
+ media = ''
60
+ media = " " + target.media.map do |medium|
61
+ medium.name.map{ |i| i.accept(self) }.join(' ')
62
+ end.join(', ') if target.media.length > 0
63
+
64
+ "#{indent}@import #{target.uri.accept(self)}#{media};"
65
+ end
66
+
67
+ visitor_for CSS::RuleSet do |target|
68
+ "#{indent}" +
69
+ target.selectors.map { |sel| sel.accept self }.join(", ") + " {#{line_break}" +
70
+ target.declarations.map { |decl| decl.accept self }.join(line_break) +
71
+ "#{line_break}#{indent}}"
72
+ end
73
+
74
+ visitor_for CSS::Declaration do |target|
75
+ important = target.important? ? ' !important' : ''
76
+
77
+ indent {
78
+ "#{indent}#{escape_css_identifier target.property}: " + target.expressions.map { |exp|
79
+
80
+ op = '/' == exp.operator ? ' /' : exp.operator
81
+
82
+ [
83
+ op,
84
+ exp.accept(self),
85
+ ].join ' '
86
+ }.join.strip + "#{important};"
87
+ }
88
+ end
89
+
90
+ visitor_for Terms::Ident do |target|
91
+ escape_css_identifier target.value
92
+ end
93
+
94
+ visitor_for Terms::Hash do |target|
95
+ "##{target.value}"
96
+ end
97
+
98
+ visitor_for Selectors::MediaExpression do |target|
99
+ '(' + escape_css_string(target.name) + ': ' +
100
+ target.value.map { |x| x.accept self }.join + ')'
101
+ end
102
+
103
+ visitor_for Terms::URI do |target|
104
+ "url(\"#{escape_css_string target.value}\")"
105
+ end
106
+
107
+ visitor_for Terms::Function do |target|
108
+ "#{escape_css_identifier target.name}(" +
109
+ target.params.map { |x|
110
+ [
111
+ x.operator,
112
+ x.accept(self)
113
+ ].compact.join(' ')
114
+ }.join + ')'
115
+ end
116
+
117
+ visitor_for Terms::Rgb do |target|
118
+ params = [
119
+ target.red,
120
+ target.green,
121
+ target.blue
122
+ ].map { |c|
123
+ c.accept(self)
124
+ }.join ', '
125
+
126
+ %{rgb(#{params})}
127
+ end
128
+
129
+ visitor_for Terms::String do |target|
130
+ "\"#{escape_css_string target.value}\""
131
+ end
132
+
133
+ visitor_for Terms::Number do |target|
134
+ [
135
+ target.unary_operator == :minus ? '-' : nil,
136
+ target.value,
137
+ target.type
138
+ ].compact.join
139
+ end
140
+
141
+ visitor_for Selector do |target|
142
+ target.simple_selectors.map { |ss| ss.accept self }.join
143
+ end
144
+
145
+ visitor_for Selectors::Simple, Selectors::Universal, Selectors::Type do |target|
146
+ combo = {
147
+ :s => ' ',
148
+ :+ => ' + ',
149
+ :> => ' > '
150
+ }[target.combinator]
151
+
152
+ name = [nil, '*'].include?(target.name) ? target.name : escape_css_identifier(target.name)
153
+ [combo, name].compact.join +
154
+ target.additional_selectors.map { |as| as.accept self }.join
155
+ end
156
+
157
+ visitor_for Selectors::Id do |target|
158
+ "##{escape_css_identifier target.name}"
159
+ end
160
+
161
+ visitor_for Selectors::Class do |target|
162
+ ".#{escape_css_identifier target.name}"
163
+ end
164
+
165
+ visitor_for Selectors::PseudoClass do |target|
166
+ if target.extra.nil?
167
+ ":#{escape_css_identifier target.name}"
168
+ else
169
+ ":#{escape_css_identifier target.name}(#{escape_css_identifier target.extra})"
170
+ end
171
+ end
172
+
173
+ visitor_for Selectors::PseudoElement do |target|
174
+ if target.css2.nil?
175
+ "::#{escape_css_identifier target.name}"
176
+ else
177
+ ":#{escape_css_identifier target.name}"
178
+ end
179
+ end
180
+
181
+ visitor_for Selectors::Attribute do |target|
182
+ case target.match_way
183
+ when Selectors::Attribute::SET
184
+ "[#{escape_css_identifier target.name}]"
185
+ when Selectors::Attribute::EQUALS
186
+ "[#{escape_css_identifier target.name}=\"#{escape_css_string target.value}\"]"
187
+ when Selectors::Attribute::INCLUDES
188
+ "[#{escape_css_identifier target.name} ~= \"#{escape_css_string target.value}\"]"
189
+ when Selectors::Attribute::DASHMATCH
190
+ "[#{escape_css_identifier target.name} |= \"#{escape_css_string target.value}\"]"
191
+ else
192
+ raise "no matching matchway"
193
+ end
194
+ end
195
+
196
+ private
197
+ def indent
198
+ if block_given?
199
+ @indent_level += 1
200
+ result = yield
201
+ @indent_level -= 1
202
+ return result
203
+ end
204
+ "#{@indent_space * @indent_level}"
205
+ end
206
+
207
+ def line_break
208
+ "\n"
209
+ end
210
+
211
+ def indent_space
212
+ ' '
213
+ end
214
+
215
+ def escape_css_identifier text
216
+ # CSS2 4.1.3 p2
217
+ unsafe_chars = /[#{Regexp.escape CSS_IDENTIFIER_ILLEGAL_CHARACTERS}]/
218
+ text.gsub(/^\d|^\-(?=\-|\d)|#{unsafe_chars}/um) do |char|
219
+ if ':()-\\ ='.include? char
220
+ "\\#{char}"
221
+ else # I don't trust others to handle space termination well.
222
+ "\\#{char.unpack('U').first.to_s(16).rjust(6, '0')}"
223
+ end
224
+ end
225
+ end
226
+
227
+ def escape_css_string text
228
+ text.gsub(/[\\"\n\r\f]/) {CSS_STRING_ESCAPE_MAP[$&]}
229
+ end
230
+ end
231
+
232
+ class ToMinifiedCSS < ToCSS
233
+ def line_break
234
+ ""
235
+ end
236
+
237
+ def indent_space
238
+ ' '
239
+ end
240
+
241
+ visitor_for CSS::RuleSet do |target|
242
+ target.selectors.map { |sel| sel.accept self }.join(", ") + " {" +
243
+ target.declarations.map { |decl| decl.accept self }.join +
244
+ " }"
245
+ end
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,17 @@
1
+ module CSSPool
2
+ module Visitors
3
+ class Visitor
4
+ def self.visitor_for *klasses, &block
5
+ klasses.each do |klass|
6
+ method_name = klass.name.split('::').join('_')
7
+ define_method(:"visit_#{method_name}", block)
8
+ end
9
+ end
10
+
11
+ def accept target
12
+ method_name = target.class.name.split('::').join('_')
13
+ send(:"visit_#{method_name}", target)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ require 'csspool/visitors/visitor'
2
+ require 'csspool/visitors/to_css'
3
+ require 'csspool/visitors/comparable'
4
+ require 'csspool/visitors/iterator'
5
+ require 'csspool/visitors/children'
data/lib/csspool.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'csspool/node'
2
+ require 'csspool/selectors'
3
+ require 'csspool/terms'
4
+ require 'csspool/selector'
5
+ old = $-w
6
+ $-w = false
7
+ require 'csspool/css/parser'
8
+ $-w = old
9
+ require 'csspool/css/tokenizer'
10
+ require 'csspool/sac'
11
+ require 'csspool/css'
12
+ require 'csspool/visitors'
13
+ require 'csspool/collection'
14
+
15
+ module CSSPool
16
+ VERSION = "3.1.0"
17
+
18
+ def self.CSS doc
19
+ CSSPool::CSS::Document.parse doc
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ module CSSPool
4
+ module CSS
5
+ class TestDocument < CSSPool::TestCase
6
+ def test_search
7
+ doc = CSSPool.CSS('div > p { background: red; }')
8
+ assert_equal 1, doc['div > p'].length
9
+ assert_equal 1, doc['div > p'].first.declarations.length
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ require 'helper'
2
+
3
+ module CSSPool
4
+ module CSS
5
+ class TestImportRule < CSSPool::TestCase
6
+ def test_import
7
+ doc = CSSPool.CSS <<-eocss
8
+ @import "foo.css";
9
+ eocss
10
+
11
+ assert_equal 1, doc.import_rules.length
12
+
13
+ doc.import_rules.each do |ir|
14
+ new_doc = ir.load do |url|
15
+ assert_equal "foo.css", url
16
+ "div { background: red; }"
17
+ end
18
+ assert new_doc
19
+ assert_equal 1, new_doc.rule_sets.length
20
+ assert_equal ir, new_doc.parent_import_rule
21
+ assert_equal doc, new_doc.parent
22
+ end
23
+ end
24
+
25
+ def test_import_with_media
26
+ doc = CSSPool.CSS <<-eocss
27
+ @import "foo.css" screen, print;
28
+ eocss
29
+
30
+ assert_equal 1, doc.import_rules.length
31
+ doc.import_rules.each do |ir|
32
+ new_doc = ir.load do |url|
33
+ "div { background: red; }"
34
+ end
35
+ new_doc.rule_sets.each do |rs|
36
+ assert_equal ir.media, rs.media
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end