xdissent-less 0.8.12

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ /*.css
2
+ *.tmproj
3
+ *.gem
4
+ pkg/
data/LICENSE ADDED
@@ -0,0 +1,179 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ Copyright (c) 2009 Alexis Sellier
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ LESS
2
+ ====
3
+ It's time CSS was done right – LESS is _leaner_ css.
4
+
5
+ Explained
6
+ ---------
7
+ LESS allows you to write CSS the way (I think) it was meant to, that is: with *variables*, *nested rules* and *mixins*!
8
+
9
+ ### Here's some example LESS code:
10
+
11
+ @dark: #110011;
12
+ .outline { border: 1px solid black }
13
+
14
+ .article {
15
+ a { text-decoration: none }
16
+ p { color: @dark }
17
+ .outline;
18
+ }
19
+
20
+ ### And the CSS output it produces:
21
+
22
+ .outline { border: 1px solid black }
23
+ .article a { text-decoration: none }
24
+ .article p { color: #110011 }
25
+ .article { border: 1px solid black }
26
+
27
+ If you have CSS nightmares, just
28
+ $ lessc style.less
29
+
30
+ For more information, see you at http://lesscss.org
data/Rakefile ADDED
@@ -0,0 +1,61 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |s|
4
+ s.name = "less"
5
+ s.authors = ["cloudhead"]
6
+ s.email = "alexis@cloudhead.net"
7
+ s.summary = "LESS compiler"
8
+ s.homepage = "http://www.lesscss.org"
9
+ s.description = "LESS is leaner CSS"
10
+ s.rubyforge_project = 'less'
11
+ end
12
+ rescue LoadError
13
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
14
+ end
15
+
16
+ # rubyforge
17
+ begin
18
+ require 'rake/contrib/sshpublisher'
19
+ namespace :rubyforge do
20
+
21
+ desc "Release gem and RDoc documentation to RubyForge"
22
+ task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
23
+
24
+ namespace :release do
25
+ desc "Publish RDoc to RubyForge."
26
+ task :docs => [:rdoc] do
27
+ config = YAML.load(
28
+ File.read(File.expand_path('~/.rubyforge/user-config.yml'))
29
+ )
30
+ options << '--line-numbers' << '--inline-source'
31
+ host = "#{config['username']}@rubyforge.org"
32
+ remote_dir = "/var/www/gforge-projects/the-perfect-gem/"
33
+ local_dir = 'rdoc'
34
+
35
+ Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
36
+ end
37
+ end
38
+ end
39
+ rescue LoadError
40
+ puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
41
+ end
42
+
43
+ begin
44
+ require 'spec/rake/spectask'
45
+
46
+ Spec::Rake::SpecTask.new("spec") do |t|
47
+ t.spec_files = FileList['spec/**/*_spec.rb']
48
+ t.spec_opts = ['--color', '--format=specdoc']
49
+ end
50
+
51
+ task :test do
52
+ Rake::Task['spec'].invoke
53
+ end
54
+
55
+ Spec::Rake::SpecTask.new("rcov_spec") do |t|
56
+ t.spec_files = FileList['spec/**/*_spec.rb']
57
+ t.spec_opts = ['--color']
58
+ t.rcov = true
59
+ t.rcov_opts = ['--exclude', '^spec,/gems/']
60
+ end
61
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.8.12
data/bin/lessc ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.dirname(__FILE__) + "/../lib"
4
+
5
+ require 'optparse'
6
+ require 'less'
7
+
8
+ CSS = '.css'
9
+
10
+ # Argument defaults
11
+ options = {
12
+ :watch => false,
13
+ :compress => false,
14
+ :debug => false,
15
+ :inheritance => :desc
16
+ }
17
+
18
+ # Get arguments
19
+ opts = OptionParser.new do |o|
20
+ o.banner = "usage: lessc source [destination] [--watch]"
21
+ o.separator ""
22
+
23
+ # Watch mode
24
+ o.on("-w", "--watch", "watch for changes") do
25
+ options[:watch] = true
26
+ end
27
+
28
+ # Child-type inheritance
29
+ o.on("-c", "--child", "nesting uses child-type inheritance") do
30
+ options[:chain] = :child
31
+ end
32
+
33
+ # Compression needs a proper algorithm
34
+ #
35
+ # o.on("-x", "--compress", "compress css file") do
36
+ # options[:compress] = true
37
+ # end
38
+
39
+ o.separator ""
40
+
41
+ # Help
42
+ o.on_tail("-h", "--help", "show this message") do
43
+ puts opts
44
+ exit
45
+ end
46
+
47
+ o.on_tail("-d", "--debug", "show full error messages") do
48
+ options[:debug] = true
49
+ end
50
+
51
+ # Version
52
+ o.on_tail("-v", "--version", "show version") do
53
+ puts "lessc " + Less.version
54
+ exit
55
+ end
56
+ end
57
+
58
+ opts.parse! # Parse arguments into `options` hash
59
+
60
+ # Get source and destintation from command line
61
+ case ARGV.size
62
+ when 1
63
+ options[:source] = ARGV[ 0 ]
64
+ when 2
65
+ options[:source] = ARGV[ 0 ]
66
+ options[:destination] = ARGV[ 1 ]
67
+ else
68
+ puts opts
69
+ exit
70
+ end
71
+
72
+ Less::Command.new( options ).run!
data/less.gemspec ADDED
@@ -0,0 +1,67 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{less}
5
+ s.version = "0.8.12"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["cloudhead"]
9
+ s.date = %q{2009-06-21}
10
+ s.default_executable = %q{lessc}
11
+ s.description = %q{LESS is leaner CSS}
12
+ s.email = %q{alexis@cloudhead.net}
13
+ s.executables = ["lessc"]
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "LICENSE",
21
+ "README.md",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "bin/lessc",
25
+ "less.gemspec",
26
+ "lib/less.rb",
27
+ "lib/less/command.rb",
28
+ "lib/less/engine.rb",
29
+ "lib/less/tree.rb",
30
+ "spec/command_spec.rb",
31
+ "spec/css/less-0.8.10.css",
32
+ "spec/css/less-0.8.11.css",
33
+ "spec/css/less-0.8.12.css",
34
+ "spec/css/less-0.8.5.css",
35
+ "spec/css/less-0.8.6.css",
36
+ "spec/css/less-0.8.7.css",
37
+ "spec/css/less-0.8.8.css",
38
+ "spec/engine_spec.rb",
39
+ "spec/spec.css",
40
+ "spec/spec.less",
41
+ "spec/spec_helper.rb",
42
+ "spec/tree_spec.rb"
43
+ ]
44
+ s.has_rdoc = true
45
+ s.homepage = %q{http://www.lesscss.org}
46
+ s.rdoc_options = ["--charset=UTF-8"]
47
+ s.require_paths = ["lib"]
48
+ s.rubyforge_project = %q{less}
49
+ s.rubygems_version = %q{1.3.1}
50
+ s.summary = %q{LESS compiler}
51
+ s.test_files = [
52
+ "spec/command_spec.rb",
53
+ "spec/engine_spec.rb",
54
+ "spec/spec_helper.rb",
55
+ "spec/tree_spec.rb"
56
+ ]
57
+
58
+ if s.respond_to? :specification_version then
59
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
60
+ s.specification_version = 2
61
+
62
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
63
+ else
64
+ end
65
+ else
66
+ end
67
+ end
@@ -0,0 +1,95 @@
1
+ module Less
2
+ class Command
3
+ CSS = '.css'
4
+
5
+ attr_accessor :source, :destination, :options
6
+
7
+ def initialize options
8
+ @source = options[:source]
9
+ @destination = (options[:destination] || options[:source]).gsub /\.(less|lss)/, CSS
10
+ @options = options
11
+ end
12
+
13
+ def watch?() @options[:watch] end
14
+ def compress?() @options[:compress] end
15
+ def debug?() @options[:debug] end
16
+
17
+ # little function which allows us to
18
+ # Ctrl-C exit inside the passed block
19
+ def watch &block
20
+ begin
21
+ block.call
22
+ rescue Interrupt
23
+ puts
24
+ exit 0
25
+ end
26
+ end
27
+
28
+ def run!
29
+ compile(true) unless File.exist? @destination
30
+
31
+ if watch?
32
+ log "Watching for changes in #@source ...Ctrl-C to abort.\n"
33
+
34
+ # Main watch loop
35
+ loop do
36
+ watch { sleep 1 }
37
+
38
+ # File has changed
39
+ if File.stat( @source ).mtime > File.stat( @destination ).mtime
40
+ log "Change detected... "
41
+
42
+ # Loop until error is fixed
43
+ until compile
44
+ log "Press [enter] to continue..."
45
+ watch { $stdin.gets }
46
+ end
47
+ end
48
+ end
49
+ else
50
+ compile
51
+ end
52
+ end
53
+
54
+ def compile new = false
55
+ begin
56
+ # Create a new Less object with the contents of a file
57
+ css = File.read( @source )
58
+ # Process import rules
59
+ imports = css.scan(/(@import\s+(?:url\()?\s*(?:\'|\")?)([a-zA-Z0-9.]*)((?:\'|\")?\)?;)/)
60
+ imports.each { |f| css = css.gsub(f.join, File.read(f[1])) }
61
+ css = Less::Engine.new(css).to_css @options[:inheritance]
62
+ css = css.delete " \n" if compress?
63
+
64
+ File.open( @destination, "w" ) do |file|
65
+ file.write css
66
+ end
67
+ puts "#{new ? '* [Created]' : ' [Updated]'} #{@destination.split('/').last}" if watch?
68
+ rescue Errno::ENOENT => e
69
+ abort "#{e}"
70
+ rescue SyntaxError
71
+ error = debug?? $! : $!.message.split("\n")[1..-1].collect {|e|
72
+ e.gsub(/\(eval\)\:(\d+)\:\s/, 'line \1: ')
73
+ } * "\n"
74
+ err "errors were found in the .less file! \n#{error}\n"
75
+ rescue MixedUnitsError => e
76
+ err "`#{e}` you're mixing units together! What do you expect?\n"
77
+ rescue CompoundOperationError => e
78
+ err "`#{e}` operations in compound declarations aren't allowed, sorry!\n"
79
+ rescue PathError => e
80
+ err "`#{e}` was not found.\n"
81
+ else
82
+ true
83
+ end
84
+ end
85
+
86
+ # Just a logging function to avoid typing '}'
87
+ def log s = ''
88
+ print '* ' + s.to_s
89
+ end
90
+
91
+ def err s = ''
92
+ print "!! #{s}"
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,154 @@
1
+ module Less
2
+ class Engine < String
3
+ # Compound properties, such as `border: 1px solid black`
4
+ COMPOUND = {'font' => true, 'background' => false, 'border' => false }
5
+ REGEX = {
6
+ :path => /([#.][->#.\w ]+)?( ?> ?)?/, # #header > .title
7
+ :selector => /[-\w #.,>*+:\(\)\=\[\]']/, # .cow .milk > a
8
+ :variable => /@([-\w]+)/, # @milk-white
9
+ :property => /@[-\w]+|[-a-z*0-9_]+/, # font-size
10
+ :color => /#([a-zA-Z0-9]{3,6})\b/, # #f0f0f0
11
+ :number => /\d+(?>\.\d+)?/, # 24.8
12
+ :unit => /px|em|pt|cm|mm|%/ # em
13
+ }
14
+ REGEX[:numeric] = /#{REGEX[:number]}(#{REGEX[:unit]})?/
15
+ REGEX[:operand] = /#{REGEX[:color]}|#{REGEX[:numeric]}/
16
+
17
+ def initialize s
18
+ super
19
+ @tree = Tree.new self.hashify
20
+ end
21
+
22
+ def compile
23
+ #
24
+ # Parse the variables and mixins
25
+ #
26
+ # We use symbolic keys, such as :mixins, to store LESS-only data,
27
+ # each branch has its own :mixins => [], and :variables => {}
28
+ # Once a declaration has been recognised as LESS-specific, it is copied
29
+ # in the appropriate data structure in that branch. The declaration itself
30
+ # can then be deleted.
31
+ #
32
+ @tree = @tree.traverse :leaf do |key, value, path, node|
33
+ matched = if match = key.match( REGEX[:variable] )
34
+ node[:variables] ||= Tree.new
35
+ node[:variables][ match.captures.first ] = value
36
+ elsif value == :mixin
37
+ node[:mixins] ||= []
38
+ node[:mixins] << key
39
+ end
40
+ node.delete key if matched # Delete the property if it's LESS-specific
41
+ end
42
+
43
+ #
44
+ # Evaluate mixins
45
+ #
46
+ @tree = @tree.traverse :branch do |path, node|
47
+ if node.include? :mixins
48
+ node[:mixins].each do |m|
49
+ @tree.find( :mixin, m.delete(' ').split('>') ).each {|k, v| node[ k ] = v }
50
+ end
51
+ end
52
+ end
53
+
54
+ # Call `evaluate` on variables, such as '@dark: @light / 2'
55
+ @tree = @tree.traverse :branch do |path, node|
56
+ node.vars.each do |key, value|
57
+ evaluate key, value, path, node.vars
58
+ end if node.vars?
59
+ end
60
+
61
+ # Call `evaluate` on css properties, such as 'font-size: @big'
62
+ @tree = @tree.traverse :leaf do |key, value, path, node|
63
+ evaluate key, value, path, node
64
+ end
65
+
66
+ #
67
+ # Evaluate operations (2+2)
68
+ #
69
+ # Units are: 1px, 1em, 1%, #111
70
+ @tree = @tree.traverse :leaf do |key, value, path, node|
71
+ node[ key ] = value.gsub /(#{REGEX[:operand]}(\s?)[-+\/*](\4))+(#{REGEX[:operand]})/ do |operation|
72
+ # Disallow operations on certain compound declarations
73
+ if COMPOUND[ key ]
74
+ next value
75
+ else
76
+ raise CompoundOperationError, "#{key}: #{value}"
77
+ end if COMPOUND.include? key
78
+
79
+ if (unit = operation.scan(/#{REGEX[:numeric]}|(#)/i).flatten.compact.uniq).size <= 1
80
+ unit = unit.join
81
+ operation = if unit == '#'
82
+ evaluate = lambda do |v|
83
+ result = eval v
84
+ unit + ( result.zero?? '000' : result.to_s(16) )
85
+ end
86
+ operation.gsub REGEX[:color] do
87
+ hex = $1 * ( $1.size < 6 ? 6 / $1.size : 1 )
88
+ hex.to_i(16)
89
+ end.delete unit
90
+ else
91
+ evaluate = lambda {|v| eval( v ).to_s + unit }
92
+ operation.gsub REGEX[:unit], ''
93
+ end.to_s
94
+ next if operation.match /[a-z]/i
95
+ evaluate.call operation
96
+ else
97
+ raise MixedUnitsError, value
98
+ end
99
+ end
100
+ end
101
+ end
102
+ alias render compile
103
+
104
+ #
105
+ # Evaluate variables
106
+ #
107
+ def evaluate key, expression, path, node
108
+ if expression.is_a? String and expression.include? '@' # There's a var to evaluate
109
+ expression.scan /#{REGEX[:path]}#{REGEX[:variable]}/ do |var|
110
+ name = var.last
111
+ var = var.join.delete ' '
112
+
113
+ value = if var.include? '>'
114
+ @tree.find :var, var.split('>') # Try finding it in a specific namespace
115
+ else
116
+ node.var(var) or @tree.nearest var, path # Try local first, then nearest scope
117
+ end
118
+
119
+ if value
120
+ # Substitute variable with value
121
+ node[ key ] = node[ key ].gsub /#{REGEX[:path]}@#{name}/, value
122
+ else
123
+ node.delete key # Discard the declaration if the variable wasn't found
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ def to_css chain = :desc
130
+ self.compile.to_css chain
131
+ end
132
+
133
+ def hashify
134
+ #
135
+ # Parse the LESS structure into a hash
136
+ #
137
+ ###
138
+ # less: color: black;
139
+ # hashify: "color" => "black"
140
+ #
141
+ hash = self.gsub(/\r\n/, "\n"). # m$
142
+ gsub(/"/, "'"). # " to '
143
+ gsub(/'(.*?)'/) { "'" + CGI.escape( $1 ) + "'" }. # Escape string values
144
+ gsub(/\/\/.*\n/, ''). # Comments //
145
+ gsub(/\/\*.*?\*\//m, ''). # Comments /*
146
+ gsub(/\s+/, ' '). # Replace \t\n
147
+ gsub(/(#{REGEX[:property]}): *([^\{\}]+?) *(;|(?=\}))/,'"\1"=>"\2",'). # Rules
148
+ gsub(/\}/, "},"). # Closing }
149
+ gsub(/([, ]*)(#{REGEX[:selector]}+?)[ \n]*(?=\{)/, '\1"\2"=>'). # Selectors
150
+ gsub(/([.#][->\w .#]+);/, '"\\1" => :mixin,') # Mixins
151
+ eval "{#{ hash }}" # Return {hash}
152
+ end
153
+ end
154
+ end