cloudhead-less 0.8.12 → 1.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.
- data/README.md +10 -1
- data/Rakefile +21 -2
- data/VERSION +1 -1
- data/bin/lessc +0 -8
- data/less.gemspec +48 -15
- data/lib/less/command.rb +24 -25
- data/lib/less/engine/builder.rb +13 -0
- data/lib/less/engine/less.tt +379 -0
- data/lib/less/engine/nodes/element.rb +153 -0
- data/lib/less/engine/nodes/entity.rb +59 -0
- data/lib/less/engine/nodes/function.rb +61 -0
- data/lib/less/engine/nodes/literal.rb +132 -0
- data/lib/less/engine/nodes/property.rb +120 -0
- data/lib/less/engine/nodes/selector.rb +39 -0
- data/lib/less/engine/nodes.rb +8 -0
- data/lib/less/engine/parser.rb +3854 -0
- data/lib/less/engine.rb +42 -139
- data/lib/less.rb +65 -10
- data/spec/command_spec.rb +2 -6
- data/spec/css/accessors-1.0.css +20 -0
- data/spec/css/big-1.0.css +3770 -0
- data/spec/css/comments-1.0.css +11 -0
- data/spec/css/css-1.0.css +40 -0
- data/spec/css/functions-1.0.css +8 -0
- data/spec/css/import-1.0.css +13 -0
- data/spec/css/mixins-1.0.css +30 -0
- data/spec/css/operations-1.0.css +30 -0
- data/spec/css/rulesets-1.0.css +19 -0
- data/spec/css/scope-1.0.css +16 -0
- data/spec/css/strings-1.0.css +14 -0
- data/spec/css/variables-1.0.css +7 -0
- data/spec/css/whitespace-1.0.css +11 -0
- data/spec/engine_spec.rb +66 -18
- data/spec/less/accessors-1.0.less +20 -0
- data/spec/less/big-1.0.less +4810 -0
- data/spec/less/colors-1.0.less +0 -0
- data/spec/less/comments-1.0.less +46 -0
- data/spec/less/css-1.0.less +82 -0
- data/spec/less/exceptions/mixed-units-error.less +0 -0
- data/spec/less/exceptions/name-error-1.0.less +0 -0
- data/spec/less/exceptions/syntax-error-1.0.less +0 -0
- data/spec/less/functions-1.0.less +6 -0
- data/spec/less/import/import-test-a.less +2 -0
- data/spec/less/import/import-test-b.less +8 -0
- data/spec/less/import/import-test-c.less +5 -0
- data/spec/less/import-1.0.less +7 -0
- data/spec/less/mixins-1.0.less +43 -0
- data/spec/less/operations-1.0.less +39 -0
- data/spec/less/rulesets-1.0.less +30 -0
- data/spec/less/scope-1.0.less +33 -0
- data/spec/less/strings-1.0.less +14 -0
- data/spec/less/variables-1.0.less +16 -0
- data/spec/less/whitespace-1.0.less +21 -0
- data/spec/spec.css +81 -23
- data/spec/spec.less +3 -4
- data/spec/spec_helper.rb +4 -1
- metadata +46 -13
- data/lib/less/tree.rb +0 -82
- data/spec/css/less-0.8.10.css +0 -30
- data/spec/css/less-0.8.11.css +0 -31
- data/spec/css/less-0.8.12.css +0 -28
- data/spec/css/less-0.8.5.css +0 -24
- data/spec/css/less-0.8.6.css +0 -24
- data/spec/css/less-0.8.7.css +0 -24
- data/spec/css/less-0.8.8.css +0 -25
- data/spec/tree_spec.rb +0 -5
@@ -0,0 +1,153 @@
|
|
1
|
+
module Less
|
2
|
+
module Node
|
3
|
+
#
|
4
|
+
# Element
|
5
|
+
#
|
6
|
+
# div {...}
|
7
|
+
#
|
8
|
+
# TODO: Look into making @rules its own hash-like class
|
9
|
+
# TODO: Look into whether selector should be child by default
|
10
|
+
#
|
11
|
+
class Element < ::String
|
12
|
+
include Enumerable
|
13
|
+
include Entity
|
14
|
+
|
15
|
+
attr_accessor :rules, :selector, :partial, :file
|
16
|
+
|
17
|
+
def initialize name = "", selector = ''
|
18
|
+
super name
|
19
|
+
|
20
|
+
@partial = false
|
21
|
+
@rules = [] # Holds all the nodes under this element's hierarchy
|
22
|
+
@selector = Selector[selector.strip].new # descendant | child | adjacent
|
23
|
+
end
|
24
|
+
|
25
|
+
def class?; self =~ /^\./ end
|
26
|
+
def id?; self =~ /^#/ end
|
27
|
+
def universal?; self == '*' end
|
28
|
+
|
29
|
+
def tag?
|
30
|
+
not id? || class? || universal?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Top-most node?
|
34
|
+
def root?
|
35
|
+
self == '' && parent.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
def empty?
|
39
|
+
@rules.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
def leaf?
|
43
|
+
elements.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Accessors for the different nodes in @rules
|
48
|
+
#
|
49
|
+
def identifiers; @rules.select {|r| r.kind_of? Property } end
|
50
|
+
def properties; @rules.select {|r| r.instance_of? Property } end
|
51
|
+
def variables; @rules.select {|r| r.instance_of? Variable } end
|
52
|
+
def elements; @rules.select {|r| r.instance_of? Element } end
|
53
|
+
|
54
|
+
# Select a child element
|
55
|
+
# TODO: Implement full selector syntax
|
56
|
+
def [] key
|
57
|
+
@rules.find {|i| i.to_s == key }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Same as above, except with a specific selector
|
61
|
+
# TODO: clean this up or implement it differently
|
62
|
+
def descend selector, element
|
63
|
+
if selector.is_a? Child
|
64
|
+
s = self[element].selector
|
65
|
+
self[element] if s.is_a? Child or s.is_a? Descendant
|
66
|
+
elsif selector.is_a? Descendant
|
67
|
+
self[element]
|
68
|
+
else
|
69
|
+
self[element] if self[element].selector.class == selector.class
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Add an arbitrary node to this element
|
75
|
+
#
|
76
|
+
def << obj
|
77
|
+
if obj.kind_of? Node::Entity
|
78
|
+
obj.parent = self
|
79
|
+
@rules << obj
|
80
|
+
else
|
81
|
+
raise ArgumentError, "argument can't be a #{obj.class}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def last; elements.last end
|
86
|
+
def first; elements.first end
|
87
|
+
def to_s; super end
|
88
|
+
|
89
|
+
# Entry point for the css conversion
|
90
|
+
def to_css path = []
|
91
|
+
path << @selector.to_css << self unless root?
|
92
|
+
|
93
|
+
content = properties.map do |i|
|
94
|
+
' ' * 2 + i.to_css
|
95
|
+
end.compact.reject(&:empty?) * "\n"
|
96
|
+
|
97
|
+
content = content.include?("\n") ?
|
98
|
+
"\n#{content}\n" : " #{content.strip} "
|
99
|
+
ruleset = !content.strip.empty??
|
100
|
+
"#{path.reject(&:empty?).join.strip} {#{content}}\n" : ""
|
101
|
+
|
102
|
+
css = ruleset + elements.map do |i|
|
103
|
+
i.to_css(path)
|
104
|
+
end.reject(&:empty?).join
|
105
|
+
path.pop; path.pop
|
106
|
+
css
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Find the nearest variable in the hierarchy or raise a NameError
|
111
|
+
#
|
112
|
+
def nearest ident
|
113
|
+
ary = ident =~ /^[.#]/ ? :elements : :variables
|
114
|
+
path.map do |node|
|
115
|
+
node.send(ary).find {|i| i.to_s == ident }
|
116
|
+
end.compact.first.tap do |result|
|
117
|
+
raise VariableNameError, ident unless result
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def each path = [], &blk
|
122
|
+
#
|
123
|
+
# Traverse the whole tree, returning each leaf or branch (recursive)
|
124
|
+
#
|
125
|
+
###
|
126
|
+
# Aside from the key & value, we yield the full path of the leaf,
|
127
|
+
# aswell as the branch which contains it (self).
|
128
|
+
# Note that in :branch mode, we only return 'twigs', branches which contain leaves.
|
129
|
+
#
|
130
|
+
elements.each do |element|
|
131
|
+
path << element
|
132
|
+
yield element, path if element.leaf?
|
133
|
+
element.each path, &blk
|
134
|
+
path.pop
|
135
|
+
end
|
136
|
+
self
|
137
|
+
end
|
138
|
+
alias :traverse :each
|
139
|
+
|
140
|
+
def inspect depth = 0
|
141
|
+
indent = lambda {|i| '. ' * i }
|
142
|
+
put = lambda {|ary| ary.map {|i| indent[ depth + 1 ] + i.inspect } * "\n"}
|
143
|
+
|
144
|
+
(root?? "\n" : "") + [
|
145
|
+
indent[ depth ] + (self == '' ? '*' : self.to_s),
|
146
|
+
put[ properties ],
|
147
|
+
put[ variables ],
|
148
|
+
elements.map {|i| i.inspect( depth + 1 ) } * "\n"
|
149
|
+
].reject(&:empty?).join("\n") + "\n" + indent[ depth ]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Less
|
2
|
+
#
|
3
|
+
# Node::Entity
|
4
|
+
#
|
5
|
+
# TODO: Use delegate class -> @rules
|
6
|
+
#
|
7
|
+
# Class hierarchy
|
8
|
+
#
|
9
|
+
# - Entity
|
10
|
+
# - Element
|
11
|
+
# - Entity
|
12
|
+
# - Function
|
13
|
+
# - Keyword
|
14
|
+
# - Literal
|
15
|
+
# - Color
|
16
|
+
# - Number
|
17
|
+
# - String
|
18
|
+
# - FontFamily
|
19
|
+
# - Property
|
20
|
+
# - Variable
|
21
|
+
#
|
22
|
+
module Node
|
23
|
+
module Entity
|
24
|
+
attr_accessor :parent
|
25
|
+
|
26
|
+
def initialize value, parent = nil
|
27
|
+
super value
|
28
|
+
@parent = parent
|
29
|
+
end
|
30
|
+
|
31
|
+
def path node = self
|
32
|
+
path = []
|
33
|
+
while node do
|
34
|
+
path << node
|
35
|
+
node = node.parent
|
36
|
+
end
|
37
|
+
path
|
38
|
+
end
|
39
|
+
|
40
|
+
def root
|
41
|
+
path.last
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect; to_s end
|
45
|
+
def to_css; to_s end
|
46
|
+
def to_s; super end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Anonymous < ::String
|
50
|
+
include Entity
|
51
|
+
end
|
52
|
+
|
53
|
+
class Operator < ::String
|
54
|
+
def to_ruby
|
55
|
+
self
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Less
|
2
|
+
module Functions
|
3
|
+
def rgb *rgb
|
4
|
+
rgba rgb, 1.0
|
5
|
+
end
|
6
|
+
|
7
|
+
def hsl *args
|
8
|
+
hsla args, 1.0
|
9
|
+
end
|
10
|
+
|
11
|
+
def rgba *rgba
|
12
|
+
r, g, b, a = rgba.flatten
|
13
|
+
hex = [r, g, b].inject("") do |str, c|
|
14
|
+
c = c.to_i.to_s(16)
|
15
|
+
c = '0' + c if c.length < 2
|
16
|
+
str + c
|
17
|
+
end
|
18
|
+
Node::Color.new hex, a
|
19
|
+
end
|
20
|
+
|
21
|
+
def hsla h, s, l, a = 1.0
|
22
|
+
m2 = ( l <= 0.5 ) ? l * ( s + 1 ) : l + s - l * s
|
23
|
+
m1 = l * 2 - m2;
|
24
|
+
|
25
|
+
hue = lambda do |h|
|
26
|
+
h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h)
|
27
|
+
if h * 6 < 1 then m1 + (m2 - m1) * h * 6
|
28
|
+
elsif h * 2 < 1 then m2
|
29
|
+
elsif h * 3 < 2 then m1 + (m2 - m1) * (2/3 - h) * 6
|
30
|
+
else m1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
rgba hue[ h + 1/3 ], hue[ h ], hue[ h - 1/3 ], a
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module Node
|
39
|
+
class Function < ::String
|
40
|
+
include Functions
|
41
|
+
include Entity
|
42
|
+
|
43
|
+
def initialize name, *args
|
44
|
+
@args = args.flatten
|
45
|
+
super name
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_css
|
49
|
+
self.evaluate.to_css
|
50
|
+
end
|
51
|
+
|
52
|
+
def evaluate
|
53
|
+
send self.to_sym, *@args
|
54
|
+
end
|
55
|
+
|
56
|
+
def method_missing meth, *args
|
57
|
+
Node::Anonymous.new("#{meth}(#{args.map(&:to_css) * ', '})")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module Less
|
2
|
+
module Node
|
3
|
+
module Literal
|
4
|
+
include Entity
|
5
|
+
|
6
|
+
def unit
|
7
|
+
nil
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
#
|
12
|
+
# rgb(255, 0, 0) #f0f0f0
|
13
|
+
#
|
14
|
+
class Color < DelegateClass(Fixnum)
|
15
|
+
include Literal
|
16
|
+
attr_reader :color
|
17
|
+
|
18
|
+
def initialize color = nil, opacity = 1.0
|
19
|
+
@color = if color.is_a? Array
|
20
|
+
rgba color
|
21
|
+
elsif color.is_a? ::String
|
22
|
+
color.delete! unit
|
23
|
+
(color * ( color.length < 6 ? 6 / color.length : 1 )).to_i 16
|
24
|
+
else
|
25
|
+
color
|
26
|
+
end
|
27
|
+
super @color.to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
def unit
|
31
|
+
'#'
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_css
|
35
|
+
unit + (self <= 0 ? '0' * 6 : self.to_i.to_s(16))
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_ruby
|
39
|
+
color
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
"#{unit}#{super}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def inspect; to_s end
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# 6 10px 125%
|
51
|
+
#
|
52
|
+
class Number < DelegateClass(Float)
|
53
|
+
include Literal
|
54
|
+
|
55
|
+
attr_accessor :unit
|
56
|
+
|
57
|
+
def initialize value, unit = nil
|
58
|
+
super value.to_f
|
59
|
+
@unit = (unit.nil? || unit.empty?) ? nil : unit
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
"#{super}#@unit"
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_ruby
|
67
|
+
self.to_f
|
68
|
+
end
|
69
|
+
|
70
|
+
def inspect
|
71
|
+
to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_css
|
75
|
+
"#{(self % 1).zero?? self.to_i.to_s : self.to_s}#@unit"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# "hello world"
|
81
|
+
#
|
82
|
+
class String < ::String
|
83
|
+
include Literal
|
84
|
+
|
85
|
+
attr_reader :quotes, :content
|
86
|
+
|
87
|
+
def initialize str
|
88
|
+
@quotes, @content = unless str.nil? or str.empty?
|
89
|
+
str.match(/('|")(.*?)(\1)/).captures rescue [nil, str]
|
90
|
+
else
|
91
|
+
[nil, ""]
|
92
|
+
end
|
93
|
+
super @content
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_css
|
97
|
+
"#@quotes#{@content}#@quotes"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class Font
|
102
|
+
include Literal
|
103
|
+
end
|
104
|
+
|
105
|
+
class FontFamily < Array
|
106
|
+
include Literal
|
107
|
+
|
108
|
+
def initialize family = []
|
109
|
+
super family
|
110
|
+
end
|
111
|
+
|
112
|
+
def to_css
|
113
|
+
self.map(&:to_css) * ', '
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# red small border-collapse
|
119
|
+
#
|
120
|
+
class Keyword < ::String
|
121
|
+
include Entity
|
122
|
+
|
123
|
+
def to_css
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
def inspect
|
128
|
+
"#{self}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Less
|
2
|
+
module Node
|
3
|
+
class Property < ::String
|
4
|
+
include Entity
|
5
|
+
|
6
|
+
attr_accessor :value
|
7
|
+
|
8
|
+
def initialize key, value = nil
|
9
|
+
super key
|
10
|
+
log "\n[new] #{self.class} `#{key}`\n"
|
11
|
+
@value = Expression.new(value ? [value] : [])
|
12
|
+
@eval = false # Store the first evaluation in here
|
13
|
+
end
|
14
|
+
|
15
|
+
def << token
|
16
|
+
log "[adding] to #{self.to_s}: '#{token.to_s}' <#{token.class}>\n"
|
17
|
+
token = Node::Anonymous.new(*token) unless token.is_a? Entity or token.is_a? Operator
|
18
|
+
@value << token
|
19
|
+
end
|
20
|
+
|
21
|
+
def empty?; !@value || @value.empty? end
|
22
|
+
def eval?; @eval end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
self + (empty?? "" : ": `#{value.map {|i| i.to_s } * ' | '}`")
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
# TODO: @eval and @value should be merged
|
33
|
+
def evaluate
|
34
|
+
@eval || @eval = value.evaluate
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_css
|
38
|
+
"#{self}: #{evaluate.to_css};"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Variable < Property
|
43
|
+
def initialize key, value = nil
|
44
|
+
super key.delete('@'), value
|
45
|
+
end
|
46
|
+
|
47
|
+
def inspect
|
48
|
+
"@#{super}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s
|
52
|
+
"@#{super}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_ruby
|
56
|
+
value.evaluate.to_ruby
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_css
|
60
|
+
value.evaluate.to_css
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Expression < Array
|
65
|
+
def operators; select {|i| i.is_a? Operator } end
|
66
|
+
def entities; select {|i| i.kind_of? Entity } end
|
67
|
+
def literals; select {|i| i.kind_of? Literal } end
|
68
|
+
|
69
|
+
def inspect
|
70
|
+
'[' + map {|i| i.inspect }.join(', ') + ']'
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_css
|
74
|
+
map {|i| i.to_css } * ' '
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Evaluates the expression and instantiates a new Literal with the result
|
79
|
+
# ex: [#111, +, #111] will evaluate to a Color node, with value #222
|
80
|
+
#
|
81
|
+
def evaluate
|
82
|
+
log "evaluating #{self}"
|
83
|
+
log "#{self.select{|i| i.is_a? Entity }}"
|
84
|
+
if size > 2 && (entities.size == operators.size + 1)
|
85
|
+
# Create a sub-expression with all the variables/properties evaluated
|
86
|
+
evaluated = Expression.new map {|e| e.respond_to?(:evaluate) ? e.evaluate : e }
|
87
|
+
|
88
|
+
unit = evaluated.literals.map do |node|
|
89
|
+
node.unit
|
90
|
+
end.compact.uniq.tap do |ary|
|
91
|
+
raise MixedUnitsError, self * ' ' if ary.size > 1
|
92
|
+
end.join
|
93
|
+
|
94
|
+
entity = evaluated.literals.find {|e| e.unit == unit } || evaluated.first
|
95
|
+
ruby = map {|e| e.to_ruby if e.respond_to? :to_ruby }
|
96
|
+
|
97
|
+
unless ruby.include? nil
|
98
|
+
log "ruby(#{unit}): " + ruby.join(' ') + "\n"
|
99
|
+
|
100
|
+
if entity
|
101
|
+
log "\n# => #{eval(ruby.join)} <#{entity.class}>\n"
|
102
|
+
entity.class.new(eval(ruby.join), *(unit if entity.class == Node::Number))
|
103
|
+
else
|
104
|
+
log "\n# => not evaluated"
|
105
|
+
first
|
106
|
+
end
|
107
|
+
else
|
108
|
+
log "some elements dont respond to to_ruby: #{ruby}"
|
109
|
+
self
|
110
|
+
end
|
111
|
+
elsif size == 1
|
112
|
+
log "not evaluating, size == 1"
|
113
|
+
first
|
114
|
+
else
|
115
|
+
self
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Less
|
2
|
+
module Node
|
3
|
+
class Selector < ::String
|
4
|
+
include Entity
|
5
|
+
|
6
|
+
Selectors = {
|
7
|
+
:Descendant => '',
|
8
|
+
:Child => '>',
|
9
|
+
:Adjacent => '+',
|
10
|
+
:Pseudo => ':',
|
11
|
+
:Sibling => '~'
|
12
|
+
}
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
super Selectors[ self.class.to_s.split('::').last.to_sym ]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.[] key
|
19
|
+
Node.const_get(Selectors.find {|k, v| v == key }.first)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Descendant < Selector
|
24
|
+
def to_css; " " end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Child < Selector
|
28
|
+
def to_css; " #{self} " end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Adjacent < Selector
|
32
|
+
def to_css; " #{self} " end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Pseudo < Selector
|
36
|
+
def to_css; self end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|