csspool 0.2.6 → 2.0.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 (138) hide show
  1. data/.autotest +16 -0
  2. data/{CHANGELOG.txt → CHANGELOG.rdoc} +10 -0
  3. data/Manifest.txt +60 -72
  4. data/README.rdoc +78 -0
  5. data/Rakefile +9 -52
  6. data/lib/csspool/collection.rb +50 -0
  7. data/lib/csspool/css/charset.rb +7 -0
  8. data/lib/csspool/css/declaration.rb +8 -0
  9. data/lib/csspool/css/document.rb +34 -0
  10. data/lib/csspool/css/document_handler.rb +51 -0
  11. data/lib/csspool/css/import_rule.rb +15 -0
  12. data/lib/csspool/css/media.rb +7 -0
  13. data/lib/csspool/css/rule_set.rb +12 -0
  14. data/lib/csspool/css.rb +7 -0
  15. data/lib/csspool/lib_croco/cr_additional_sel.rb +46 -0
  16. data/lib/csspool/lib_croco/cr_attr_sel.rb +16 -0
  17. data/lib/csspool/lib_croco/cr_doc_handler.rb +24 -0
  18. data/lib/csspool/lib_croco/cr_num.rb +13 -0
  19. data/lib/csspool/lib_croco/cr_parser.rb +11 -0
  20. data/lib/csspool/lib_croco/cr_parsing_location.rb +17 -0
  21. data/lib/csspool/lib_croco/cr_pseudo.rb +14 -0
  22. data/lib/csspool/lib_croco/cr_rgb.rb +18 -0
  23. data/lib/csspool/lib_croco/cr_selector.rb +34 -0
  24. data/lib/csspool/lib_croco/cr_simple_sel.rb +54 -0
  25. data/lib/csspool/lib_croco/cr_term.rb +97 -0
  26. data/lib/csspool/lib_croco/glist.rb +21 -0
  27. data/lib/csspool/lib_croco.rb +78 -0
  28. data/lib/csspool/node.rb +5 -0
  29. data/lib/csspool/sac/document.rb +35 -0
  30. data/lib/csspool/sac/parser.rb +122 -0
  31. data/lib/csspool/sac.rb +2 -0
  32. data/lib/csspool/selector.rb +32 -0
  33. data/lib/csspool/selectors/additional.rb +6 -0
  34. data/lib/csspool/selectors/attribute.rb +21 -0
  35. data/lib/csspool/selectors/class.rb +11 -0
  36. data/lib/csspool/selectors/id.rb +11 -0
  37. data/lib/csspool/selectors/pseudo_class.rb +13 -0
  38. data/lib/csspool/selectors/simple.rb +22 -0
  39. data/lib/csspool/selectors/type.rb +6 -0
  40. data/lib/csspool/selectors/universal.rb +6 -0
  41. data/lib/csspool/selectors.rb +8 -0
  42. data/lib/csspool/terms/function.rb +17 -0
  43. data/lib/csspool/terms/hash.rb +6 -0
  44. data/lib/csspool/terms/ident.rb +15 -0
  45. data/lib/csspool/terms/number.rb +14 -0
  46. data/lib/csspool/terms/rgb.rb +23 -0
  47. data/lib/csspool/terms/string.rb +6 -0
  48. data/lib/csspool/terms/uri.rb +6 -0
  49. data/lib/csspool/terms.rb +7 -0
  50. data/lib/csspool/visitable.rb +18 -0
  51. data/lib/csspool/visitors/comparable.rb +78 -0
  52. data/lib/csspool/visitors/to_css.rb +201 -0
  53. data/lib/csspool/visitors/visitor.rb +17 -0
  54. data/lib/csspool/visitors.rb +3 -0
  55. data/lib/csspool.rb +18 -1
  56. data/test/css/test_document.rb +13 -0
  57. data/test/css/test_import_rule.rb +42 -0
  58. data/test/helper.rb +62 -6
  59. data/test/sac/test_parser.rb +115 -0
  60. data/test/sac/test_properties.rb +43 -0
  61. data/test/sac/test_terms.rb +134 -0
  62. data/test/test_collection.rb +81 -0
  63. data/test/test_parser.rb +75 -241
  64. data/test/test_selector.rb +45 -34
  65. data/test/visitors/test_comparable.rb +72 -0
  66. data/test/visitors/test_to_css.rb +141 -0
  67. metadata +98 -85
  68. data/README.txt +0 -79
  69. data/lib/css/sac/conditions/attribute_condition.rb +0 -56
  70. data/lib/css/sac/conditions/begin_hyphen_condition.rb +0 -22
  71. data/lib/css/sac/conditions/class_condition.rb +0 -22
  72. data/lib/css/sac/conditions/combinator_condition.rb +0 -40
  73. data/lib/css/sac/conditions/condition.rb +0 -35
  74. data/lib/css/sac/conditions/id_condition.rb +0 -27
  75. data/lib/css/sac/conditions/one_of_condition.rb +0 -22
  76. data/lib/css/sac/conditions/pseudo_class_condition.rb +0 -24
  77. data/lib/css/sac/conditions.rb +0 -5
  78. data/lib/css/sac/document_handler.rb +0 -67
  79. data/lib/css/sac/error_handler.rb +0 -14
  80. data/lib/css/sac/generated_parser.rb +0 -999
  81. data/lib/css/sac/generated_property_parser.rb +0 -9214
  82. data/lib/css/sac/lexeme.rb +0 -29
  83. data/lib/css/sac/lexical_unit.rb +0 -203
  84. data/lib/css/sac/parse_exception.rb +0 -6
  85. data/lib/css/sac/parser.rb +0 -115
  86. data/lib/css/sac/property_parser.rb +0 -47
  87. data/lib/css/sac/selectors/child_selector.rb +0 -40
  88. data/lib/css/sac/selectors/conditional_selector.rb +0 -49
  89. data/lib/css/sac/selectors/descendant_selector.rb +0 -40
  90. data/lib/css/sac/selectors/element_selector.rb +0 -39
  91. data/lib/css/sac/selectors/selector.rb +0 -31
  92. data/lib/css/sac/selectors/sibling_selector.rb +0 -39
  93. data/lib/css/sac/selectors/simple_selector.rb +0 -25
  94. data/lib/css/sac/selectors.rb +0 -5
  95. data/lib/css/sac/token.rb +0 -27
  96. data/lib/css/sac/tokenizer.rb +0 -187
  97. data/lib/css/sac/visitable.rb +0 -106
  98. data/lib/css/sac.rb +0 -14
  99. data/lib/css/stylesheet/rule.rb +0 -20
  100. data/lib/css/stylesheet/stylesheet.rb +0 -96
  101. data/lib/css/stylesheet.rb +0 -3
  102. data/lib/parser.y +0 -308
  103. data/lib/property_parser.y +0 -2346
  104. data/lib/property_parser.y.erb +0 -1321
  105. data/test/condition_test_case.rb +0 -6
  106. data/test/selector_test_case.rb +0 -20
  107. data/test/test_all.rb +0 -4
  108. data/test/test_attribute_condition.rb +0 -39
  109. data/test/test_begin_hyphen_condition.rb +0 -15
  110. data/test/test_child_selector.rb +0 -60
  111. data/test/test_class_condition.rb +0 -15
  112. data/test/test_combinator_condition.rb +0 -38
  113. data/test/test_condition.rb +0 -31
  114. data/test/test_conditional_selector.rb +0 -44
  115. data/test/test_descendant_selector.rb +0 -64
  116. data/test/test_element_selector.rb +0 -31
  117. data/test/test_error_recovery.rb +0 -28
  118. data/test/test_id_condition.rb +0 -16
  119. data/test/test_lexeme.rb +0 -39
  120. data/test/test_lexical_color.rb +0 -49
  121. data/test/test_lexical_function.rb +0 -31
  122. data/test/test_lexical_ident.rb +0 -30
  123. data/test/test_lexical_number.rb +0 -68
  124. data/test/test_lexical_string.rb +0 -23
  125. data/test/test_lexical_unit.rb +0 -106
  126. data/test/test_lexical_uri.rb +0 -28
  127. data/test/test_one_of_condition.rb +0 -15
  128. data/test/test_parse_error.rb +0 -199
  129. data/test/test_property_parser.rb +0 -593
  130. data/test/test_selector_as_string.rb +0 -94
  131. data/test/test_selector_parser.rb +0 -178
  132. data/test/test_sibling_selector.rb +0 -42
  133. data/test/test_simple_selector.rb +0 -9
  134. data/test/test_specificity.rb +0 -76
  135. data/test/test_stylesheet.rb +0 -95
  136. data/test/test_token.rb +0 -24
  137. data/test/test_tokenizer.rb +0 -127
  138. data/test/test_xpath.rb +0 -105
