unboxed-less 1.2.12
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/CHANGELOG +62 -0
- data/LICENSE +179 -0
- data/README.md +48 -0
- data/Rakefile +60 -0
- data/VERSION +1 -0
- data/bin/lessc +92 -0
- data/lib/ext.rb +31 -0
- data/lib/less.rb +32 -0
- data/lib/less/command.rb +98 -0
- data/lib/less/engine.rb +55 -0
- data/lib/less/engine/grammar/common.tt +29 -0
- data/lib/less/engine/grammar/entity.tt +130 -0
- data/lib/less/engine/grammar/less.tt +326 -0
- data/lib/less/engine/nodes.rb +9 -0
- data/lib/less/engine/nodes/element.rb +278 -0
- data/lib/less/engine/nodes/entity.rb +79 -0
- data/lib/less/engine/nodes/function.rb +84 -0
- data/lib/less/engine/nodes/literal.rb +171 -0
- data/lib/less/engine/nodes/property.rb +231 -0
- data/lib/less/engine/nodes/ruleset.rb +12 -0
- data/lib/less/engine/nodes/selector.rb +44 -0
- data/spec/command_spec.rb +102 -0
- data/spec/css/accessors.css +18 -0
- data/spec/css/big.css +3768 -0
- data/spec/css/colors.css +14 -0
- data/spec/css/comments.css +9 -0
- data/spec/css/css-3.css +17 -0
- data/spec/css/css.css +50 -0
- data/spec/css/functions.css +6 -0
- data/spec/css/import.css +12 -0
- data/spec/css/lazy-eval.css +1 -0
- data/spec/css/mixins-args.css +32 -0
- data/spec/css/mixins.css +28 -0
- data/spec/css/operations.css +28 -0
- data/spec/css/parens.css +20 -0
- data/spec/css/rulesets.css +17 -0
- data/spec/css/scope.css +11 -0
- data/spec/css/selectors.css +13 -0
- data/spec/css/strings.css +12 -0
- data/spec/css/variables.css +7 -0
- data/spec/css/whitespace.css +7 -0
- data/spec/engine_spec.rb +112 -0
- data/spec/less/accessors.less +20 -0
- data/spec/less/big.less +4810 -0
- data/spec/less/colors.less +35 -0
- data/spec/less/comments.less +46 -0
- data/spec/less/css-3.less +45 -0
- data/spec/less/css.less +104 -0
- data/spec/less/exceptions/mixed-units-error.less +3 -0
- data/spec/less/exceptions/name-error-1.0.less +3 -0
- data/spec/less/exceptions/syntax-error-1.0.less +3 -0
- data/spec/less/functions.less +6 -0
- data/spec/less/hidden.less +25 -0
- data/spec/less/import.less +8 -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 +7 -0
- data/spec/less/import/import-test-d.css +1 -0
- data/spec/less/lazy-eval.less +6 -0
- data/spec/less/literal-css.less +11 -0
- data/spec/less/mixins-args.less +59 -0
- data/spec/less/mixins.less +43 -0
- data/spec/less/operations.less +39 -0
- data/spec/less/parens.less +26 -0
- data/spec/less/rulesets.less +30 -0
- data/spec/less/scope.less +32 -0
- data/spec/less/selectors.less +24 -0
- data/spec/less/strings.less +14 -0
- data/spec/less/variables.less +24 -0
- data/spec/less/whitespace.less +34 -0
- data/spec/spec.css +50 -0
- data/spec/spec_helper.rb +8 -0
- data/unboxed-less.gemspec +121 -0
- metadata +140 -0
@@ -0,0 +1,9 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'engine/nodes/entity'
|
4
|
+
require 'engine/nodes/function'
|
5
|
+
require 'engine/nodes/ruleset'
|
6
|
+
require 'engine/nodes/element'
|
7
|
+
require 'engine/nodes/property'
|
8
|
+
require 'engine/nodes/literal'
|
9
|
+
require 'engine/nodes/selector'
|
@@ -0,0 +1,278 @@
|
|
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
|
12
|
+
include Enumerable
|
13
|
+
include Entity
|
14
|
+
|
15
|
+
attr_accessor :rules, :selector, :file,
|
16
|
+
:set, :imported, :name
|
17
|
+
|
18
|
+
def initialize name = "", selector = ''
|
19
|
+
@name = name
|
20
|
+
@set, @imported = [], []
|
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?; name =~ /^\./ end
|
26
|
+
def id?; name =~ /^#/ end
|
27
|
+
def universal?; name == '*' end
|
28
|
+
|
29
|
+
def tag?
|
30
|
+
not id? || class? || universal?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Top-most node?
|
34
|
+
def root?
|
35
|
+
parent.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
def empty?
|
39
|
+
@rules.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
def leaf?
|
43
|
+
elements.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
# Group similar rulesets together
|
47
|
+
# This is horrible, horrible code,
|
48
|
+
# but it'll have to do until I find
|
49
|
+
# a proper way to do it.
|
50
|
+
def group
|
51
|
+
matched = false
|
52
|
+
stack, result = elements.dup, []
|
53
|
+
return self unless elements.size > 1
|
54
|
+
|
55
|
+
elements.each do
|
56
|
+
e = stack.first
|
57
|
+
result << e unless matched
|
58
|
+
|
59
|
+
matched = stack[1..-1].each do |ee|
|
60
|
+
if e.equiv? ee and e.elements.size == 0
|
61
|
+
self[e].set << ee
|
62
|
+
stack.shift
|
63
|
+
else
|
64
|
+
stack.shift
|
65
|
+
break false
|
66
|
+
end
|
67
|
+
end if stack.size > 1
|
68
|
+
end
|
69
|
+
@rules -= (elements - result)
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# Accessors for the different nodes in @rules
|
75
|
+
#
|
76
|
+
def identifiers; @rules.select {|r| r.kind_of? Property } end
|
77
|
+
def properties; @rules.select {|r| r.instance_of? Property } end
|
78
|
+
def variables; @rules.select {|r| r.instance_of? Variable } end
|
79
|
+
def elements; @rules.select {|r| r.kind_of? Element } end
|
80
|
+
def mixins; @rules.select {|r| r.instance_of? Mixin::Call} end
|
81
|
+
def parameters; [] end
|
82
|
+
|
83
|
+
# Select a child element
|
84
|
+
# TODO: Implement full selector syntax & merge with descend()
|
85
|
+
def [] key
|
86
|
+
case key
|
87
|
+
when Entity
|
88
|
+
@rules.find {|i| i.eql? key }
|
89
|
+
when String
|
90
|
+
@rules.find {|i| i.to_s == key }
|
91
|
+
else raise ArgumentError
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def == other
|
96
|
+
name == other.name
|
97
|
+
end
|
98
|
+
|
99
|
+
def eql? other
|
100
|
+
super and self.equiv? other
|
101
|
+
end
|
102
|
+
|
103
|
+
def equiv? other
|
104
|
+
rules.size == other.rules.size and
|
105
|
+
!rules.zip(other.rules).map do |a, b|
|
106
|
+
a.to_css == b.to_css
|
107
|
+
end.include?(false)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Same as above, except with a specific selector
|
111
|
+
# TODO: clean this up or implement it differently
|
112
|
+
def descend selector, element
|
113
|
+
if selector.is_a? Child
|
114
|
+
s = self[element.name].selector
|
115
|
+
self[element.name] if s.is_a? Child or s.is_a? Descendant
|
116
|
+
elsif selector.is_a? Descendant
|
117
|
+
self[element.name]
|
118
|
+
else
|
119
|
+
self[element.name] if self[element.name].selector.class == selector.class
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def mix arr = []
|
124
|
+
@rules += arr.map do |r|
|
125
|
+
n = r.copy
|
126
|
+
n.parent = self
|
127
|
+
n
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
# Add an arbitrary node to this element
|
133
|
+
#
|
134
|
+
def << obj
|
135
|
+
if obj.kind_of? Node::Entity
|
136
|
+
obj.parent = self
|
137
|
+
@rules << obj
|
138
|
+
else
|
139
|
+
raise ArgumentError, "argument can't be a #{obj.class}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def last; elements.last end
|
144
|
+
def first; elements.first end
|
145
|
+
def to_s; root?? '*' : name end
|
146
|
+
|
147
|
+
#
|
148
|
+
# Entry point for the css conversion
|
149
|
+
#
|
150
|
+
def to_css path = [], env = nil
|
151
|
+
path << @selector.to_css << name unless root?
|
152
|
+
|
153
|
+
# puts "to_css env: #{env ? env.variables : "nil"}"
|
154
|
+
|
155
|
+
content = (properties + mixins).map do |i|
|
156
|
+
' ' * 2 + i.to_css(env)
|
157
|
+
end.compact.reject(&:empty?) * "\n"
|
158
|
+
|
159
|
+
content = content.include?("\n") ? "\n#{content}\n" : " #{content.strip} "
|
160
|
+
ruleset = if is_a?(Mixin::Def)
|
161
|
+
content.strip
|
162
|
+
else
|
163
|
+
!content.strip.empty??
|
164
|
+
"#{[path.reject(&:empty?).join.strip,
|
165
|
+
*@set.map(&:name)].uniq * ', '} {#{content}}\n" : ""
|
166
|
+
end
|
167
|
+
|
168
|
+
css = ruleset + elements.
|
169
|
+
reject {|e| e.is_a? Mixin::Def }.map do |i|
|
170
|
+
i.to_css(path, env)
|
171
|
+
end.reject(&:empty?).join
|
172
|
+
2.times {path.pop}
|
173
|
+
css
|
174
|
+
end
|
175
|
+
|
176
|
+
#
|
177
|
+
# Find the nearest node in the hierarchy or raise a NameError
|
178
|
+
#
|
179
|
+
def nearest ident, type = nil
|
180
|
+
ary = type || ident =~ /^[.#]/ ? :elements : :variables
|
181
|
+
result = path.map do |node|
|
182
|
+
node.send(ary).find {|i| i.to_s == ident }
|
183
|
+
end.compact.first
|
184
|
+
raise VariableNameError, ("#{ident} in #{self.to_s}") unless result
|
185
|
+
result
|
186
|
+
end
|
187
|
+
|
188
|
+
#
|
189
|
+
# Traverse the whole tree, returning each leaf (recursive)
|
190
|
+
#
|
191
|
+
def each path = [], &blk
|
192
|
+
elements.each do |element|
|
193
|
+
path << element
|
194
|
+
yield element, path if element.leaf?
|
195
|
+
element.each path, &blk
|
196
|
+
path.pop
|
197
|
+
end
|
198
|
+
self
|
199
|
+
end
|
200
|
+
|
201
|
+
def inspect depth = 0
|
202
|
+
indent = lambda {|i| '. ' * i }
|
203
|
+
put = lambda {|ary| ary.map {|i| indent[ depth + 1 ] + i.inspect } * "\n"}
|
204
|
+
|
205
|
+
(root?? "\n" : "") + [
|
206
|
+
indent[ depth ] + self.to_s,
|
207
|
+
put[ variables ],
|
208
|
+
put[ properties ],
|
209
|
+
put[ mixins ],
|
210
|
+
elements.map {|i| i.inspect( depth + 1 ) } * "\n"
|
211
|
+
].reject(&:empty?).join("\n") + "\n" + indent[ depth ]
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
module Mixin
|
216
|
+
class Call
|
217
|
+
include Entity
|
218
|
+
|
219
|
+
def initialize mixin, params, parent
|
220
|
+
# puts "Initializing a Mixin::Call #{mixin}"
|
221
|
+
@mixin = mixin
|
222
|
+
self.parent = parent
|
223
|
+
@params = params.each do |e|
|
224
|
+
e.parent = self.parent
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def to_css env = nil
|
229
|
+
# puts "\n\n"
|
230
|
+
# puts "call .#{@mixin.name} #{@params} <#{@params.class}>"
|
231
|
+
@mixin.call(@params.map {|e| e.evaluate(env) })
|
232
|
+
end
|
233
|
+
|
234
|
+
def inspect
|
235
|
+
"#{@mixin.to_s} (#{@params})"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class Def < Element
|
240
|
+
attr_accessor :params
|
241
|
+
|
242
|
+
def initialize name, params = []
|
243
|
+
super name
|
244
|
+
@params = params.each do |param|
|
245
|
+
param.parent = self
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def call args = []
|
250
|
+
env = Element.new
|
251
|
+
|
252
|
+
@params.zip(args).each do |param, val|
|
253
|
+
env << (val ? Variable.new(param.to_s, Expression.new([val])) : param)
|
254
|
+
end
|
255
|
+
|
256
|
+
#b ? Node::Variable.new(a.to_s, Expression.new([b])) : a
|
257
|
+
|
258
|
+
# puts "#{self.inspect}"
|
259
|
+
# puts "env: #{env.variables} root?: #{env.root?}"
|
260
|
+
# puts "\nTOCSS"
|
261
|
+
to_css([], env)
|
262
|
+
end
|
263
|
+
|
264
|
+
def variables
|
265
|
+
params + super
|
266
|
+
end
|
267
|
+
|
268
|
+
def to_s
|
269
|
+
'.' + name
|
270
|
+
end
|
271
|
+
|
272
|
+
def to_css path, env
|
273
|
+
super(path, env)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Less
|
2
|
+
#
|
3
|
+
# Node::Entity
|
4
|
+
#
|
5
|
+
# Everything in the tree is an Entity
|
6
|
+
#
|
7
|
+
# Mixin/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
|
+
# TODO: Use delegate class -> @rules
|
23
|
+
#
|
24
|
+
module Node
|
25
|
+
module Entity
|
26
|
+
attr_accessor :parent
|
27
|
+
|
28
|
+
def initialize value, parent = nil
|
29
|
+
super value
|
30
|
+
@parent = parent
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Returns the path from any given node, to the root
|
35
|
+
#
|
36
|
+
# ex: ['color', 'p', '#header', 'body', '*']
|
37
|
+
#
|
38
|
+
def path node = self
|
39
|
+
path = []
|
40
|
+
while node do
|
41
|
+
path << node
|
42
|
+
node = node.parent
|
43
|
+
end
|
44
|
+
path
|
45
|
+
end
|
46
|
+
|
47
|
+
def root
|
48
|
+
path.last
|
49
|
+
end
|
50
|
+
|
51
|
+
def inspect; to_s end
|
52
|
+
def to_css; to_s end
|
53
|
+
def to_s; super end
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# An anonymous node, for all the 'other' stuff
|
58
|
+
# which doesn't need any specific functionality.
|
59
|
+
#
|
60
|
+
class Anonymous < String
|
61
|
+
include Entity
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# + * - /
|
66
|
+
#
|
67
|
+
class Operator < String
|
68
|
+
def to_ruby
|
69
|
+
self
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Paren < String
|
74
|
+
def to_ruby
|
75
|
+
self
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Less
|
2
|
+
#
|
3
|
+
# Functions useable from within the style-sheet go here
|
4
|
+
#
|
5
|
+
module Functions
|
6
|
+
def rgb *rgb
|
7
|
+
rgba rgb, 1.0
|
8
|
+
end
|
9
|
+
|
10
|
+
def hsl *args
|
11
|
+
hsla *[args, 1.0].flatten
|
12
|
+
end
|
13
|
+
|
14
|
+
#
|
15
|
+
# RGBA to Node::Color
|
16
|
+
#
|
17
|
+
def rgba *rgba
|
18
|
+
Node::Color.new *rgba.flatten
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# HSLA to RGBA
|
23
|
+
#
|
24
|
+
def hsla h, s, l, a = 1.0
|
25
|
+
m2 = ( l <= 0.5 ) ? l * ( s + 1 ) : l + s - l * s
|
26
|
+
m1 = l * 2 - m2;
|
27
|
+
|
28
|
+
hue = lambda do |h|
|
29
|
+
h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h)
|
30
|
+
if h * 6 < 1 then m1 + (m2 - m1) * h * 6
|
31
|
+
elsif h * 2 < 1 then m2
|
32
|
+
elsif h * 3 < 2 then m1 + (m2 - m1) * (2/3 - h) * 6
|
33
|
+
else m1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
rgba hue[ h + 1/3 ], hue[ h ], hue[ h - 1/3 ], a
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.available
|
41
|
+
self.instance_methods.map(&:to_sym)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module Node
|
46
|
+
#
|
47
|
+
# A CSS function, like rgb() or url()
|
48
|
+
#
|
49
|
+
# it calls functions from the Functions module
|
50
|
+
#
|
51
|
+
class Function < String
|
52
|
+
include Entity
|
53
|
+
include Functions
|
54
|
+
|
55
|
+
def initialize name, args
|
56
|
+
@args = if args.is_a? Array
|
57
|
+
args.map {|e| e.is_a?(Expression) ? e : Expression.new(e, self)}
|
58
|
+
else
|
59
|
+
[args]
|
60
|
+
end
|
61
|
+
|
62
|
+
super name
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_css env = nil
|
66
|
+
self.evaluate(env).to_css
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Call the function
|
71
|
+
#
|
72
|
+
# If the function isn't found, we just print it out,
|
73
|
+
# this is the case for url(), for example,
|
74
|
+
#
|
75
|
+
def evaluate context = nil
|
76
|
+
if Functions.available.include? self.to_sym
|
77
|
+
send to_sym, *@args
|
78
|
+
else
|
79
|
+
Node::Anonymous.new("#{to_sym}(#{@args.map(&:to_css) * ', '})")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|