@@ -1,187 +0,0 @@
1
- require "css/sac/lexeme"
2
- require "css/sac/token"
3
-
4
- module CSS
5
- module SAC
6
- class Tokenizer
7
- def initialize(&block)
8
- @lexemes = []
9
- @macros = {}
10
-
11
- # http://www.w3.org/TR/CSS21/syndata.html
12
- macro(:h, /([0-9a-f])/ )
13
- macro(:nonascii, /([\200-\377])/ )
14
- macro(:nl, /(\n|\r\n|\r|\f)/ )
15
- macro(:unicode, /(\\#{m(:h)}{1,6}(\r\n|[ \t\r\n\f])?)/ )
16
- macro(:escape, /(#{m(:unicode)}|\\[^\r\n\f0-9a-f])/ )
17
- macro(:nmstart, /([_a-z]|#{m(:nonascii)}|#{m(:escape)})/ )
18
- macro(:nmchar, /([_a-z0-9-]|#{m(:nonascii)}|#{m(:escape)})/ )
19
- macro(:string1, /(\"([^\n\r\f\\\"]|\\#{m(:nl)}|#{m(:escape)})*\")/ )
20
- macro(:string2, /(\'([^\n\r\f\\']|\\#{m(:nl)}|#{m(:escape)})*\')/ )
21
- macro(:invalid1, /(\"([^\n\r\f\\\"]|\\#{m(:nl)}|#{m(:escape)})*)/ )
22
- macro(:invalid2, /(\'([^\n\r\f\\']|\\#{m(:nl)}|#{m(:escape)})*)/ )
23
- macro(:comment, /(\/\*[^*]*\*+([^\/*][^*]*\*+)*\/)/ )
24
- macro(:ident, /(-?#{m(:nmstart)}#{m(:nmchar)}*)/ )
25
- macro(:name, /(#{m(:nmchar)}+)/ )
26
- macro(:num, /([0-9]+|[0-9]*\.[0-9]+)/ )
27
- macro(:string, /(#{m(:string1)}|#{m(:string2)})/ )
28
- macro(:invalid, /(#{m(:invalid1)}|#{m(:invalid2)})/ )
29
- macro(:url, /(([!#\$%&*-~]|#{m(:nonascii)}|#{m(:escape)})*)/ )
30
- macro(:s, /([ \t\r\n\f]+)/ )
31
- macro(:w, /(#{m(:s)}?)/ )
32
- macro(:A, /(a|\\0{0,4}(41|61)(\r\n|[ \t\r\n\f])?)/ )
33
- macro(:C, /(c|\\0{0,4}(43|63)(\r\n|[ \t\r\n\f])?)/ )
34
- macro(:D, /(d|\\0{0,4}(44|64)(\r\n|[ \t\r\n\f])?)/ )
35
- macro(:E, /(e|\\0{0,4}(45|65)(\r\n|[ \t\r\n\f])?)/ )
36
- macro(:G, /(g|\\0{0,4}(47|67)(\r\n|[ \t\r\n\f])?|\\g)/ )
37
- macro(:H, /(h|\\0{0,4}(48|68)(\r\n|[ \t\r\n\f])?|\\h)/ )
38
- macro(:I, /(i|\\0{0,4}(49|69)(\r\n|[ \t\r\n\f])?|\\i)/ )
39
- macro(:K, /(k|\\0{0,4}(4b|6b)(\r\n|[ \t\r\n\f])?|\\k)/ )
40
- macro(:M, /(m|\\0{0,4}(4d|6d)(\r\n|[ \t\r\n\f])?|\\m)/ )
41
- macro(:N, /(n|\\0{0,4}(4e|6e)(\r\n|[ \t\r\n\f])?|\\n)/ )
42
- macro(:O, /(o|\\0{0,4}(51|71)(\r\n|[ \t\r\n\f])?|\\o)/ )
43
- macro(:P, /(p|\\0{0,4}(50|70)(\r\n|[ \t\r\n\f])?|\\p)/ )
44
- macro(:R, /(r|\\0{0,4}(52|72)(\r\n|[ \t\r\n\f])?|\\r)/ )
45
- macro(:S, /(s|\\0{0,4}(53|73)(\r\n|[ \t\r\n\f])?|\\s)/ )
46
- macro(:T, /(t|\\0{0,4}(54|74)(\r\n|[ \t\r\n\f])?|\\t)/ )
47
- macro(:X, /(x|\\0{0,4}(58|78)(\r\n|[ \t\r\n\f])?|\\x)/ )
48
- macro(:Z, /(z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z)/ )
49
-
50
- #token :COMMENT do |patterns|
51
- # patterns << /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//
52
- # patterns << /#{m(:s)}+\/\*[^*]*\*+([^\/*][^*]*\*+)*\//
53
- #end
54
-
55
- token(:LBRACE, /#{m(:w)}\{/)
56
- token(:PLUS, /#{m(:w)}\+/)
57
- token(:GREATER, /#{m(:w)}>/)
58
- token(:COMMA, /#{m(:w)},/)
59
-
60
- token(:S, /#{m(:s)}/)
61
-
62
- #token :URI do |patterns|
63
- # patterns << /url\(#{m(:w)}#{m(:string)}#{m(:w)}\)/
64
- # patterns << /url\(#{m(:w)}#{m(:url)}#{m(:w)}\)/
65
- #end
66
-
67
- token(:FUNCTION, /#{m(:ident)}\(/)
68
- token(:IDENT, /#{m(:ident)}/)
69
-
70
- token(:CDO, /<!--/)
71
- token(:CDC, /-->/)
72
- token(:INCLUDES, /~=/)
73
- token(:DASHMATCH, /\|=/)
74
- #token(:STRING, /#{m(:string)}/)
75
- token(:INVALID, /#{m(:invalid)}/)
76
- token(:HASH, /##{m(:name)}/)
77
- token(:IMPORT_SYM, /@#{m(:I)}#{m(:M)}#{m(:P)}#{m(:O)}#{m(:R)}#{m(:T)}/)
78
- token(:PAGE_SYM, /@#{m(:P)}#{m(:A)}#{m(:G)}#{m(:E)}/)
79
- token(:MEDIA_SYM, /@#{m(:M)}#{m(:E)}#{m(:D)}#{m(:I)}#{m(:A)}/)
80
- token(:CHARSET_SYM, /@#{m(:C)}#{m(:H)}#{m(:A)}#{m(:R)}#{m(:S)}#{m(:E)}#{m(:T)}/)
81
- token(:IMPORTANT_SYM, /!(#{m(:w)}|#{m(:comment)})*#{m(:I)}#{m(:M)}#{m(:P)}#{m(:O)}#{m(:R)}#{m(:T)}#{m(:A)}#{m(:N)}#{m(:T)}/)
82
- token(:EMS, /#{m(:num)}#{m(:E)}#{m(:M)}/)
83
- token(:EXS, /#{m(:num)}#{m(:E)}#{m(:X)}/)
84
-
85
- token :LENGTH do |patterns|
86
- patterns << /#{m(:num)}#{m(:P)}#{m(:X)}/
87
- patterns << /#{m(:num)}#{m(:C)}#{m(:M)}/
88
- patterns << /#{m(:num)}#{m(:M)}#{m(:M)}/
89
- patterns << /#{m(:num)}#{m(:I)}#{m(:N)}/
90
- patterns << /#{m(:num)}#{m(:P)}#{m(:T)}/
91
- patterns << /#{m(:num)}#{m(:P)}#{m(:C)}/
92
- end
93
-
94
- token :ANGLE do |patterns|
95
- patterns << /#{m(:num)}#{m(:D)}#{m(:E)}#{m(:G)}/
96
- patterns << /#{m(:num)}#{m(:R)}#{m(:A)}#{m(:D)}/
97
- patterns << /#{m(:num)}#{m(:G)}#{m(:R)}#{m(:A)}#{m(:D)}/
98
- end
99
-
100
- token :TIME do |patterns|
101
- patterns << /#{m(:num)}#{m(:M)}#{m(:S)}/
102
- patterns << /#{m(:num)}#{m(:S)}/
103
- end
104
-
105
- token :FREQ do |patterns|
106
- patterns << /#{m(:num)}#{m(:H)}#{m(:Z)}/
107
- patterns << /#{m(:num)}#{m(:K)}#{m(:H)}#{m(:Z)}/
108
- end
109
-
110
- token(:DIMENSION, /#{m(:num)}#{m(:ident)}/)
111
- token(:PERCENTAGE, /#{m(:num)}%/)
112
- token(:NUMBER, /#{m(:num)}/)
113
-
114
-
115
- yield self if block_given?
116
- end
117
-
118
- def tokenize(input_data)
119
- tokens = []
120
- pos = 0
121
-
122
- comment_pattern = /\/\*.*?\*\//m
123
- comments = input_data.scan(comment_pattern)
124
- non_comments = input_data.split(comment_pattern)
125
-
126
- # Handle a small edge case, if our CSS is *only* comments,
127
- # the split, zip, scan trick won't work
128
- if non_comments.length == 0
129
- tokens = comments.map { |x| Token.new(:COMMENT, x, nil) }
130
- else
131
- non_comments.zip(comments).each do |non_comment, comment|
132
- non_comment.split(/url\([^\)]*\)/m).zip(
133
- non_comment.scan(/url\([^\)]*\)/m)
134
- ).each do |non_url, url|
135
- non_url.split(/"[^"]*"|'[^']*'/m).zip(
136
- non_url.scan(/"[^"]*"|'[^']*'/m)
137
- ).each do |non_string, quoted_string|
138
- if non_string.length > 0 && non_string =~ /\A\s*\Z/m
139
- tokens << Token.new(:S, non_string, nil)
140
- else
141
- non_string.split(/[ \t\r\n\f]*(?![{}+>]*)/m).zip(
142
- non_string.scan(/[ \t\r\n\f]*(?![{}+>]*)/m)
143
- ).each do |string, whitespace|
144
- until string.empty?
145
- token = nil
146
- @lexemes.each do |lexeme|
147
- match = lexeme.pattern.match(string)
148
- if match
149
- token = Token.new(lexeme.name, match.to_s, pos)
150
- break
151
- end
152
- end
153
-
154
- token ||= DelimiterToken.new(/^./.match(string).to_s, pos)
155
-
156
- tokens << token
157
- string = string.slice(Range.new(token.value.length, -1))
158
- pos += token.value.length
159
- end
160
- tokens << Token.new(:S, whitespace, nil) if whitespace
161
- end
162
- end
163
- tokens << Token.new(:STRING, quoted_string, nil) if quoted_string
164
- end
165
- tokens << Token.new(:URI, url, nil) if url
166
- end
167
- tokens << Token.new(:COMMENT, comment, nil) if comment
168
- end
169
- end
170
-
171
- tokens
172
- end
173
-
174
- private
175
-
176
- def token(name, pattern=nil, &block)
177
- @lexemes << Lexeme.new(name, pattern, &block)
178
- end
179
-
180
- def macro(name, regex=nil)
181
- regex ? @macros[name] = regex : @macros[name].source
182
- end
183
-
184
- alias :m :macro
185
- end
186
- end
187
- end
@@ -1,106 +0,0 @@
1
- module CSS
2
- module SAC
3
- module Visitable # :nodoc:
4
- # Based off the visitor pattern from RubyGarden
5
- def accept(visitor, &block)
6
- klass = self.class.ancestors.find { |klass|
7
- visitor.respond_to?("visit_#{klass.name.split(/::/)[-1]}")
8
- }
9
-
10
- if klass
11
- visitor.send(:"visit_#{klass.name.split(/::/)[-1]}", self, &block)
12
- else
13
- raise "No visitor for '#{self.class}'"
14
- end
15
- end
16
- end
17
-
18
- class MatchesVisitor
19
- def initialize(node)
20
- @node = node
21
- end
22
-
23
- def accept(target)
24
- target.accept(self)
25
- end
26
-
27
- def visit_SimpleSelector(o)
28
- return false if @node.nil?
29
- return false unless @node.respond_to?(:name)
30
- true
31
- end
32
-
33
- def visit_ElementSelector(o)
34
- return false unless visit_SimpleSelector(o) # *sigh*
35
- o.name == @node.name
36
- end
37
-
38
- def visit_ChildSelector(o)
39
- return false unless visit_SimpleSelector(o) # *sigh*
40
- return false unless @node.respond_to?(:parent)
41
- return false if @node.parent.nil?
42
- return false unless o.selector.accept(self)
43
- @node = @node.parent
44
- o.parent.accept(self)
45
- end
46
-
47
- def visit_DescendantSelector(o)
48
- return false unless visit_SimpleSelector(o) # *sigh*
49
- return false unless @node.respond_to?(:parent)
50
- return false if @node.parent.nil?
51
- return false unless o.selector.accept(self)
52
-
53
- @node = @node.parent
54
- loop {
55
- return true if o.ancestor.accept(self)
56
- return false unless @node.respond_to?(:parent)
57
- @node = @node.parent
58
- }
59
- end
60
-
61
- def visit_SiblingSelector(o)
62
- return false unless visit_SimpleSelector(o) # *sigh*
63
- return false unless @node.respond_to?(:next_sibling)
64
- return false if @node.next_sibling.nil?
65
- return false unless o.selector.accept(self)
66
- @node = @node.next_sibling
67
- o.sibling.accept(self)
68
- end
69
-
70
- def visit_ConditionalSelector(o)
71
- return false unless visit_SimpleSelector(o) # *sigh*
72
- [o.selector, o.condition].compact.all? { |x|
73
- x.accept(self)
74
- }
75
- end
76
-
77
- def visit_AttributeCondition(o)
78
- return false unless @node.respond_to?(:attributes)
79
- return false unless @node.attributes[o.local_name]
80
- @node.attributes[o.local_name] == o.value
81
- end
82
-
83
- def visit_BeginHyphenCondition(o)
84
- return false unless @node.respond_to?(:attributes)
85
- return false unless @node.attributes[o.local_name]
86
- @node.attributes[o.local_name] =~ /^#{o.value}/
87
- end
88
-
89
- def visit_CombinatorCondition(o)
90
- o.first.accept(self) && o.second.accept(self)
91
- end
92
-
93
- def visit_OneOfCondition(o)
94
- return false unless @node.respond_to?(:attributes)
95
- return false unless @node.attributes[o.local_name]
96
- @node.attributes[o.local_name].split.any? { |attribute|
97
- attribute == o.value
98
- }
99
- end
100
-
101
- def visit_PseudoClassCondition(o)
102
- false # Fuck pseudo classes. --Aaron
103
- end
104
- end
105
- end
106
- end
data/lib/css/sac.rb DELETED
@@ -1,14 +0,0 @@
1
- require "css/sac/parser"
2
- require "css/stylesheet"
3
-
4
- module CSS
5
- module SAC
6
- class << self
7
- def parse(text)
8
- parser = CSS::SAC::Parser.new
9
- parser.parse(text)
10
- parser
11
- end
12
- end
13
- end
14
- end
@@ -1,20 +0,0 @@
1
- require 'set'
2
- module CSS
3
- class StyleSheet
4
- class Rule
5
- include Comparable
6
-
7
- attr_accessor :selector, :properties, :index
8
- def initialize(selector, index, properties = [])
9
- @selector = selector
10
- @properties = Set.new(properties)
11
- @index = index
12
- end
13
-
14
- def <=>(other)
15
- comp = selector.specificity <=> other.selector.specificity
16
- comp == 0 ? index <=> other.index : comp
17
- end
18
- end
19
- end
20
- end
@@ -1,96 +0,0 @@
1
- module CSS
2
- class StyleSheet < CSS::SAC::DocumentHandler
3
- attr_reader :rules
4
-
5
- def initialize(sac)
6
- @sac = sac
7
- @rules = []
8
- @current_rules = []
9
- @selector_index = 0
10
- end
11
-
12
- def start_selector(selectors)
13
- selectors.each { |selector|
14
- @current_rules << Rule.new(selector, @selector_index)
15
- }
16
- end
17
-
18
- def end_selector(selectors)
19
- @rules += @current_rules
20
- @current_rules = []
21
- @selector_index += 1
22
- reduce!
23
- end
24
-
25
- def find_rule(rule)
26
- rule = self.create_rule(rule) if rule.is_a?(String)
27
- rules.find { |x| x.selector == rule.selector }
28
- end
29
- alias :[] :find_rule
30
-
31
- # Find all rules used in +hpricot_document+
32
- def find_all_rules_matching(hpricot_document)
33
- used_rules = []
34
- hpricot_document.search('//').each do |node|
35
- if matching = (rules_matching(node))
36
- used_rules += matching
37
- end
38
- end
39
- used_rules.uniq
40
- end
41
-
42
- # Find all rules that match +node+. +node+ must quack like an Hpricot
43
- # node.
44
- def rules_matching(node)
45
- rules.find_all { |rule|
46
- rule.selector =~ node
47
- }
48
- end
49
- alias :=~ :rules_matching
50
-
51
- def create_rule(rule)
52
- Rule.new(@sac.parse_rule(rule).first, @selector_index += 1)
53
- end
54
-
55
- def property(name, value, important)
56
- @current_rules.each { |selector|
57
- selector.properties << [name, value, important]
58
- }
59
- end
60
-
61
- # Get a hash of rules by property
62
- def rules_by_property
63
- rules_by_property = Hash.new { |h,k| h[k] = [] }
64
- @rules.each { |sel|
65
- props = sel.properties.to_a.sort_by { |x| x.hash } # HACK?
66
- rules_by_property[props] << sel
67
- }
68
- rules_by_property
69
- end
70
-
71
- def to_css
72
- rules_by_property.map do |properties, rules|
73
- rules.map { |rule| rule.selector.to_css }.sort.join(', ') + " {\n" +
74
- properties.map { |key,value,important|
75
- # Super annoying. If the property is font-family, its supposed to
76
- # be commas
77
- join_val = ('font-family' == key) ? ', ' : ' '
78
- values = [value].flatten.join(join_val)
79
- "#{key}:#{values}#{important ? ' !important' : ''};"
80
- }.join("\n") + "\n}"
81
- end.sort.join("\n")
82
- end
83
-
84
- private
85
- # Remove duplicate rules
86
- def reduce!
87
- unique_rules = {}
88
- @rules.each do |rule|
89
- (unique_rules[rule.selector] ||= rule).properties += rule.properties
90
- end
91
- @rules = unique_rules.values
92
- self
93
- end
94
- end
95
- end
96
-
@@ -1,3 +0,0 @@
1
- require "css/stylesheet/stylesheet"
2
- require "css/stylesheet/rule"
3
-